updated for android 16 fixing some detection issues
This commit is contained in:
parent
f3e01a116c
commit
e7c34d0de5
98
.gitignore
vendored
98
.gitignore
vendored
@ -1,49 +1,49 @@
|
||||
*.iml
|
||||
.gradle
|
||||
/local.properties
|
||||
/.idea/caches
|
||||
/.idea/libraries
|
||||
/.idea/modules.xml
|
||||
/.idea/workspace.xml
|
||||
/.idea/navEditor.xml
|
||||
/.idea/assetWizardSettings.xml
|
||||
.DS_Store
|
||||
/build
|
||||
/captures
|
||||
.externalNativeBuild
|
||||
.cxx
|
||||
local.properties
|
||||
|
||||
# Gradle files
|
||||
.gradle/
|
||||
build/
|
||||
|
||||
# Local configuration file (sdk path, etc)
|
||||
local.properties
|
||||
|
||||
# Log/OS Files
|
||||
*.log
|
||||
|
||||
# Android Studio generated files and folders
|
||||
captures/
|
||||
.externalNativeBuild/
|
||||
.cxx/
|
||||
*.apk
|
||||
output.json
|
||||
|
||||
# IntelliJ
|
||||
*.iml
|
||||
.idea/
|
||||
misc.xml
|
||||
deploymentTargetDropDown.xml
|
||||
render.experimental.xml
|
||||
|
||||
# Keystore files
|
||||
*.jks
|
||||
*.keystore
|
||||
|
||||
# Google Services (e.g. APIs or Firebase)
|
||||
google-services.json
|
||||
|
||||
# Android Profiling
|
||||
*.hprof
|
||||
*.iml
|
||||
.gradle
|
||||
/local.properties
|
||||
/.idea/caches
|
||||
/.idea/libraries
|
||||
/.idea/modules.xml
|
||||
/.idea/workspace.xml
|
||||
/.idea/navEditor.xml
|
||||
/.idea/assetWizardSettings.xml
|
||||
.DS_Store
|
||||
/build
|
||||
/captures
|
||||
.externalNativeBuild
|
||||
.cxx
|
||||
local.properties
|
||||
|
||||
# Gradle files
|
||||
.gradle/
|
||||
build/
|
||||
|
||||
# Local configuration file (sdk path, etc)
|
||||
local.properties
|
||||
|
||||
# Log/OS Files
|
||||
*.log
|
||||
|
||||
# Android Studio generated files and folders
|
||||
captures/
|
||||
.externalNativeBuild/
|
||||
.cxx/
|
||||
*.apk
|
||||
output.json
|
||||
|
||||
# IntelliJ
|
||||
*.iml
|
||||
.idea/
|
||||
misc.xml
|
||||
deploymentTargetDropDown.xml
|
||||
render.experimental.xml
|
||||
|
||||
# Keystore files
|
||||
*.jks
|
||||
*.keystore
|
||||
|
||||
# Google Services (e.g. APIs or Firebase)
|
||||
google-services.json
|
||||
|
||||
# Android Profiling
|
||||
*.hprof
|
||||
|
||||
252
README.md
252
README.md
@ -1,126 +1,126 @@
|
||||
# DJI-FCC-HACK
|
||||
|
||||
A simple Android app that forces DJI N1 remotes to FCC
|
||||
|
||||
<img src=".github/light.webp" alt="app" width="200"/><img src=".github/dark.webp" alt="app" width="200"/>
|
||||
|
||||
> [!WARNING]
|
||||
> This only works for drones with DJI N1 remotes. If you have a different remote, this app will not work for you.
|
||||
|
||||
## How to use
|
||||
|
||||
Download the latest release from the [releases page](https://github.com/M4TH1EU/DJI-FCC-HACK/releases) and install it on your Android device.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> You need to repeat the following steps every time you turn on the drone and/or remote.
|
||||
|
||||
Then follow these steps:
|
||||
|
||||
1. Turn on the drone and remote and wait a few seconds for them to connect.
|
||||
2. Connect your phone to the **bottom** USB port of the remote.
|
||||
3. Click on 'Send FCC Patch'.
|
||||
4. Disconnect your phone from the bottom USB port of the remote and connect it to the **top** USB port.
|
||||
5. Enjoy your drone with FCC mode.
|
||||
|
||||
## Compatibility
|
||||
|
||||
This app should work on any Android device running Android 8 and above.
|
||||
|
||||
**Tested on the following drones:**
|
||||
|
||||
* DJI Mavic Air 2
|
||||
* DJI Mini 4K
|
||||
* DJI Mini 2
|
||||
* DJI Air 2S
|
||||
* DJI Neo 2
|
||||
|
||||
> [!WARNING]
|
||||
> Many people reported that this hack doesn't work with the Mini 3, if anyone finds a working hack I could take a look at reverse-engineering it.
|
||||
|
||||
> [!NOTE]
|
||||
> Please let me know if you have tested this app on another drone so I can update this README.
|
||||
|
||||
## How do I know if it worked?
|
||||
|
||||
Open the DJI Fly app and go to the Transmission tab. Look at the horizontal bar around -90 dBm:
|
||||
|
||||
* If it lines up with the 1km mark, your drone is in CE mode.
|
||||
* If it falls below the 1km mark, your drone is in FCC mode.
|
||||
|
||||
*Check the images below for reference.*
|
||||
|
||||
| FCC | CE |
|
||||
| ----------------------------- | --------------------------- |
|
||||
|  |  |
|
||||
|
||||
## FAQ
|
||||
|
||||
### Does this work on iOS?
|
||||
|
||||
No, this app is only available for Android.
|
||||
|
||||
### Does this work on DJI Smart Controller?
|
||||
|
||||
No, this app only works with N1 remotes (the ones without a screen).
|
||||
|
||||
### Does this work on DJI XYZ drone?
|
||||
|
||||
Maybe? Give it a try and let me know so I can update this README.
|
||||
|
||||
### How does this work?
|
||||
|
||||
This app simply sends a command to the remote to switch to FCC mode over the USB port.
|
||||
|
||||
### Can I use this app to switch back to CE mode?
|
||||
|
||||
No, this app only switches to FCC mode. To switch back to CE, turn off the drone and remote, then power them back on.
|
||||
|
||||
## Goggles Support
|
||||
>[!WARNING]
|
||||
> This app is not related to the following FCC file-based hacks for goggles; they are included here for reference only.
|
||||
|
||||
Steps to enable higher power output for DJI Goggles:
|
||||
|
||||
**DJI Goggles V1/V2**
|
||||
|
||||
* Create a text file named `naco_pwr.txt` with content: `pwr_2`
|
||||
* Copy it to a microSD card
|
||||
* Power on Goggles and Air Unit, wait for camera image
|
||||
* Insert SD card into Goggles and restart
|
||||
|
||||
**DJI Goggles 2 / Goggles 3**
|
||||
|
||||
* Create an empty file named `ham_cfg_support` (no extension)
|
||||
* Copy it to a microSD card
|
||||
* Insert SD card into Goggles
|
||||
* Power on Goggles
|
||||
|
||||
## Air Units
|
||||
>[!WARNING]
|
||||
> This app is not related to the following FCC file-based hacks for air units; they are included here for reference only.
|
||||
|
||||
Steps to enable FCC mode on DJI video transmitters:
|
||||
|
||||
**Air Unit V1**
|
||||
|
||||
* Create `naco_pwr.txt` with `pwr_2` inside
|
||||
* Copy to microSD card, insert into Air Unit
|
||||
* Power on
|
||||
|
||||
**Vista**
|
||||
|
||||
* Create `naco_pwr.txt` with `pwr_2` inside
|
||||
* Power on Vista and connect via USB
|
||||
* Copy file to Vista storage when it appears
|
||||
* Power cycle the unit
|
||||
|
||||
**Air Unit O3**
|
||||
|
||||
* Create empty file `ham_cfg_support`
|
||||
* Connect O3 via USB
|
||||
* Copy to O3 storage
|
||||
* Power cycle
|
||||
|
||||
## Credits
|
||||
|
||||
This app is based on the work of [galbb](https://mavicpilots.com/members/galbb.148459/) on the [MavicPilots forum](https://mavicpilots.com/threads/mavic-air-2-switch-to-fcc-mode-using-an-android-app.115027/).
|
||||
# DJI-FCC-HACK
|
||||
|
||||
A simple Android app that forces DJI N1 remotes to FCC
|
||||
|
||||
<img src=".github/light.webp" alt="app" width="200"/><img src=".github/dark.webp" alt="app" width="200"/>
|
||||
|
||||
> [!WARNING]
|
||||
> This only works for drones with DJI N1 remotes. If you have a different remote, this app will not work for you.
|
||||
|
||||
## How to use
|
||||
|
||||
Download the latest release from the [releases page](https://github.com/M4TH1EU/DJI-FCC-HACK/releases) and install it on your Android device.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> You need to repeat the following steps every time you turn on the drone and/or remote.
|
||||
|
||||
Then follow these steps:
|
||||
|
||||
1. Turn on the drone and remote and wait a few seconds for them to connect.
|
||||
2. Connect your phone to the **bottom** USB port of the remote.
|
||||
3. Click on 'Send FCC Patch'.
|
||||
4. Disconnect your phone from the bottom USB port of the remote and connect it to the **top** USB port.
|
||||
5. Enjoy your drone with FCC mode.
|
||||
|
||||
## Compatibility
|
||||
|
||||
This app should work on any Android device running Android 8 and above.
|
||||
|
||||
**Tested on the following drones:**
|
||||
|
||||
* DJI Mavic Air 2
|
||||
* DJI Mini 4K
|
||||
* DJI Mini 2
|
||||
* DJI Air 2S
|
||||
* DJI Neo 2
|
||||
|
||||
> [!WARNING]
|
||||
> Many people reported that this hack doesn't work with the Mini 3, if anyone finds a working hack I could take a look at reverse-engineering it.
|
||||
|
||||
> [!NOTE]
|
||||
> Please let me know if you have tested this app on another drone so I can update this README.
|
||||
|
||||
## How do I know if it worked?
|
||||
|
||||
Open the DJI Fly app and go to the Transmission tab. Look at the horizontal bar around -90 dBm:
|
||||
|
||||
* If it lines up with the 1km mark, your drone is in CE mode.
|
||||
* If it falls below the 1km mark, your drone is in FCC mode.
|
||||
|
||||
*Check the images below for reference.*
|
||||
|
||||
| FCC | CE |
|
||||
| ----------------------------- | --------------------------- |
|
||||
|  |  |
|
||||
|
||||
## FAQ
|
||||
|
||||
### Does this work on iOS?
|
||||
|
||||
No, this app is only available for Android.
|
||||
|
||||
### Does this work on DJI Smart Controller?
|
||||
|
||||
No, this app only works with N1 remotes (the ones without a screen).
|
||||
|
||||
### Does this work on DJI XYZ drone?
|
||||
|
||||
Maybe? Give it a try and let me know so I can update this README.
|
||||
|
||||
### How does this work?
|
||||
|
||||
This app simply sends a command to the remote to switch to FCC mode over the USB port.
|
||||
|
||||
### Can I use this app to switch back to CE mode?
|
||||
|
||||
No, this app only switches to FCC mode. To switch back to CE, turn off the drone and remote, then power them back on.
|
||||
|
||||
## Goggles Support
|
||||
>[!WARNING]
|
||||
> This app is not related to the following FCC file-based hacks for goggles; they are included here for reference only.
|
||||
|
||||
Steps to enable higher power output for DJI Goggles:
|
||||
|
||||
**DJI Goggles V1/V2**
|
||||
|
||||
* Create a text file named `naco_pwr.txt` with content: `pwr_2`
|
||||
* Copy it to a microSD card
|
||||
* Power on Goggles and Air Unit, wait for camera image
|
||||
* Insert SD card into Goggles and restart
|
||||
|
||||
**DJI Goggles 2 / Goggles 3**
|
||||
|
||||
* Create an empty file named `ham_cfg_support` (no extension)
|
||||
* Copy it to a microSD card
|
||||
* Insert SD card into Goggles
|
||||
* Power on Goggles
|
||||
|
||||
## Air Units
|
||||
>[!WARNING]
|
||||
> This app is not related to the following FCC file-based hacks for air units; they are included here for reference only.
|
||||
|
||||
Steps to enable FCC mode on DJI video transmitters:
|
||||
|
||||
**Air Unit V1**
|
||||
|
||||
* Create `naco_pwr.txt` with `pwr_2` inside
|
||||
* Copy to microSD card, insert into Air Unit
|
||||
* Power on
|
||||
|
||||
**Vista**
|
||||
|
||||
* Create `naco_pwr.txt` with `pwr_2` inside
|
||||
* Power on Vista and connect via USB
|
||||
* Copy file to Vista storage when it appears
|
||||
* Power cycle the unit
|
||||
|
||||
**Air Unit O3**
|
||||
|
||||
* Create empty file `ham_cfg_support`
|
||||
* Connect O3 via USB
|
||||
* Copy to O3 storage
|
||||
* Power cycle
|
||||
|
||||
## Credits
|
||||
|
||||
This app is based on the work of [galbb](https://mavicpilots.com/members/galbb.148459/) on the [MavicPilots forum](https://mavicpilots.com/threads/mavic-air-2-switch-to-fcc-mode-using-an-android-app.115027/).
|
||||
|
||||
@ -1,60 +1,60 @@
|
||||
plugins {
|
||||
alias(libs.plugins.android.application)
|
||||
alias(libs.plugins.kotlin.android)
|
||||
alias(libs.plugins.kotlin.compose)
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "ch.mathieubroillet.djiffchack"
|
||||
compileSdk = 35
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "ch.mathieubroillet.djiffchack"
|
||||
minSdk = 26
|
||||
targetSdk = 35
|
||||
versionCode = 1
|
||||
versionName = "1.1"
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
isMinifyEnabled = false
|
||||
proguardFiles(
|
||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||
"proguard-rules.pro"
|
||||
)
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = "11"
|
||||
}
|
||||
buildFeatures {
|
||||
compose = true
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("com.github.mik3y:usb-serial-for-android:3.8.1")
|
||||
|
||||
implementation(libs.androidx.core.ktx)
|
||||
implementation(libs.androidx.lifecycle.runtime.ktx)
|
||||
implementation(libs.androidx.activity.compose)
|
||||
implementation(platform(libs.androidx.compose.bom))
|
||||
implementation(libs.androidx.ui)
|
||||
implementation(libs.androidx.ui.graphics)
|
||||
implementation(libs.androidx.ui.tooling.preview)
|
||||
implementation(libs.androidx.material3)
|
||||
testImplementation(libs.junit)
|
||||
androidTestImplementation(libs.androidx.junit)
|
||||
androidTestImplementation(libs.androidx.espresso.core)
|
||||
androidTestImplementation(platform(libs.androidx.compose.bom))
|
||||
androidTestImplementation(libs.androidx.ui.test.junit4)
|
||||
debugImplementation(libs.androidx.ui.tooling)
|
||||
debugImplementation(libs.androidx.ui.test.manifest)
|
||||
plugins {
|
||||
alias(libs.plugins.android.application)
|
||||
alias(libs.plugins.kotlin.android)
|
||||
alias(libs.plugins.kotlin.compose)
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "ch.mathieubroillet.djiffchack"
|
||||
compileSdk = 35
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "ch.mathieubroillet.djiffchack"
|
||||
minSdk = 26
|
||||
targetSdk = 35
|
||||
versionCode = 1
|
||||
versionName = "1.1"
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
isMinifyEnabled = false
|
||||
proguardFiles(
|
||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||
"proguard-rules.pro"
|
||||
)
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = "11"
|
||||
}
|
||||
buildFeatures {
|
||||
compose = true
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("com.github.mik3y:usb-serial-for-android:3.8.1")
|
||||
|
||||
implementation(libs.androidx.core.ktx)
|
||||
implementation(libs.androidx.lifecycle.runtime.ktx)
|
||||
implementation(libs.androidx.activity.compose)
|
||||
implementation(platform(libs.androidx.compose.bom))
|
||||
implementation(libs.androidx.ui)
|
||||
implementation(libs.androidx.ui.graphics)
|
||||
implementation(libs.androidx.ui.tooling.preview)
|
||||
implementation(libs.androidx.material3)
|
||||
testImplementation(libs.junit)
|
||||
androidTestImplementation(libs.androidx.junit)
|
||||
androidTestImplementation(libs.androidx.espresso.core)
|
||||
androidTestImplementation(platform(libs.androidx.compose.bom))
|
||||
androidTestImplementation(libs.androidx.ui.test.junit4)
|
||||
debugImplementation(libs.androidx.ui.tooling)
|
||||
debugImplementation(libs.androidx.ui.test.manifest)
|
||||
}
|
||||
40
app/proguard-rules.pro
vendored
40
app/proguard-rules.pro
vendored
@ -1,21 +1,21 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
@ -1,24 +1,24 @@
|
||||
package ch.mathieubroillet.djiffchack
|
||||
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
import org.junit.Assert.*
|
||||
|
||||
/**
|
||||
* Instrumented test, which will execute on an Android device.
|
||||
*
|
||||
* See [testing documentation](http://d.android.com/tools/testing).
|
||||
*/
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class ExampleInstrumentedTest {
|
||||
@Test
|
||||
fun useAppContext() {
|
||||
// Context of the app under test.
|
||||
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
|
||||
assertEquals("ch.mathieubroillet.djiffchack", appContext.packageName)
|
||||
}
|
||||
package ch.mathieubroillet.djiffchack
|
||||
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
import org.junit.Assert.*
|
||||
|
||||
/**
|
||||
* Instrumented test, which will execute on an Android device.
|
||||
*
|
||||
* See [testing documentation](http://d.android.com/tools/testing).
|
||||
*/
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class ExampleInstrumentedTest {
|
||||
@Test
|
||||
fun useAppContext() {
|
||||
// Context of the app under test.
|
||||
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
|
||||
assertEquals("ch.mathieubroillet.djiffchack", appContext.packageName)
|
||||
}
|
||||
}
|
||||
@ -1,38 +1,34 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-feature android:name="android.hardware.usb.host" android:required="false"/>
|
||||
<uses-feature android:name="android.hardware.usb.accessory" android:required="true"/>
|
||||
|
||||
<uses-permission android:name="android.permission.USB_PERMISSION" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||
android:fullBackupContent="@xml/backup_rules"
|
||||
android:icon="@mipmap/icon"
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/icon_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.Djiffchack"
|
||||
tools:targetApi="31">
|
||||
|
||||
<uses-library android:name="com.android.future.usb.accessory"/>
|
||||
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true"
|
||||
android:theme="@style/Theme.Djiffchack">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"/>
|
||||
</intent-filter>
|
||||
<meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" android:resource="@xml/device_filter"/>
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-feature android:name="android.hardware.usb.host" android:required="false"/>
|
||||
<uses-feature android:name="android.hardware.usb.accessory" android:required="true"/>
|
||||
|
||||
<uses-permission android:name="android.permission.USB_PERMISSION" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||
android:fullBackupContent="@xml/backup_rules"
|
||||
android:icon="@mipmap/icon"
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/icon_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.Djiffchack"
|
||||
tools:targetApi="31">
|
||||
|
||||
<uses-library android:name="com.android.future.usb.accessory"/>
|
||||
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true"
|
||||
android:theme="@style/Theme.Djiffchack">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
@ -1,14 +1,14 @@
|
||||
package ch.mathieubroillet.djiffchack
|
||||
|
||||
|
||||
object Constants {
|
||||
private const val PACKAGE = "ch.mathieubroillet.djiffchack"
|
||||
const val INTENT_ACTION_GRANT_USB_PERMISSION = "$PACKAGE.USB_PERMISSION"
|
||||
|
||||
// The "magic bytes" that enable FCC mode
|
||||
// The credits goes to @galbb from https://mavicpilots.com/threads/mavic-air-2-switch-to-fcc-mode-using-an-android-app.115027/
|
||||
val BYTES_1 = byteArrayOf(85, 13, 4, 33, 42, 31, 0, 0, 0, 0, 1, -122, 32)
|
||||
val BYTES_2 = byteArrayOf(85, 24, 4, 32, 2, 9, 0, 0, 64, 9, 39, 0, 2, 72, 0, -1, -1, 2, 0, 0, 0, 0, -127, 31)
|
||||
|
||||
const val GITHUB_URL = "https://github.com/M4TH1EU/DJI-FCC-HACK"
|
||||
package ch.mathieubroillet.djiffchack
|
||||
|
||||
|
||||
object Constants {
|
||||
private const val PACKAGE = "ch.mathieubroillet.djiffchack"
|
||||
const val INTENT_ACTION_GRANT_USB_PERMISSION = "$PACKAGE.USB_PERMISSION"
|
||||
|
||||
// The "magic bytes" that enable FCC mode
|
||||
// The credits goes to @galbb from https://mavicpilots.com/threads/mavic-air-2-switch-to-fcc-mode-using-an-android-app.115027/
|
||||
val BYTES_1 = byteArrayOf(85, 13, 4, 33, 42, 31, 0, 0, 0, 0, 1, -122, 32)
|
||||
val BYTES_2 = byteArrayOf(85, 24, 4, 32, 2, 9, 0, 0, 64, 9, 39, 0, 2, 72, 0, -1, -1, 2, 0, 0, 0, 0, -127, 31)
|
||||
|
||||
const val GITHUB_URL = "https://github.com/M4TH1EU/DJI-FCC-HACK"
|
||||
}
|
||||
@ -19,6 +19,8 @@ import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
@ -32,7 +34,9 @@ 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.Info
|
||||
import androidx.compose.material.icons.filled.Refresh
|
||||
import androidx.compose.material.icons.filled.Star
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.CardDefaults
|
||||
@ -45,9 +49,11 @@ import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateListOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.runtime.snapshots.SnapshotStateList
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalUriHandler
|
||||
@ -66,17 +72,44 @@ import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
|
||||
|
||||
data class UsbDeviceInfo(
|
||||
val device: UsbDevice,
|
||||
val isDjiDevice: Boolean,
|
||||
val hasPermission: Boolean
|
||||
)
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
private lateinit var usbManager: UsbManager
|
||||
private var usbConnected by mutableStateOf(false)
|
||||
private var isPatching by mutableStateOf(false)
|
||||
private val debugLogs = mutableStateListOf<String>()
|
||||
private val maxLogs = 50
|
||||
private val usbDevices = mutableStateListOf<UsbDeviceInfo>()
|
||||
private var selectedDevice by mutableStateOf<UsbDevice?>(null)
|
||||
|
||||
private fun addDebugLog(message: String) {
|
||||
val timestamp = SimpleDateFormat("HH:mm:ss.SSS", Locale.getDefault()).format(Date())
|
||||
val logEntry = "[$timestamp] $message"
|
||||
debugLogs.add(0, logEntry) // Add to the beginning
|
||||
Log.d("DEBUG_LOG", message)
|
||||
|
||||
// Keep only the most recent logs
|
||||
if (debugLogs.size > maxLogs) {
|
||||
debugLogs.removeAt(debugLogs.lastIndex)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
usbManager = getSystemService(Context.USB_SERVICE) as UsbManager
|
||||
addDebugLog("App started")
|
||||
addDebugLog("Android ${android.os.Build.VERSION.RELEASE} (API ${android.os.Build.VERSION.SDK_INT})")
|
||||
|
||||
// Register receiver to detect USB plug/unplug events
|
||||
val filter = IntentFilter().apply {
|
||||
@ -85,15 +118,24 @@ class MainActivity : ComponentActivity() {
|
||||
addAction(Constants.INTENT_ACTION_GRANT_USB_PERMISSION)
|
||||
}
|
||||
ContextCompat.registerReceiver(
|
||||
this, usbReceiver, filter, ContextCompat.RECEIVER_NOT_EXPORTED
|
||||
this, usbReceiver, filter, ContextCompat.RECEIVER_EXPORTED
|
||||
)
|
||||
|
||||
// Initial check for USB connection
|
||||
refreshUsbConnection()
|
||||
// Initial USB scan
|
||||
refreshUsbDeviceList()
|
||||
|
||||
setContent {
|
||||
DJI_FCC_HACK_Theme {
|
||||
MainScreen(usbConnected, ::refreshUsbConnection, ::sendPatch, isPatching)
|
||||
MainScreen(
|
||||
usbConnected = usbConnected,
|
||||
onRefresh = ::refreshUsbDeviceList,
|
||||
onSendPatch = ::sendPatch,
|
||||
isPatching = isPatching,
|
||||
debugLogs = debugLogs,
|
||||
usbDevices = usbDevices,
|
||||
selectedDevice = selectedDevice,
|
||||
onDeviceSelected = ::selectDevice
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -103,29 +145,90 @@ class MainActivity : ComponentActivity() {
|
||||
unregisterReceiver(usbReceiver)
|
||||
}
|
||||
|
||||
private fun selectDevice(device: UsbDevice) {
|
||||
addDebugLog("User selected device: VID=${device.vendorId}, PID=${device.productId}")
|
||||
selectedDevice = device
|
||||
|
||||
// Check permission status
|
||||
val hasPermission = usbManager.hasPermission(device)
|
||||
addDebugLog("Permission status: $hasPermission")
|
||||
|
||||
if (!hasPermission) {
|
||||
addDebugLog("Requesting permission for selected device...")
|
||||
usbConnected = false
|
||||
requestUsbPermission(device)
|
||||
} else {
|
||||
addDebugLog("✓ Permission already granted for selected device")
|
||||
usbConnected = true
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the USB connection status
|
||||
* Refreshes the USB device list
|
||||
*/
|
||||
private fun refreshUsbConnection() {
|
||||
if (usbManager.deviceList.isNotEmpty()) {
|
||||
val device: UsbDevice = usbManager.deviceList.values.first()
|
||||
Log.d("USB_CONNECTION", device.vendorId.toString() + ":" + device.productId.toString())
|
||||
private fun refreshUsbDeviceList() {
|
||||
addDebugLog("=== Scanning for USB devices ===")
|
||||
addDebugLog("Android ${android.os.Build.VERSION.RELEASE} (API ${android.os.Build.VERSION.SDK_INT})")
|
||||
|
||||
// Check to be sure the device is the initialized DJI Remote (and not another USB device)
|
||||
if (device.productId != 4128) {
|
||||
Log.d("USB_CONNECTION", "Device not supported ${device.productId}")
|
||||
usbConnected = false
|
||||
return
|
||||
}
|
||||
// Check if USB host is supported
|
||||
val hasUsbHostFeature = packageManager.hasSystemFeature("android.hardware.usb.host")
|
||||
addDebugLog("USB Host feature available: $hasUsbHostFeature")
|
||||
|
||||
if (usbManager.openDevice(device) == null) {
|
||||
Log.d("USB_CONNECTION", "Requesting USB Permission")
|
||||
requestUsbPermission(device)
|
||||
} else {
|
||||
usbConnected = true
|
||||
// Get device list
|
||||
val deviceList = usbManager.deviceList
|
||||
addDebugLog("UsbManager.deviceList size: ${deviceList.size}")
|
||||
|
||||
if (deviceList.isEmpty()) {
|
||||
addDebugLog("WARNING: No USB devices found")
|
||||
addDebugLog("Possible reasons:")
|
||||
addDebugLog(" - No USB device connected")
|
||||
addDebugLog(" - Android 16 USB enumeration bug")
|
||||
addDebugLog(" - USB Protection enabled (unlock phone)")
|
||||
addDebugLog(" - Try unplugging and re-plugging USB")
|
||||
// Don't clear usbDevices here - keep showing last known devices
|
||||
// Only clear connection status
|
||||
if (selectedDevice != null) {
|
||||
val stillExists = deviceList.values.any { it == selectedDevice }
|
||||
if (!stillExists) {
|
||||
addDebugLog("Selected device no longer in list")
|
||||
usbConnected = false
|
||||
selectedDevice = null
|
||||
}
|
||||
}
|
||||
} else {
|
||||
usbConnected = false
|
||||
addDebugLog("Found ${deviceList.size} USB device(s):")
|
||||
|
||||
// Build new list without clearing immediately
|
||||
val newDevices = mutableListOf<UsbDeviceInfo>()
|
||||
|
||||
deviceList.values.forEachIndexed { index, device ->
|
||||
val isDji = device.vendorId == 11427 && device.productId == 4128
|
||||
val hasPerm = usbManager.hasPermission(device)
|
||||
|
||||
addDebugLog(" [$index] VID=0x${device.vendorId.toString(16)} PID=0x${device.productId.toString(16)}")
|
||||
addDebugLog(" Name: ${device.deviceName}")
|
||||
addDebugLog(" DJI Device: $isDji")
|
||||
addDebugLog(" Has Permission: $hasPerm")
|
||||
|
||||
newDevices.add(UsbDeviceInfo(device, isDji, hasPerm))
|
||||
|
||||
// Update selected device permission status if it's in the new list
|
||||
if (device == selectedDevice) {
|
||||
usbConnected = hasPerm
|
||||
addDebugLog("Selected device found, permission: $hasPerm")
|
||||
}
|
||||
|
||||
// Auto-select DJI device if found and has permission and nothing selected
|
||||
if (isDji && hasPerm && selectedDevice == null) {
|
||||
selectedDevice = device
|
||||
usbConnected = true
|
||||
addDebugLog("✓ Auto-selected DJI device with permission")
|
||||
}
|
||||
}
|
||||
|
||||
// Update the list only after building complete list
|
||||
usbDevices.clear()
|
||||
usbDevices.addAll(newDevices)
|
||||
}
|
||||
}
|
||||
|
||||
@ -133,12 +236,23 @@ class MainActivity : ComponentActivity() {
|
||||
* Sends the FCC patch to the DJI remote via USB
|
||||
*/
|
||||
private fun sendPatch(): Boolean {
|
||||
// At this point, we assume the USB device is connected and we have permission to access it
|
||||
if (!usbConnected) {
|
||||
Toast.makeText(this, "No USB device connected!", Toast.LENGTH_SHORT).show()
|
||||
addDebugLog("=== Starting FCC Patch ===")
|
||||
|
||||
if (selectedDevice == null) {
|
||||
addDebugLog("ERROR: No device selected")
|
||||
Toast.makeText(this, "Please select a USB device first!", Toast.LENGTH_SHORT).show()
|
||||
return false
|
||||
}
|
||||
|
||||
if (!usbManager.hasPermission(selectedDevice!!)) {
|
||||
addDebugLog("ERROR: No permission for selected device")
|
||||
Toast.makeText(this, "No USB permission!", Toast.LENGTH_SHORT).show()
|
||||
return false
|
||||
}
|
||||
|
||||
val device = selectedDevice!!
|
||||
addDebugLog("Using device: VID=${device.vendorId}, PID=${device.productId}")
|
||||
|
||||
val probeTable = ProbeTable().apply {
|
||||
addProduct(11427, 4128, CdcAcmSerialDriver::class.java)
|
||||
|
||||
@ -148,30 +262,41 @@ class MainActivity : ComponentActivity() {
|
||||
}
|
||||
|
||||
// Retrieve the custom device (DJI remote) with the correct driver from the probe table above
|
||||
val driver = UsbSerialProber(probeTable).probeDevice(usbManager.deviceList.values.first())
|
||||
addDebugLog("Probing USB device...")
|
||||
val driver = UsbSerialProber(probeTable).probeDevice(device)
|
||||
addDebugLog("Opening USB device...")
|
||||
val deviceConnection = usbManager.openDevice(driver.device)
|
||||
if (deviceConnection == null) {
|
||||
Log.e("USB_PATCH", "Error opening USB device")
|
||||
addDebugLog("ERROR: Failed to open USB device")
|
||||
Toast.makeText(this, "Error opening USB device", Toast.LENGTH_SHORT).show()
|
||||
return false
|
||||
}
|
||||
|
||||
try {
|
||||
addDebugLog("Getting serial port...")
|
||||
val deviceSerialPort = driver.ports.firstOrNull()
|
||||
if (deviceSerialPort == null) {
|
||||
Log.e("USB_PATCH", "Error opening USB port")
|
||||
addDebugLog("ERROR: No serial port found")
|
||||
Toast.makeText(this, "Error opening USB port", Toast.LENGTH_SHORT).show()
|
||||
return false
|
||||
}
|
||||
|
||||
addDebugLog("Opening serial port...")
|
||||
deviceSerialPort.open(deviceConnection)
|
||||
addDebugLog("Setting parameters (19200 baud, 8N1)...")
|
||||
deviceSerialPort.setParameters(19200, 8, 1, UsbSerialPort.PARITY_NONE)
|
||||
addDebugLog("Writing patch bytes (part 1)...")
|
||||
deviceSerialPort.write(Constants.BYTES_1, 1000)
|
||||
addDebugLog("Writing patch bytes (part 2)...")
|
||||
deviceSerialPort.write(Constants.BYTES_2, 1000)
|
||||
addDebugLog("SUCCESS: Patch sent successfully!")
|
||||
|
||||
Toast.makeText(this, "Patched successfully", Toast.LENGTH_LONG).show()
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
addDebugLog("ERROR: ${e.javaClass.simpleName}: ${e.message}")
|
||||
Toast.makeText(this, "Error: ${e.message}", Toast.LENGTH_LONG).show()
|
||||
}
|
||||
|
||||
@ -183,14 +308,27 @@ class MainActivity : ComponentActivity() {
|
||||
* Requests USB permission for the device
|
||||
*/
|
||||
private fun requestUsbPermission(device: UsbDevice) {
|
||||
addDebugLog("Creating permission request intent...")
|
||||
addDebugLog("Package name: $packageName")
|
||||
|
||||
val permissionIntent = PendingIntent.getBroadcast(
|
||||
this,
|
||||
0,
|
||||
Intent(Constants.INTENT_ACTION_GRANT_USB_PERMISSION).apply { setPackage(packageName) },
|
||||
PendingIntent.FLAG_MUTABLE
|
||||
Intent(Constants.INTENT_ACTION_GRANT_USB_PERMISSION).apply {
|
||||
setPackage(packageName)
|
||||
putExtra(UsbManager.EXTRA_DEVICE, device)
|
||||
},
|
||||
PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
|
||||
)
|
||||
|
||||
usbManager.requestPermission(device, permissionIntent)
|
||||
addDebugLog("Calling usbManager.requestPermission()...")
|
||||
try {
|
||||
usbManager.requestPermission(device, permissionIntent)
|
||||
addDebugLog("Permission request sent successfully")
|
||||
} catch (e: Exception) {
|
||||
addDebugLog("ERROR requesting permission: ${e.message}")
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -202,18 +340,51 @@ class MainActivity : ComponentActivity() {
|
||||
when (intent.action) {
|
||||
UsbManager.ACTION_USB_DEVICE_ATTACHED -> {
|
||||
Log.d("USB_EVENT", "USB Device Connected")
|
||||
refreshUsbConnection()
|
||||
addDebugLog("EVENT: USB device attached")
|
||||
refreshUsbDeviceList()
|
||||
}
|
||||
|
||||
UsbManager.ACTION_USB_DEVICE_DETACHED -> {
|
||||
Log.d("USB_EVENT", "USB Device Disconnected")
|
||||
refreshUsbConnection()
|
||||
addDebugLog("EVENT: USB device detached")
|
||||
val device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE, UsbDevice::class.java)
|
||||
if (device == selectedDevice) {
|
||||
addDebugLog("Selected device was detached")
|
||||
selectedDevice = null
|
||||
usbConnected = false
|
||||
}
|
||||
refreshUsbDeviceList()
|
||||
}
|
||||
|
||||
Constants.INTENT_ACTION_GRANT_USB_PERMISSION -> {
|
||||
if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
|
||||
val device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE, UsbDevice::class.java)
|
||||
val granted = intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)
|
||||
|
||||
addDebugLog("Permission response for device: VID=${device?.vendorId}, PID=${device?.productId}")
|
||||
addDebugLog("Permission granted: $granted")
|
||||
|
||||
if (granted) {
|
||||
Log.d("USB_EVENT", "USB Permission Granted")
|
||||
refreshUsbConnection()
|
||||
addDebugLog("✓ USB permission granted by user")
|
||||
|
||||
// Verify permission with hasPermission
|
||||
if (device != null) {
|
||||
val verified = usbManager.hasPermission(device)
|
||||
addDebugLog("Permission verified with hasPermission(): $verified")
|
||||
|
||||
if (device == selectedDevice && verified) {
|
||||
usbConnected = true
|
||||
addDebugLog("✓ Selected device now connected")
|
||||
}
|
||||
}
|
||||
|
||||
// Refresh immediately
|
||||
refreshUsbDeviceList()
|
||||
} else {
|
||||
addDebugLog("❌ USB permission denied by user")
|
||||
if (device == selectedDevice) {
|
||||
usbConnected = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -227,14 +398,25 @@ fun MainScreen(
|
||||
usbConnected: Boolean,
|
||||
onRefresh: () -> Unit,
|
||||
onSendPatch: () -> Boolean,
|
||||
isPatching: Boolean = false
|
||||
isPatching: Boolean = false,
|
||||
debugLogs: SnapshotStateList<String> = mutableStateListOf(),
|
||||
usbDevices: SnapshotStateList<UsbDeviceInfo> = mutableStateListOf(),
|
||||
selectedDevice: UsbDevice? = null,
|
||||
onDeviceSelected: (UsbDevice) -> Unit = {}
|
||||
) {
|
||||
var buttonText by remember { mutableStateOf("Send FCC Patch") }
|
||||
var buttonEnabled by remember { mutableStateOf(true) }
|
||||
var showDebugLog by remember { mutableStateOf(false) }
|
||||
val uriHandler = LocalUriHandler.current
|
||||
|
||||
Scaffold(topBar = {
|
||||
TopAppBar(title = { Text("DJI FCC Hack") }, actions = {
|
||||
androidx.compose.material3.TextButton(onClick = { showDebugLog = !showDebugLog }) {
|
||||
Text(
|
||||
text = if (showDebugLog) "Hide Log" else "Show Log",
|
||||
style = MaterialTheme.typography.labelMedium
|
||||
)
|
||||
}
|
||||
IconButton(onClick = onRefresh, enabled = !isPatching) {
|
||||
Icon(Icons.Default.Refresh, contentDescription = "Refresh USB Connection")
|
||||
}
|
||||
@ -256,25 +438,25 @@ fun MainScreen(
|
||||
modifier = Modifier.size(75.dp),
|
||||
)
|
||||
|
||||
// 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,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
Text(
|
||||
text = "This app is provided as-is and is not affiliated with DJI. Use at your own risk.",
|
||||
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,
|
||||
// fontWeight = FontWeight.Bold
|
||||
// )
|
||||
// Spacer(modifier = Modifier.height(4.dp))
|
||||
// Text(
|
||||
// text = "This app is provided as-is and is not affiliated with DJI. Use at your own risk.",
|
||||
// style = MaterialTheme.typography.bodyMedium
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
|
||||
// Instructions Section
|
||||
Card(
|
||||
@ -311,28 +493,240 @@ fun MainScreen(
|
||||
}
|
||||
}
|
||||
|
||||
// USB Connection Status
|
||||
// USB Device Selection Card
|
||||
Card(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
shape = RoundedCornerShape(16.dp),
|
||||
colors = CardDefaults.cardColors(
|
||||
containerColor = if (usbConnected) MaterialTheme.colorScheme.primaryContainer else MaterialTheme.colorScheme.errorContainer
|
||||
containerColor = MaterialTheme.colorScheme.surfaceVariant
|
||||
)
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.padding(16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.Center
|
||||
Column(modifier = Modifier.padding(16.dp)) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Icon(
|
||||
Icons.Default.Build,
|
||||
contentDescription = "USB Devices",
|
||||
tint = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Text(
|
||||
text = "USB Devices (${usbDevices.size})",
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
if (usbDevices.isEmpty()) {
|
||||
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
Text(
|
||||
text = "No USB devices found",
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = MaterialTheme.colorScheme.error
|
||||
)
|
||||
// Text(
|
||||
// text = "Troubleshooting steps:",
|
||||
// style = MaterialTheme.typography.bodySmall,
|
||||
// fontWeight = FontWeight.Bold,
|
||||
// color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
// )
|
||||
// Column(modifier = Modifier.padding(start = 8.dp)) {
|
||||
// listOf(
|
||||
// "Make sure phone is UNLOCKED",
|
||||
// "Try unplugging and re-plugging USB",
|
||||
// "Check USB notification - change to 'File Transfer' mode",
|
||||
// "Enable USB debugging in Developer options",
|
||||
// "Known bug in Android 16 - may not work reliably"
|
||||
// ).forEach { tip ->
|
||||
// Row(modifier = Modifier.padding(vertical = 2.dp)) {
|
||||
// Text("• ", style = MaterialTheme.typography.bodySmall)
|
||||
// Text(
|
||||
// text = tip,
|
||||
// style = MaterialTheme.typography.bodySmall,
|
||||
// color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// Spacer(modifier = Modifier.height(4.dp))
|
||||
// Text(
|
||||
// text = "Tap refresh (↻) after trying each step",
|
||||
// style = MaterialTheme.typography.bodySmall,
|
||||
// fontStyle = FontStyle.Italic,
|
||||
// color = MaterialTheme.colorScheme.primary
|
||||
// )
|
||||
}
|
||||
} else {
|
||||
usbDevices.forEach { deviceInfo ->
|
||||
val isSelected = deviceInfo.device == selectedDevice
|
||||
Card(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 4.dp)
|
||||
.clickable { onDeviceSelected(deviceInfo.device) }
|
||||
.then(
|
||||
if (isSelected) {
|
||||
Modifier.border(
|
||||
3.dp,
|
||||
MaterialTheme.colorScheme.primary,
|
||||
RoundedCornerShape(8.dp)
|
||||
)
|
||||
} else {
|
||||
Modifier
|
||||
}
|
||||
),
|
||||
shape = RoundedCornerShape(8.dp),
|
||||
colors = CardDefaults.cardColors(
|
||||
containerColor = if (isSelected)
|
||||
MaterialTheme.colorScheme.primaryContainer
|
||||
else
|
||||
MaterialTheme.colorScheme.surfaceContainerHighest
|
||||
)
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.padding(12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Text(
|
||||
text = "VID: 0x${deviceInfo.device.vendorId.toString(16)} / PID: 0x${deviceInfo.device.productId.toString(16)}",
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = MaterialTheme.colorScheme.onSurface
|
||||
)
|
||||
if (deviceInfo.isDjiDevice) {
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Icon(
|
||||
Icons.Default.Star,
|
||||
contentDescription = "DJI Device",
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
modifier = Modifier.size(20.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
Text(
|
||||
text = deviceInfo.device.deviceName,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
if (deviceInfo.isDjiDevice) {
|
||||
Text(
|
||||
text = "✓ DJI Remote Controller",
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
}
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
Text(
|
||||
text = if (deviceInfo.hasPermission) "✓ Has Permission" else "⚠ No Permission",
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = if (deviceInfo.hasPermission)
|
||||
MaterialTheme.colorScheme.primary
|
||||
else
|
||||
MaterialTheme.colorScheme.error
|
||||
)
|
||||
if (!deviceInfo.hasPermission) {
|
||||
androidx.compose.material3.TextButton(
|
||||
onClick = { onDeviceSelected(deviceInfo.device) },
|
||||
contentPadding = androidx.compose.foundation.layout.PaddingValues(horizontal = 8.dp, vertical = 0.dp),
|
||||
modifier = Modifier.height(24.dp)
|
||||
) {
|
||||
Text(
|
||||
text = "Grant",
|
||||
style = MaterialTheme.typography.labelSmall
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isSelected) {
|
||||
Icon(
|
||||
Icons.Default.CheckCircle,
|
||||
contentDescription = "Selected",
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
modifier = Modifier.size(24.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Debug Log Section
|
||||
if (showDebugLog) {
|
||||
Card(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
shape = RoundedCornerShape(16.dp),
|
||||
colors = CardDefaults.cardColors(
|
||||
containerColor = MaterialTheme.colorScheme.surfaceVariant
|
||||
)
|
||||
) {
|
||||
Icon(
|
||||
if (usbConnected) Icons.Default.CheckCircle else Icons.Default.Clear,
|
||||
contentDescription = "USB Status"
|
||||
)
|
||||
Spacer(Modifier.width(8.dp))
|
||||
Text(
|
||||
text = if (usbConnected) "Remote Connected" else "Remote Not Connected",
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
)
|
||||
Column(modifier = Modifier.padding(16.dp)) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Icon(
|
||||
Icons.Default.Info,
|
||||
contentDescription = "Debug Info",
|
||||
tint = MaterialTheme.colorScheme.primary
|
||||
)
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Text(
|
||||
text = "Debug Log",
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
if (debugLogs.isEmpty()) {
|
||||
Text(
|
||||
text = "No logs yet...",
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
fontStyle = FontStyle.Italic,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
} else {
|
||||
Card(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(200.dp),
|
||||
shape = RoundedCornerShape(8.dp),
|
||||
colors = CardDefaults.cardColors(
|
||||
containerColor = MaterialTheme.colorScheme.surface
|
||||
)
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(8.dp)
|
||||
) {
|
||||
debugLogs.forEach { log ->
|
||||
Text(
|
||||
text = log,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
fontFamily = androidx.compose.ui.text.font.FontFamily.Monospace,
|
||||
modifier = Modifier.padding(vertical = 2.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -353,13 +747,22 @@ fun MainScreen(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(56.dp),
|
||||
enabled = usbConnected && !isPatching && buttonEnabled
|
||||
enabled = selectedDevice != null && !isPatching && buttonEnabled
|
||||
) {
|
||||
Icon(Icons.Default.Build, contentDescription = "Patch")
|
||||
Spacer(Modifier.width(8.dp))
|
||||
Text(buttonText)
|
||||
}
|
||||
|
||||
if (selectedDevice == null && usbDevices.isNotEmpty()) {
|
||||
Text(
|
||||
text = "Please select a USB device above",
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.error,
|
||||
fontStyle = FontStyle.Italic
|
||||
)
|
||||
}
|
||||
|
||||
// Links
|
||||
Row {
|
||||
IconButton(onClick = { uriHandler.openUri(Constants.GITHUB_URL) }) {
|
||||
@ -384,6 +787,17 @@ fun MainScreen(
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = MaterialTheme.colorScheme.secondary,
|
||||
)
|
||||
Text(
|
||||
text = " updated by ",
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.secondary
|
||||
)
|
||||
Text(
|
||||
text = "luhf",
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = MaterialTheme.colorScheme.secondary,
|
||||
)
|
||||
}
|
||||
|
||||
Text(
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
package ch.mathieubroillet.djiffchack.ui.theme
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
val Purple80 = Color(0xFFD0BCFF)
|
||||
val PurpleGrey80 = Color(0xFFCCC2DC)
|
||||
val Pink80 = Color(0xFFEFB8C8)
|
||||
|
||||
val Purple40 = Color(0xFF6650a4)
|
||||
val PurpleGrey40 = Color(0xFF625b71)
|
||||
package ch.mathieubroillet.djiffchack.ui.theme
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
val Purple80 = Color(0xFFD0BCFF)
|
||||
val PurpleGrey80 = Color(0xFFCCC2DC)
|
||||
val Pink80 = Color(0xFFEFB8C8)
|
||||
|
||||
val Purple40 = Color(0xFF6650a4)
|
||||
val PurpleGrey40 = Color(0xFF625b71)
|
||||
val Pink40 = Color(0xFF7D5260)
|
||||
@ -1,57 +1,57 @@
|
||||
package ch.mathieubroillet.djiffchack.ui.theme
|
||||
|
||||
import android.os.Build
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.darkColorScheme
|
||||
import androidx.compose.material3.dynamicDarkColorScheme
|
||||
import androidx.compose.material3.dynamicLightColorScheme
|
||||
import androidx.compose.material3.lightColorScheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
|
||||
private val DarkColorScheme = darkColorScheme(
|
||||
primary = Purple80,
|
||||
secondary = PurpleGrey80,
|
||||
tertiary = Pink80
|
||||
)
|
||||
|
||||
private val LightColorScheme = lightColorScheme(
|
||||
primary = Purple40,
|
||||
secondary = PurpleGrey40,
|
||||
tertiary = Pink40
|
||||
|
||||
/* Other default colors to override
|
||||
background = Color(0xFFFFFBFE),
|
||||
surface = Color(0xFFFFFBFE),
|
||||
onPrimary = Color.White,
|
||||
onSecondary = Color.White,
|
||||
onTertiary = Color.White,
|
||||
onBackground = Color(0xFF1C1B1F),
|
||||
onSurface = Color(0xFF1C1B1F),
|
||||
*/
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun DJI_FCC_HACK_Theme(
|
||||
darkTheme: Boolean = isSystemInDarkTheme(),
|
||||
// Dynamic color is available on Android 12+
|
||||
dynamicColor: Boolean = true,
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
val colorScheme = when {
|
||||
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
|
||||
val context = LocalContext.current
|
||||
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
|
||||
}
|
||||
|
||||
darkTheme -> DarkColorScheme
|
||||
else -> LightColorScheme
|
||||
}
|
||||
|
||||
MaterialTheme(
|
||||
colorScheme = colorScheme,
|
||||
typography = Typography,
|
||||
content = content
|
||||
)
|
||||
package ch.mathieubroillet.djiffchack.ui.theme
|
||||
|
||||
import android.os.Build
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.darkColorScheme
|
||||
import androidx.compose.material3.dynamicDarkColorScheme
|
||||
import androidx.compose.material3.dynamicLightColorScheme
|
||||
import androidx.compose.material3.lightColorScheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
|
||||
private val DarkColorScheme = darkColorScheme(
|
||||
primary = Purple80,
|
||||
secondary = PurpleGrey80,
|
||||
tertiary = Pink80
|
||||
)
|
||||
|
||||
private val LightColorScheme = lightColorScheme(
|
||||
primary = Purple40,
|
||||
secondary = PurpleGrey40,
|
||||
tertiary = Pink40
|
||||
|
||||
/* Other default colors to override
|
||||
background = Color(0xFFFFFBFE),
|
||||
surface = Color(0xFFFFFBFE),
|
||||
onPrimary = Color.White,
|
||||
onSecondary = Color.White,
|
||||
onTertiary = Color.White,
|
||||
onBackground = Color(0xFF1C1B1F),
|
||||
onSurface = Color(0xFF1C1B1F),
|
||||
*/
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun DJI_FCC_HACK_Theme(
|
||||
darkTheme: Boolean = isSystemInDarkTheme(),
|
||||
// Dynamic color is available on Android 12+
|
||||
dynamicColor: Boolean = true,
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
val colorScheme = when {
|
||||
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
|
||||
val context = LocalContext.current
|
||||
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
|
||||
}
|
||||
|
||||
darkTheme -> DarkColorScheme
|
||||
else -> LightColorScheme
|
||||
}
|
||||
|
||||
MaterialTheme(
|
||||
colorScheme = colorScheme,
|
||||
typography = Typography,
|
||||
content = content
|
||||
)
|
||||
}
|
||||
@ -1,34 +1,34 @@
|
||||
package ch.mathieubroillet.djiffchack.ui.theme
|
||||
|
||||
import androidx.compose.material3.Typography
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.sp
|
||||
|
||||
// Set of Material typography styles to start with
|
||||
val Typography = Typography(
|
||||
bodyLarge = TextStyle(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontSize = 16.sp,
|
||||
lineHeight = 24.sp,
|
||||
letterSpacing = 0.5.sp
|
||||
)
|
||||
/* Other default text styles to override
|
||||
titleLarge = TextStyle(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontSize = 22.sp,
|
||||
lineHeight = 28.sp,
|
||||
letterSpacing = 0.sp
|
||||
),
|
||||
labelSmall = TextStyle(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.Medium,
|
||||
fontSize = 11.sp,
|
||||
lineHeight = 16.sp,
|
||||
letterSpacing = 0.5.sp
|
||||
)
|
||||
*/
|
||||
package ch.mathieubroillet.djiffchack.ui.theme
|
||||
|
||||
import androidx.compose.material3.Typography
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.sp
|
||||
|
||||
// Set of Material typography styles to start with
|
||||
val Typography = Typography(
|
||||
bodyLarge = TextStyle(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontSize = 16.sp,
|
||||
lineHeight = 24.sp,
|
||||
letterSpacing = 0.5.sp
|
||||
)
|
||||
/* Other default text styles to override
|
||||
titleLarge = TextStyle(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontSize = 22.sp,
|
||||
lineHeight = 28.sp,
|
||||
letterSpacing = 0.sp
|
||||
),
|
||||
labelSmall = TextStyle(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.Medium,
|
||||
fontSize = 11.sp,
|
||||
lineHeight = 16.sp,
|
||||
letterSpacing = 0.5.sp
|
||||
)
|
||||
*/
|
||||
)
|
||||
@ -1,15 +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="#000000"/>
|
||||
<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="#000000"/>
|
||||
<path
|
||||
android:pathData="m138.69,18.63 l-12.64,53.73l26.09,-0l12.64,-53.73l-26.09,-0z"
|
||||
android:fillColor="#000000"/>
|
||||
</vector>
|
||||
<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="#000000"/>
|
||||
<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="#000000"/>
|
||||
<path
|
||||
android:pathData="m138.69,18.63 l-12.64,53.73l26.09,-0l12.64,-53.73l-26.09,-0z"
|
||||
android:fillColor="#000000"/>
|
||||
</vector>
|
||||
|
||||
@ -1,15 +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>
|
||||
<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>
|
||||
|
||||
@ -1,35 +1,35 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="1066"
|
||||
android:viewportHeight="1053">
|
||||
<path
|
||||
android:pathData="m211.8,0.9h642.4c116.7,0 211.2,94.6 211.2,211.2v629c0,116.6 -94.5,211.1 -211.2,211.1h-642.4c-116.6,0 -211.2,-94.5 -211.2,-211.1v-629c0,-116.6 94.6,-211.2 211.2,-211.2z"
|
||||
android:fillColor="#be3876"/>
|
||||
<path
|
||||
android:pathData="m1.9,838.7l-0.1,-395.1 511.6,608.7h-297.9c-123.3,-1.5 -213.2,-92.4 -213.6,-213.6z"
|
||||
android:fillColor="#9b2b5f"/>
|
||||
<path
|
||||
android:pathData="m278.6,772.9l-76.1,-90.6 -85,347.3c41.6,22.7 82.8,22 128,22.7z"
|
||||
android:fillColor="#a62862"/>
|
||||
<path
|
||||
android:pathData="m245.5,1052.3h267.9l-234.8,-279.4z"
|
||||
android:fillColor="#a0406c"/>
|
||||
<path
|
||||
android:pathData="m278.6,772.9l80.9,-772 -157,681.4z"
|
||||
android:fillColor="#c44b83"/>
|
||||
<path
|
||||
android:pathData="m278.6,772.9l80.9,-772h172.9l314.3,374.9 -160.7,676.4 -172.6,0.1z"
|
||||
android:fillColor="#a62862"/>
|
||||
<path
|
||||
android:pathData="m1065.4,510.1l-533.4,-509.2 533.4,635.5z"
|
||||
android:fillColor="#c44b83"/>
|
||||
<path
|
||||
android:pathData="m1065.4,510.1l-533.4,-509.2h322.2c122.3,0.1 211.4,101.1 211.2,211.2z"
|
||||
android:fillColor="#a0406c"/>
|
||||
<path
|
||||
android:pathData="m686,1052.5c133,-56.7 272.3,-129.9 379.2,-252.8v54.5c0,85.5 -85.9,198.3 -205.5,198.3z"
|
||||
android:strokeAlpha="0.1"
|
||||
android:fillColor="#ac3a6f"
|
||||
android:fillAlpha="0.1"/>
|
||||
</vector>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="1066"
|
||||
android:viewportHeight="1053">
|
||||
<path
|
||||
android:pathData="m211.8,0.9h642.4c116.7,0 211.2,94.6 211.2,211.2v629c0,116.6 -94.5,211.1 -211.2,211.1h-642.4c-116.6,0 -211.2,-94.5 -211.2,-211.1v-629c0,-116.6 94.6,-211.2 211.2,-211.2z"
|
||||
android:fillColor="#be3876"/>
|
||||
<path
|
||||
android:pathData="m1.9,838.7l-0.1,-395.1 511.6,608.7h-297.9c-123.3,-1.5 -213.2,-92.4 -213.6,-213.6z"
|
||||
android:fillColor="#9b2b5f"/>
|
||||
<path
|
||||
android:pathData="m278.6,772.9l-76.1,-90.6 -85,347.3c41.6,22.7 82.8,22 128,22.7z"
|
||||
android:fillColor="#a62862"/>
|
||||
<path
|
||||
android:pathData="m245.5,1052.3h267.9l-234.8,-279.4z"
|
||||
android:fillColor="#a0406c"/>
|
||||
<path
|
||||
android:pathData="m278.6,772.9l80.9,-772 -157,681.4z"
|
||||
android:fillColor="#c44b83"/>
|
||||
<path
|
||||
android:pathData="m278.6,772.9l80.9,-772h172.9l314.3,374.9 -160.7,676.4 -172.6,0.1z"
|
||||
android:fillColor="#a62862"/>
|
||||
<path
|
||||
android:pathData="m1065.4,510.1l-533.4,-509.2 533.4,635.5z"
|
||||
android:fillColor="#c44b83"/>
|
||||
<path
|
||||
android:pathData="m1065.4,510.1l-533.4,-509.2h322.2c122.3,0.1 211.4,101.1 211.2,211.2z"
|
||||
android:fillColor="#a0406c"/>
|
||||
<path
|
||||
android:pathData="m686,1052.5c133,-56.7 272.3,-129.9 379.2,-252.8v54.5c0,85.5 -85.9,198.3 -205.5,198.3z"
|
||||
android:strokeAlpha="0.1"
|
||||
android:fillColor="#ac3a6f"
|
||||
android:fillAlpha="0.1"/>
|
||||
</vector>
|
||||
|
||||
@ -1,14 +1,14 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="633"
|
||||
android:viewportHeight="586">
|
||||
<group android:scaleX="0.67"
|
||||
android:scaleY="0.67"
|
||||
android:translateX="104.445"
|
||||
android:translateY="96.69">
|
||||
<path
|
||||
android:pathData="m319.7,122.6v100.2q0,5.7 -1.5,10.4 -1.4,4.5 -3.8,8 -2.4,3.4 -5.6,5.9 -3.1,2.4 -6.7,3.9 -3.4,1.5 -7,2.3 -3.5,0.7 -6.6,0.7h-40.1q-5.7,0 -10.3,-1.5 -4.5,-1.4 -8,-3.8 -3.4,-2.5 -5.9,-5.6 -2.4,-3.2 -3.9,-6.6 -1.6,-3.5 -2.3,-7 -0.7,-3.6 -0.7,-6.7v-30.1q0,-8.5 3.1,-14.4 3.1,-6 7.7,-9.7 4.7,-3.7 10.2,-5.3 5.5,-1.7 10.1,-1.7h40.1v22.2h-39.9q-4.5,0 -6.8,2.4 -2.3,2.2 -2.3,6.5v29.9q0,4.6 2.2,6.9 2.3,2.3 6.7,2.3h40.1q4.5,0 6.7,-2.3 2.2,-2.3 2.2,-6.7v-100.2zM363.5,122.6v20.1h-22.2v-20.1zM363.5,161.6v101.3q0,5.7 -1.5,10.3 -1.4,4.6 -3.8,8.1 -2.4,3.4 -5.6,5.9 -3.1,2.3 -6.6,3.9 -3.5,1.5 -7,2.3 -3.5,0.7 -6.6,0.7h-40.1v-22.2h40.1q4.4,0 6.6,-2.3 2.3,-2.4 2.3,-6.7v-101.3zM407.3,122.6v20.1h-22.2v-20.1zM407.3,161.6v92.4h-22.2v-92.4zM232.4,391.5v22.4h-70.2v-22.4zM242.3,341.5v22.2h-89.1v100.3h-22.2v-111.3q0,-2.3 0.9,-4.4 0.8,-2 2.3,-3.6 1.5,-1.5 3.6,-2.4 2,-0.8 4.4,-0.8zM370,441.8v22.2h-80.2q-3.1,0 -6.6,-0.7 -3.6,-0.8 -7.1,-2.3 -3.4,-1.5 -6.5,-3.9 -3.2,-2.5 -5.7,-5.9 -2.4,-3.5 -3.8,-8 -1.5,-4.7 -1.5,-10.4v-60.1q0,-3.1 0.7,-6.6 0.8,-3.6 2.3,-7 1.6,-3.5 4,-6.7 2.5,-3.2 5.9,-5.6 3.5,-2.4 8.1,-3.9 4.5,-1.4 10.2,-1.4h80.2v22.2h-80.2q-4.3,0 -6.6,2.3 -2.4,2.3 -2.4,6.8v60q0,4.3 2.4,6.7 2.3,2.3 6.6,2.3zM499.8,441.8v22.2h-80.1q-3.1,0 -6.7,-0.7 -3.5,-0.8 -7,-2.3 -3.4,-1.5 -6.6,-3.9 -3.2,-2.5 -5.6,-5.9 -2.4,-3.5 -3.9,-8 -1.4,-4.7 -1.4,-10.4v-60.1q0,-3.1 0.7,-6.6 0.7,-3.6 2.3,-7 1.5,-3.5 4,-6.7 2.5,-3.2 5.9,-5.6 3.5,-2.4 8,-3.9 4.5,-1.4 10.3,-1.4h80.1v22.2h-80.1q-4.4,0 -6.7,2.3 -2.3,2.3 -2.3,6.8v60q0,4.3 2.3,6.7 2.4,2.3 6.7,2.3z"
|
||||
android:fillColor="#fff"/>
|
||||
</group>
|
||||
</vector>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="633"
|
||||
android:viewportHeight="586">
|
||||
<group android:scaleX="0.67"
|
||||
android:scaleY="0.67"
|
||||
android:translateX="104.445"
|
||||
android:translateY="96.69">
|
||||
<path
|
||||
android:pathData="m319.7,122.6v100.2q0,5.7 -1.5,10.4 -1.4,4.5 -3.8,8 -2.4,3.4 -5.6,5.9 -3.1,2.4 -6.7,3.9 -3.4,1.5 -7,2.3 -3.5,0.7 -6.6,0.7h-40.1q-5.7,0 -10.3,-1.5 -4.5,-1.4 -8,-3.8 -3.4,-2.5 -5.9,-5.6 -2.4,-3.2 -3.9,-6.6 -1.6,-3.5 -2.3,-7 -0.7,-3.6 -0.7,-6.7v-30.1q0,-8.5 3.1,-14.4 3.1,-6 7.7,-9.7 4.7,-3.7 10.2,-5.3 5.5,-1.7 10.1,-1.7h40.1v22.2h-39.9q-4.5,0 -6.8,2.4 -2.3,2.2 -2.3,6.5v29.9q0,4.6 2.2,6.9 2.3,2.3 6.7,2.3h40.1q4.5,0 6.7,-2.3 2.2,-2.3 2.2,-6.7v-100.2zM363.5,122.6v20.1h-22.2v-20.1zM363.5,161.6v101.3q0,5.7 -1.5,10.3 -1.4,4.6 -3.8,8.1 -2.4,3.4 -5.6,5.9 -3.1,2.3 -6.6,3.9 -3.5,1.5 -7,2.3 -3.5,0.7 -6.6,0.7h-40.1v-22.2h40.1q4.4,0 6.6,-2.3 2.3,-2.4 2.3,-6.7v-101.3zM407.3,122.6v20.1h-22.2v-20.1zM407.3,161.6v92.4h-22.2v-92.4zM232.4,391.5v22.4h-70.2v-22.4zM242.3,341.5v22.2h-89.1v100.3h-22.2v-111.3q0,-2.3 0.9,-4.4 0.8,-2 2.3,-3.6 1.5,-1.5 3.6,-2.4 2,-0.8 4.4,-0.8zM370,441.8v22.2h-80.2q-3.1,0 -6.6,-0.7 -3.6,-0.8 -7.1,-2.3 -3.4,-1.5 -6.5,-3.9 -3.2,-2.5 -5.7,-5.9 -2.4,-3.5 -3.8,-8 -1.5,-4.7 -1.5,-10.4v-60.1q0,-3.1 0.7,-6.6 0.8,-3.6 2.3,-7 1.6,-3.5 4,-6.7 2.5,-3.2 5.9,-5.6 3.5,-2.4 8.1,-3.9 4.5,-1.4 10.2,-1.4h80.2v22.2h-80.2q-4.3,0 -6.6,2.3 -2.4,2.3 -2.4,6.8v60q0,4.3 2.4,6.7 2.3,2.3 6.6,2.3zM499.8,441.8v22.2h-80.1q-3.1,0 -6.7,-0.7 -3.5,-0.8 -7,-2.3 -3.4,-1.5 -6.6,-3.9 -3.2,-2.5 -5.6,-5.9 -2.4,-3.5 -3.9,-8 -1.4,-4.7 -1.4,-10.4v-60.1q0,-3.1 0.7,-6.6 0.7,-3.6 2.3,-7 1.5,-3.5 4,-6.7 2.5,-3.2 5.9,-5.6 3.5,-2.4 8,-3.9 4.5,-1.4 10.3,-1.4h80.1v22.2h-80.1q-4.4,0 -6.7,2.3 -2.3,2.3 -2.3,6.8v60q0,4.3 2.3,6.7 2.4,2.3 6.7,2.3z"
|
||||
android:fillColor="#fff"/>
|
||||
</group>
|
||||
</vector>
|
||||
|
||||
@ -1,35 +1,35 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="1066"
|
||||
android:viewportHeight="1053">
|
||||
<path
|
||||
android:pathData="m211.8,0.9h642.4c116.7,0 211.2,94.6 211.2,211.2v629c0,116.6 -94.5,211.1 -211.2,211.1h-642.4c-116.6,0 -211.2,-94.5 -211.2,-211.1v-629c0,-116.6 94.6,-211.2 211.2,-211.2z"
|
||||
android:fillColor="#be3876"/>
|
||||
<path
|
||||
android:pathData="m1.9,838.7l-0.1,-395.1 511.6,608.7h-297.9c-123.3,-1.5 -213.2,-92.4 -213.6,-213.6z"
|
||||
android:fillColor="#9b2b5f"/>
|
||||
<path
|
||||
android:pathData="m278.6,772.9l-76.1,-90.6 -85,347.3c41.6,22.7 82.8,22 128,22.7z"
|
||||
android:fillColor="#a62862"/>
|
||||
<path
|
||||
android:pathData="m245.5,1052.3h267.9l-234.8,-279.4z"
|
||||
android:fillColor="#a0406c"/>
|
||||
<path
|
||||
android:pathData="m278.6,772.9l80.9,-772 -157,681.4z"
|
||||
android:fillColor="#c44b83"/>
|
||||
<path
|
||||
android:pathData="m278.6,772.9l80.9,-772h172.9l314.3,374.9 -160.7,676.4 -172.6,0.1z"
|
||||
android:fillColor="#a62862"/>
|
||||
<path
|
||||
android:pathData="m1065.4,510.1l-533.4,-509.2 533.4,635.5z"
|
||||
android:fillColor="#c44b83"/>
|
||||
<path
|
||||
android:pathData="m1065.4,510.1l-533.4,-509.2h322.2c122.3,0.1 211.4,101.1 211.2,211.2z"
|
||||
android:fillColor="#a0406c"/>
|
||||
<path
|
||||
android:pathData="m686,1052.5c133,-56.7 272.3,-129.9 379.2,-252.8v54.5c0,85.5 -85.9,198.3 -205.5,198.3z"
|
||||
android:strokeAlpha="0.1"
|
||||
android:fillColor="#ac3a6f"
|
||||
android:fillAlpha="0.1"/>
|
||||
</vector>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="1066"
|
||||
android:viewportHeight="1053">
|
||||
<path
|
||||
android:pathData="m211.8,0.9h642.4c116.7,0 211.2,94.6 211.2,211.2v629c0,116.6 -94.5,211.1 -211.2,211.1h-642.4c-116.6,0 -211.2,-94.5 -211.2,-211.1v-629c0,-116.6 94.6,-211.2 211.2,-211.2z"
|
||||
android:fillColor="#be3876"/>
|
||||
<path
|
||||
android:pathData="m1.9,838.7l-0.1,-395.1 511.6,608.7h-297.9c-123.3,-1.5 -213.2,-92.4 -213.6,-213.6z"
|
||||
android:fillColor="#9b2b5f"/>
|
||||
<path
|
||||
android:pathData="m278.6,772.9l-76.1,-90.6 -85,347.3c41.6,22.7 82.8,22 128,22.7z"
|
||||
android:fillColor="#a62862"/>
|
||||
<path
|
||||
android:pathData="m245.5,1052.3h267.9l-234.8,-279.4z"
|
||||
android:fillColor="#a0406c"/>
|
||||
<path
|
||||
android:pathData="m278.6,772.9l80.9,-772 -157,681.4z"
|
||||
android:fillColor="#c44b83"/>
|
||||
<path
|
||||
android:pathData="m278.6,772.9l80.9,-772h172.9l314.3,374.9 -160.7,676.4 -172.6,0.1z"
|
||||
android:fillColor="#a62862"/>
|
||||
<path
|
||||
android:pathData="m1065.4,510.1l-533.4,-509.2 533.4,635.5z"
|
||||
android:fillColor="#c44b83"/>
|
||||
<path
|
||||
android:pathData="m1065.4,510.1l-533.4,-509.2h322.2c122.3,0.1 211.4,101.1 211.2,211.2z"
|
||||
android:fillColor="#a0406c"/>
|
||||
<path
|
||||
android:pathData="m686,1052.5c133,-56.7 272.3,-129.9 379.2,-252.8v54.5c0,85.5 -85.9,198.3 -205.5,198.3z"
|
||||
android:strokeAlpha="0.1"
|
||||
android:fillColor="#ac3a6f"
|
||||
android:fillAlpha="0.1"/>
|
||||
</vector>
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="633"
|
||||
android:viewportHeight="586">
|
||||
<path
|
||||
android:pathData="m319.7,122.6v100.2q0,5.7 -1.5,10.4 -1.4,4.5 -3.8,8 -2.4,3.4 -5.6,5.9 -3.1,2.4 -6.7,3.9 -3.4,1.5 -7,2.3 -3.5,0.7 -6.6,0.7h-40.1q-5.7,0 -10.3,-1.5 -4.5,-1.4 -8,-3.8 -3.4,-2.5 -5.9,-5.6 -2.4,-3.2 -3.9,-6.6 -1.6,-3.5 -2.3,-7 -0.7,-3.6 -0.7,-6.7v-30.1q0,-8.5 3.1,-14.4 3.1,-6 7.7,-9.7 4.7,-3.7 10.2,-5.3 5.5,-1.7 10.1,-1.7h40.1v22.2h-39.9q-4.5,0 -6.8,2.4 -2.3,2.2 -2.3,6.5v29.9q0,4.6 2.2,6.9 2.3,2.3 6.7,2.3h40.1q4.5,0 6.7,-2.3 2.2,-2.3 2.2,-6.7v-100.2zM363.5,122.6v20.1h-22.2v-20.1zM363.5,161.6v101.3q0,5.7 -1.5,10.3 -1.4,4.6 -3.8,8.1 -2.4,3.4 -5.6,5.9 -3.1,2.3 -6.6,3.9 -3.5,1.5 -7,2.3 -3.5,0.7 -6.6,0.7h-40.1v-22.2h40.1q4.4,0 6.6,-2.3 2.3,-2.4 2.3,-6.7v-101.3zM407.3,122.6v20.1h-22.2v-20.1zM407.3,161.6v92.4h-22.2v-92.4zM232.4,391.5v22.4h-70.2v-22.4zM242.3,341.5v22.2h-89.1v100.3h-22.2v-111.3q0,-2.3 0.9,-4.4 0.8,-2 2.3,-3.6 1.5,-1.5 3.6,-2.4 2,-0.8 4.4,-0.8zM370,441.8v22.2h-80.2q-3.1,0 -6.6,-0.7 -3.6,-0.8 -7.1,-2.3 -3.4,-1.5 -6.5,-3.9 -3.2,-2.5 -5.7,-5.9 -2.4,-3.5 -3.8,-8 -1.5,-4.7 -1.5,-10.4v-60.1q0,-3.1 0.7,-6.6 0.8,-3.6 2.3,-7 1.6,-3.5 4,-6.7 2.5,-3.2 5.9,-5.6 3.5,-2.4 8.1,-3.9 4.5,-1.4 10.2,-1.4h80.2v22.2h-80.2q-4.3,0 -6.6,2.3 -2.4,2.3 -2.4,6.8v60q0,4.3 2.4,6.7 2.3,2.3 6.6,2.3zM499.8,441.8v22.2h-80.1q-3.1,0 -6.7,-0.7 -3.5,-0.8 -7,-2.3 -3.4,-1.5 -6.6,-3.9 -3.2,-2.5 -5.6,-5.9 -2.4,-3.5 -3.9,-8 -1.4,-4.7 -1.4,-10.4v-60.1q0,-3.1 0.7,-6.6 0.7,-3.6 2.3,-7 1.5,-3.5 4,-6.7 2.5,-3.2 5.9,-5.6 3.5,-2.4 8,-3.9 4.5,-1.4 10.3,-1.4h80.1v22.2h-80.1q-4.4,0 -6.7,2.3 -2.3,2.3 -2.3,6.8v60q0,4.3 2.3,6.7 2.4,2.3 6.7,2.3z"
|
||||
android:fillColor="#fff"/>
|
||||
</vector>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="633"
|
||||
android:viewportHeight="586">
|
||||
<path
|
||||
android:pathData="m319.7,122.6v100.2q0,5.7 -1.5,10.4 -1.4,4.5 -3.8,8 -2.4,3.4 -5.6,5.9 -3.1,2.4 -6.7,3.9 -3.4,1.5 -7,2.3 -3.5,0.7 -6.6,0.7h-40.1q-5.7,0 -10.3,-1.5 -4.5,-1.4 -8,-3.8 -3.4,-2.5 -5.9,-5.6 -2.4,-3.2 -3.9,-6.6 -1.6,-3.5 -2.3,-7 -0.7,-3.6 -0.7,-6.7v-30.1q0,-8.5 3.1,-14.4 3.1,-6 7.7,-9.7 4.7,-3.7 10.2,-5.3 5.5,-1.7 10.1,-1.7h40.1v22.2h-39.9q-4.5,0 -6.8,2.4 -2.3,2.2 -2.3,6.5v29.9q0,4.6 2.2,6.9 2.3,2.3 6.7,2.3h40.1q4.5,0 6.7,-2.3 2.2,-2.3 2.2,-6.7v-100.2zM363.5,122.6v20.1h-22.2v-20.1zM363.5,161.6v101.3q0,5.7 -1.5,10.3 -1.4,4.6 -3.8,8.1 -2.4,3.4 -5.6,5.9 -3.1,2.3 -6.6,3.9 -3.5,1.5 -7,2.3 -3.5,0.7 -6.6,0.7h-40.1v-22.2h40.1q4.4,0 6.6,-2.3 2.3,-2.4 2.3,-6.7v-101.3zM407.3,122.6v20.1h-22.2v-20.1zM407.3,161.6v92.4h-22.2v-92.4zM232.4,391.5v22.4h-70.2v-22.4zM242.3,341.5v22.2h-89.1v100.3h-22.2v-111.3q0,-2.3 0.9,-4.4 0.8,-2 2.3,-3.6 1.5,-1.5 3.6,-2.4 2,-0.8 4.4,-0.8zM370,441.8v22.2h-80.2q-3.1,0 -6.6,-0.7 -3.6,-0.8 -7.1,-2.3 -3.4,-1.5 -6.5,-3.9 -3.2,-2.5 -5.7,-5.9 -2.4,-3.5 -3.8,-8 -1.5,-4.7 -1.5,-10.4v-60.1q0,-3.1 0.7,-6.6 0.8,-3.6 2.3,-7 1.6,-3.5 4,-6.7 2.5,-3.2 5.9,-5.6 3.5,-2.4 8.1,-3.9 4.5,-1.4 10.2,-1.4h80.2v22.2h-80.2q-4.3,0 -6.6,2.3 -2.4,2.3 -2.4,6.8v60q0,4.3 2.4,6.7 2.3,2.3 6.6,2.3zM499.8,441.8v22.2h-80.1q-3.1,0 -6.7,-0.7 -3.5,-0.8 -7,-2.3 -3.4,-1.5 -6.6,-3.9 -3.2,-2.5 -5.6,-5.9 -2.4,-3.5 -3.9,-8 -1.4,-4.7 -1.4,-10.4v-60.1q0,-3.1 0.7,-6.6 0.7,-3.6 2.3,-7 1.5,-3.5 4,-6.7 2.5,-3.2 5.9,-5.6 3.5,-2.4 8,-3.9 4.5,-1.4 10.3,-1.4h80.1v22.2h-80.1q-4.4,0 -6.7,2.3 -2.3,2.3 -2.3,6.8v60q0,4.3 2.3,6.7 2.4,2.3 6.7,2.3z"
|
||||
android:fillColor="#fff"/>
|
||||
</vector>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/icon_background"/>
|
||||
<foreground android:drawable="@drawable/icon_foreground"/>
|
||||
<monochrome android:drawable="@drawable/icon_foreground"/>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/icon_background"/>
|
||||
<foreground android:drawable="@drawable/icon_foreground"/>
|
||||
<monochrome android:drawable="@drawable/icon_foreground"/>
|
||||
</adaptive-icon>
|
||||
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/icon_background"/>
|
||||
<foreground android:drawable="@drawable/icon_foreground"/>
|
||||
<monochrome android:drawable="@drawable/icon_foreground"/>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/icon_background"/>
|
||||
<foreground android:drawable="@drawable/icon_foreground"/>
|
||||
<monochrome android:drawable="@drawable/icon_foreground"/>
|
||||
</adaptive-icon>
|
||||
@ -1,10 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="purple_200">#FFBB86FC</color>
|
||||
<color name="purple_500">#FF6200EE</color>
|
||||
<color name="purple_700">#FF3700B3</color>
|
||||
<color name="teal_200">#FF03DAC5</color>
|
||||
<color name="teal_700">#FF018786</color>
|
||||
<color name="black">#FF000000</color>
|
||||
<color name="white">#FFFFFFFF</color>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="purple_200">#FFBB86FC</color>
|
||||
<color name="purple_500">#FF6200EE</color>
|
||||
<color name="purple_700">#FF3700B3</color>
|
||||
<color name="teal_200">#FF03DAC5</color>
|
||||
<color name="teal_700">#FF018786</color>
|
||||
<color name="black">#FF000000</color>
|
||||
<color name="white">#FFFFFFFF</color>
|
||||
</resources>
|
||||
@ -1,3 +1,3 @@
|
||||
<resources>
|
||||
<string name="app_name">DJI FCC</string>
|
||||
<resources>
|
||||
<string name="app_name">DJI FCC</string>
|
||||
</resources>
|
||||
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<style name="Theme.Djiffchack" parent="android:Theme.Material.Light.NoActionBar" />
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<style name="Theme.Djiffchack" parent="android:Theme.Material.Light.NoActionBar" />
|
||||
</resources>
|
||||
@ -1,13 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
Sample backup rules file; uncomment and customize as necessary.
|
||||
See https://developer.android.com/guide/topics/data/autobackup
|
||||
for details.
|
||||
Note: This file is ignored for devices older that API 31
|
||||
See https://developer.android.com/about/versions/12/backup-restore
|
||||
-->
|
||||
<full-backup-content>
|
||||
<!--
|
||||
<include domain="sharedpref" path="."/>
|
||||
<exclude domain="sharedpref" path="device.xml"/>
|
||||
-->
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
Sample backup rules file; uncomment and customize as necessary.
|
||||
See https://developer.android.com/guide/topics/data/autobackup
|
||||
for details.
|
||||
Note: This file is ignored for devices older that API 31
|
||||
See https://developer.android.com/about/versions/12/backup-restore
|
||||
-->
|
||||
<full-backup-content>
|
||||
<!--
|
||||
<include domain="sharedpref" path="."/>
|
||||
<exclude domain="sharedpref" path="device.xml"/>
|
||||
-->
|
||||
</full-backup-content>
|
||||
@ -1,19 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
Sample data extraction rules file; uncomment and customize as necessary.
|
||||
See https://developer.android.com/about/versions/12/backup-restore#xml-changes
|
||||
for details.
|
||||
-->
|
||||
<data-extraction-rules>
|
||||
<cloud-backup>
|
||||
<!-- TODO: Use <include> and <exclude> to control what is backed up.
|
||||
<include .../>
|
||||
<exclude .../>
|
||||
-->
|
||||
</cloud-backup>
|
||||
<!--
|
||||
<device-transfer>
|
||||
<include .../>
|
||||
<exclude .../>
|
||||
</device-transfer>
|
||||
-->
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
Sample data extraction rules file; uncomment and customize as necessary.
|
||||
See https://developer.android.com/about/versions/12/backup-restore#xml-changes
|
||||
for details.
|
||||
-->
|
||||
<data-extraction-rules>
|
||||
<cloud-backup>
|
||||
<!-- TODO: Use <include> and <exclude> to control what is backed up.
|
||||
<include .../>
|
||||
<exclude .../>
|
||||
-->
|
||||
</cloud-backup>
|
||||
<!--
|
||||
<device-transfer>
|
||||
<include .../>
|
||||
<exclude .../>
|
||||
</device-transfer>
|
||||
-->
|
||||
</data-extraction-rules>
|
||||
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<usb-device
|
||||
product-id="4128"
|
||||
vendor-id="11427"/>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<usb-device
|
||||
product-id="4128"
|
||||
vendor-id="11427"/>
|
||||
</resources>
|
||||
@ -1,17 +1,17 @@
|
||||
package ch.mathieubroillet.djiffchack
|
||||
|
||||
import org.junit.Test
|
||||
|
||||
import org.junit.Assert.*
|
||||
|
||||
/**
|
||||
* Example local unit test, which will execute on the development machine (host).
|
||||
*
|
||||
* See [testing documentation](http://d.android.com/tools/testing).
|
||||
*/
|
||||
class ExampleUnitTest {
|
||||
@Test
|
||||
fun addition_isCorrect() {
|
||||
assertEquals(4, 2 + 2)
|
||||
}
|
||||
package ch.mathieubroillet.djiffchack
|
||||
|
||||
import org.junit.Test
|
||||
|
||||
import org.junit.Assert.*
|
||||
|
||||
/**
|
||||
* Example local unit test, which will execute on the development machine (host).
|
||||
*
|
||||
* See [testing documentation](http://d.android.com/tools/testing).
|
||||
*/
|
||||
class ExampleUnitTest {
|
||||
@Test
|
||||
fun addition_isCorrect() {
|
||||
assertEquals(4, 2 + 2)
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
plugins {
|
||||
alias(libs.plugins.android.application) apply false
|
||||
alias(libs.plugins.kotlin.android) apply false
|
||||
alias(libs.plugins.kotlin.compose) apply false
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
plugins {
|
||||
alias(libs.plugins.android.application) apply false
|
||||
alias(libs.plugins.kotlin.android) apply false
|
||||
alias(libs.plugins.kotlin.compose) apply false
|
||||
}
|
||||
84
dji_fcc_patch.py
Normal file
84
dji_fcc_patch.py
Normal file
@ -0,0 +1,84 @@
|
||||
# DJI FCC Patch Script for Windows
|
||||
#
|
||||
# This script sends a "magic packet" to a DJI controller
|
||||
# to enable FCC mode, which can increase the drone's signal range.
|
||||
#
|
||||
# Credits:
|
||||
# - Based on the Android app by Mathieu Broillet: https://github.com/M4TH1EU/DJI-FCC-HACK
|
||||
# - Original research by @galbb on MavicPilots.com
|
||||
#
|
||||
# HOW TO USE:
|
||||
# 1. Install Python from the Microsoft Store or python.org.
|
||||
# 2. Install the pyserial library by opening a Command Prompt or PowerShell and running:
|
||||
# pip install pyserial
|
||||
# 3. Turn on your drone and controller, and wait for them to connect.
|
||||
# 4. Connect your PC to the bottom USB port of the controller.
|
||||
# 5. Run this script.
|
||||
# 6. If successful, disconnect the PC and connect your phone to the top USB port of the controller.
|
||||
#
|
||||
# DISCLAIMER: Use at your own risk. This script modifies your device's operation.
|
||||
# The authors are not responsible for any damage or legal issues that may arise.
|
||||
|
||||
import serial
|
||||
import serial.tools.list_ports
|
||||
import time
|
||||
|
||||
# DJI Controller USB identifiers
|
||||
VENDOR_ID = 11427
|
||||
PRODUCT_ID = 4128
|
||||
|
||||
# The "magic bytes" that enable FCC mode
|
||||
# (from ch.mathieubroillet.djiffchack.Constants.kt)
|
||||
BYTES_1 = bytes([85, 13, 4, 33, 42, 31, 0, 0, 0, 0, 1, -122 & 0xFF, 32])
|
||||
BYTES_2 = bytes([85, 24, 4, 32, 2, 9, 0, 0, 64, 9, 39, 0, 2, 72, 0, -1 & 0xFF, -1 & 0xFF, 2, 0, 0, 0, 0, -127 & 0xFF, 31])
|
||||
|
||||
def find_dji_controller():
|
||||
"""Finds the COM port of the connected DJI controller."""
|
||||
ports = serial.tools.list_ports.comports()
|
||||
for port in ports:
|
||||
if port.vid == VENDOR_ID and port.pid == PRODUCT_ID:
|
||||
print(f"Found DJI Controller at {port.device}")
|
||||
return port.device
|
||||
return None
|
||||
|
||||
def send_patch(com_port):
|
||||
"""Opens the serial port and sends the patch bytes."""
|
||||
try:
|
||||
with serial.Serial(com_port, baudrate=19200, bytesize=8, parity='N', stopbits=1, timeout=1) as ser:
|
||||
print("Sending patch...")
|
||||
|
||||
# Send first byte array
|
||||
ser.write(BYTES_1)
|
||||
print(f"Sent {len(BYTES_1)} bytes (Packet 1).")
|
||||
time.sleep(0.1) # Small delay between writes
|
||||
|
||||
# Send second byte array
|
||||
ser.write(BYTES_2)
|
||||
print(f"Sent {len(BYTES_2)} bytes (Packet 2).")
|
||||
|
||||
print("\nPatch sent successfully!")
|
||||
print("You can now disconnect the controller from your PC.")
|
||||
return True
|
||||
except serial.SerialException as e:
|
||||
print(f"Error: Could not open or write to serial port {com_port}.")
|
||||
print(f"Details: {e}")
|
||||
print("\nPlease ensure the controller is properly connected and no other software is using the COM port.")
|
||||
return False
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("--- DJI FCC Patch Script ---")
|
||||
print("Searching for DJI Controller...")
|
||||
|
||||
controller_port = find_dji_controller()
|
||||
|
||||
if controller_port:
|
||||
send_patch(controller_port)
|
||||
else:
|
||||
print("\nDJI Controller not found.")
|
||||
print("Please make sure:")
|
||||
print("1. The controller is turned on and connected to the drone.")
|
||||
print("2. Your PC is connected to the BOTTOM USB port of the controller.")
|
||||
print("3. The necessary USB drivers are installed.")
|
||||
|
||||
print("\nPress Enter to exit.")
|
||||
input()
|
||||
@ -1,23 +1,23 @@
|
||||
# Project-wide Gradle settings.
|
||||
# IDE (e.g. Android Studio) users:
|
||||
# Gradle settings configured through the IDE *will override*
|
||||
# any settings specified in this file.
|
||||
# For more details on how to configure your build environment visit
|
||||
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||
# Specifies the JVM arguments used for the daemon process.
|
||||
# The setting is particularly useful for tweaking memory settings.
|
||||
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
|
||||
# When configured, Gradle will run in incubating parallel mode.
|
||||
# This option should only be used with decoupled projects. For more details, visit
|
||||
# https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects
|
||||
# org.gradle.parallel=true
|
||||
# AndroidX package structure to make it clearer which packages are bundled with the
|
||||
# Android operating system, and which are packaged with your app's APK
|
||||
# https://developer.android.com/topic/libraries/support-library/androidx-rn
|
||||
android.useAndroidX=true
|
||||
# Kotlin code style for this project: "official" or "obsolete":
|
||||
kotlin.code.style=official
|
||||
# Enables namespacing of each library's R class so that its R class includes only the
|
||||
# resources declared in the library itself and none from the library's dependencies,
|
||||
# thereby reducing the size of the R class for that library
|
||||
# Project-wide Gradle settings.
|
||||
# IDE (e.g. Android Studio) users:
|
||||
# Gradle settings configured through the IDE *will override*
|
||||
# any settings specified in this file.
|
||||
# For more details on how to configure your build environment visit
|
||||
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||
# Specifies the JVM arguments used for the daemon process.
|
||||
# The setting is particularly useful for tweaking memory settings.
|
||||
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
|
||||
# When configured, Gradle will run in incubating parallel mode.
|
||||
# This option should only be used with decoupled projects. For more details, visit
|
||||
# https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects
|
||||
# org.gradle.parallel=true
|
||||
# AndroidX package structure to make it clearer which packages are bundled with the
|
||||
# Android operating system, and which are packaged with your app's APK
|
||||
# https://developer.android.com/topic/libraries/support-library/androidx-rn
|
||||
android.useAndroidX=true
|
||||
# Kotlin code style for this project: "official" or "obsolete":
|
||||
kotlin.code.style=official
|
||||
# Enables namespacing of each library's R class so that its R class includes only the
|
||||
# resources declared in the library itself and none from the library's dependencies,
|
||||
# thereby reducing the size of the R class for that library
|
||||
android.nonTransitiveRClass=true
|
||||
@ -1,33 +1,33 @@
|
||||
[versions]
|
||||
agp = "8.11.0"
|
||||
kotlin = "2.0.0"
|
||||
coreKtx = "1.10.1"
|
||||
junit = "4.13.2"
|
||||
junitVersion = "1.1.5"
|
||||
espressoCore = "3.5.1"
|
||||
lifecycleRuntimeKtx = "2.6.1"
|
||||
activityCompose = "1.8.0"
|
||||
composeBom = "2024.04.01"
|
||||
|
||||
[libraries]
|
||||
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
|
||||
junit = { group = "junit", name = "junit", version.ref = "junit" }
|
||||
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
|
||||
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
|
||||
androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" }
|
||||
androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" }
|
||||
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
|
||||
androidx-ui = { group = "androidx.compose.ui", name = "ui" }
|
||||
androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
|
||||
androidx-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
|
||||
androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
|
||||
androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
|
||||
androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
|
||||
androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
|
||||
|
||||
|
||||
[plugins]
|
||||
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
|
||||
kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
|
||||
|
||||
[versions]
|
||||
agp = "8.11.0"
|
||||
kotlin = "2.0.0"
|
||||
coreKtx = "1.10.1"
|
||||
junit = "4.13.2"
|
||||
junitVersion = "1.1.5"
|
||||
espressoCore = "3.5.1"
|
||||
lifecycleRuntimeKtx = "2.6.1"
|
||||
activityCompose = "1.8.0"
|
||||
composeBom = "2024.04.01"
|
||||
|
||||
[libraries]
|
||||
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
|
||||
junit = { group = "junit", name = "junit", version.ref = "junit" }
|
||||
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
|
||||
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
|
||||
androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" }
|
||||
androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" }
|
||||
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
|
||||
androidx-ui = { group = "androidx.compose.ui", name = "ui" }
|
||||
androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
|
||||
androidx-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
|
||||
androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
|
||||
androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
|
||||
androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
|
||||
androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
|
||||
|
||||
|
||||
[plugins]
|
||||
android-application = { id = "com.android.application", version = "8.6.0" }
|
||||
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
|
||||
kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
|
||||
|
||||
|
||||
7
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
7
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-all.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
5
gradlew
vendored
5
gradlew
vendored
@ -174,7 +174,8 @@ fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\''/g;1s/^/'/;
|
||||
" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=`save "$@"`
|
||||
@ -182,4 +183,4 @@ APP_ARGS=`save "$@"`
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
exec "$JAVACMD" "$@"
|
||||
@ -1,24 +1,24 @@
|
||||
pluginManagement {
|
||||
repositories {
|
||||
google {
|
||||
content {
|
||||
includeGroupByRegex("com\\.android.*")
|
||||
includeGroupByRegex("com\\.google.*")
|
||||
includeGroupByRegex("androidx.*")
|
||||
}
|
||||
}
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
}
|
||||
}
|
||||
dependencyResolutionManagement {
|
||||
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
maven(url = "https://jitpack.io")
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.name = "dji-ffc-hack"
|
||||
include(":app")
|
||||
pluginManagement {
|
||||
repositories {
|
||||
google {
|
||||
content {
|
||||
includeGroupByRegex("com\\.android.*")
|
||||
includeGroupByRegex("com\\.google.*")
|
||||
includeGroupByRegex("androidx.*")
|
||||
}
|
||||
}
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
}
|
||||
}
|
||||
dependencyResolutionManagement {
|
||||
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
maven(url = "https://jitpack.io")
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.name = "dji-ffc-hack"
|
||||
include(":app")
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user