├── .gitignore ├── .gitmodules ├── README.md ├── examples ├── android-client │ ├── .gitignore │ ├── app │ │ ├── .gitignore │ │ ├── build.gradle │ │ ├── lint.xml │ │ ├── proguard-rules.pro │ │ └── src │ │ │ ├── androidTest │ │ │ └── java │ │ │ │ └── io │ │ │ │ └── mavsdk │ │ │ │ └── androidclient │ │ │ │ └── ExampleInstrumentedTest.java │ │ │ ├── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── assets │ │ │ │ └── logback.xml │ │ │ ├── java │ │ │ │ └── io │ │ │ │ │ └── mavsdk │ │ │ │ │ └── androidclient │ │ │ │ │ ├── MapsActivity.java │ │ │ │ │ └── MapsViewModel.java │ │ │ └── res │ │ │ │ ├── drawable-v24 │ │ │ │ └── ic_launcher_foreground.xml │ │ │ │ ├── drawable │ │ │ │ ├── ic_launcher_background.xml │ │ │ │ └── ic_play_arrow_black_24dp.xml │ │ │ │ ├── layout │ │ │ │ └── activity_maps.xml │ │ │ │ ├── menu │ │ │ │ └── menu_maps.xml │ │ │ │ ├── mipmap-anydpi-v26 │ │ │ │ ├── ic_launcher.xml │ │ │ │ └── ic_launcher_round.xml │ │ │ │ ├── mipmap-hdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ │ └── values │ │ │ │ ├── strings.xml │ │ │ │ └── styles.xml │ │ │ └── test │ │ │ └── java │ │ │ └── io │ │ │ └── mavsdk │ │ │ └── androidclient │ │ │ └── ExampleUnitTest.java │ ├── build.gradle │ ├── config │ │ └── checkstyle │ │ │ └── checkstyle.xml │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ └── settings.gradle └── java-client │ ├── README.md │ ├── build.gradle │ ├── config │ └── checkstyle │ │ └── checkstyle.xml │ ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── settings.gradle │ └── src │ └── main │ └── java │ └── io │ └── mavsdk │ └── example │ ├── Calibrate.java │ ├── DownloadLastLog.java │ ├── ListPhotos.java │ ├── RunCamera.java │ ├── RunMission.java │ ├── SetRtlAltitude.java │ ├── SystemShell.java │ └── TakeoffAndLand.java ├── mavsdk_server ├── .gitignore ├── build.gradle ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src │ └── main │ ├── AndroidManifest.xml │ ├── cpp │ ├── CMakeLists.txt │ └── native-lib.cpp │ └── java │ └── io │ └── mavsdk │ └── mavsdkserver │ └── MavsdkServer.java └── sdk ├── README.md ├── build.gradle ├── config └── checkstyle │ ├── checkstyle.xml │ └── suppressions.xml ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── mavsdk ├── settings.gradle └── src │ ├── main │ └── java │ │ └── io │ │ └── mavsdk │ │ ├── MavsdkEventQueue.java │ │ ├── MavsdkException.java │ │ ├── Plugin.java │ │ ├── System.java │ │ └── internal │ │ ├── LazyPlugin.java │ │ └── Provider.java │ └── test │ └── java │ └── io │ └── mavsdk │ └── MavsdkTest.java ├── settings.gradle └── templates ├── call.j2 ├── enum.j2 ├── file.j2 ├── request.j2 ├── stream.j2 ├── struct.j2 └── type_conversions /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | 3 | .idea 4 | .gradle 5 | 6 | *.iml 7 | 8 | local.properties 9 | gradle.properties 10 | 11 | .cxx 12 | venv 13 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "sdk/proto"] 2 | path = sdk/proto 3 | url = https://github.com/dronecore/DroneCore-Proto.git 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MAVSDK-Java 2 | 3 | This is the Java frontend implementation to [MAVSDK](https://github.com/mavlink/MAVSDK). 4 | 5 | It is organized as follows: 6 | 7 | * The [examples](./examples) directory contains Java and Android examples using the sdk. 8 | * The [sdk](./sdk) directory contains the actual SDK. 9 | * The [mavsdk_server](./mavsdk_server) directory contains the Android library exposing `mavsdk_server`. 10 | 11 | ## QuickStart 12 | 13 | The fastest way to start is to follow the instructions in the README of the [java-client](./examples/java-client) example. For Android, the [android-client](./examples/android-client) is the next step. 14 | 15 | Please note `MavsdkServer` currently is not compatible for running on x86 and x86_64 Android images. If you would like to develop using `MavsdkServer`, you will need to deploy and develop using an ARM-based product or emulator. 16 | 17 | MAVSDK-Java is distributed through MavenCentral, meaning that it can be imported using gradle with: 18 | 19 | ``` 20 | dependencies { 21 | ... 22 | implementation 'io.mavsdk:mavsdk:3.0.0' 23 | ... 24 | } 25 | ``` 26 | 27 | For Android, `mavsdk_server` is distributed as an Android library (`aar`): 28 | 29 | ``` 30 | dependencies { 31 | ... 32 | implementation 'io.mavsdk:mavsdk:3.0.0' 33 | implementation 'io.mavsdk:mavsdk-server:3.0.0' 34 | ... 35 | } 36 | ``` 37 | 38 | ### ProGuard 39 | 40 | ProGuard users may need to add the following rule: 41 | 42 | ``` 43 | -keep class io.mavsdk.** { *; } 44 | ``` 45 | 46 | ### Notes 47 | 48 | 1. `MAVSDK-Java`'s plugins initialize on a background thread (`mavsdk-event-queue`). The initializations happen in a thread-safe manner and the library handles the correct order itself. This is done to provide a simple API to the users. 49 | 50 | 2. For Android, run the `mavsdk_server` as follows: 51 | 52 | ```java 53 | MavsdkServer server = new MavsdkServer(); 54 | MavsdkEventQueue.executor().execute(() -> server.run(SYSTEM_ADDRESS, MAVSDK_SERVER_PORT)); 55 | ``` 56 | 57 | This makes sure that the calling thread (which may be the UI thread) is not blocked as the `mavsdk_server` discovers a system. This should ideally be done before the user creates the `io.mavsdk.System` so that `MavsdkServer.run()` is the first command to run in the `mavsdk-event-queue`. 58 | 59 | 60 | To stop the server: 61 | 62 | ```java 63 | MavsdkEventQueue.executor().execute(() -> server.stop()); 64 | ``` 65 | 66 | 3. Users should avoid using the plugins directly by accessing them only through `io.mavsdk.System` objects. 67 | 68 | The plugins are constructed and initialized lazily upon their first call through `System`, therefore the users do not bear any runtime overhead for the plugins that they won't be using. 69 | 70 | 4. Data streams start flowing in the background once the system is discovered by the `mavsdk_server`, so they are safe to subscribe to immediately after the creation of a `System` object. Streams that are not accessed won't start flowing. 71 | 72 | 5. One-shot calls like `takeoff` and `land` are not added to the `mavsdk-event-queue` when the user subscribes to them. This is done to avoid their piling up while the `mavsdk_server` discovers a system. Instead, the `onError` callback will be triggered after a 100ms delay indicating that no system was available for the command. 73 | 74 | ## Contributing 75 | 76 | ### Coding style 77 | 78 | Java/Android coding style is ensured using CheckStyle with the Google style. 79 | 80 | #### Command line 81 | 82 | A `checkstyle` task is defined in the root `build.gradle` of each project and can be run as follows: 83 | 84 | $ ./gradlew checkstyle 85 | 86 | The `build` task depends on `checkstyle`, meaning that `$ ./gradlew build` runs the checks as well. 87 | 88 | #### IntelliJ / Android-Studio 89 | 90 | There exist a plugin for CheckStyle in JetBrains' IDEs. 91 | 92 | ##### Setup 93 | 94 | 1. Install the plugin called "CheckStyle-IDEA" in IntelliJ / Android-Studio. 95 | 2. Import the checkstyle configuration as a code style scheme in _Settings > Editor > Code Style > Java > Manage... > 96 | Import..._ by selecting "CheckStyle configuration" and then browsing to `config/checkstyle/checkstyle.xml`. 97 | 3. In _Settings > Other Settings > Checkstyle_, change the "Scan Scope" to "Only Java sources (including tests)". 98 | 4. Still in _Settings > Other Settings > Checkstyle_, add a new configuration file and browse to 99 | `config/checkstyle/checkstyle.xml`. 100 | 101 | ##### Usage 102 | 103 | In IntelliJ / Android-Studio's bottom task bar, you should see a "CheckStyle" tab. It will allow you to select your configuration 104 | with the "Rules" dropdown-list, and to run the analysis on your code. 105 | 106 | Note that by default, the IDE will not run checkstyle when building the project (whereas `$ ./gradlew build` always does it). 107 | 108 | #### Troubleshooting 109 | 110 | In IntelliJ / Android-Studio, the IDE might force the order of the imports in a way that is not following the checkstyle rules. For some reason, this is not set when importing `checkstyle.xml` as a code style scheme. However, it can be manually updated in _Settings > Code Style > Java > Import Layout_. 111 | 112 | ### Releasing 113 | 114 | Both [sdk](./sdk) and [mavsdk_server](./mavsdk_server) are released with Maven. Publishing can be done through a gradle task: 115 | 116 | ```gradle 117 | ./gradlew uploadArchives 118 | ``` 119 | 120 | This task requires a few secrets in `gradle.properties`: 121 | 122 | ``` 123 | signing.keyId= 124 | signing.password= 125 | signing.secretKeyRingFile= 126 | 127 | ossrhUsername= 128 | ossrhPassword= 129 | ``` 130 | 131 | ### Debugging without pushing to maven 132 | 133 | Sometimes you just need to rebuild `mavsdk_server` or even `libmavsdk_server.so` a couple of times to directly debug a problem. 134 | 135 | This can be achieved with the Gradle [composite builds](https://docs.gradle.org/current/userguide/composite_builds.html). This is already setup in the Android example project [here](https://github.com/mavlink/MAVSDK-Java/blob/main/examples/android-client/settings.gradle#L3-L11) (just uncomment the lines to build `sdk` and/or `mavsdk_server` from sources). 136 | 137 | 138 | Then you can just build the example and it will in turn build `mavsdk_server`. 139 | 140 | To replace the `libmavsdk_server.so`, you have to build it using dockcross and replace the file for the architecture that you're testing on. This is assuming you have MAVSDK-Java and MAVSDK side to side in the same directory: 141 | 142 | ``` 143 | cd ../MAVSDK 144 | docker run --rm dockcross/android-arm64 > ./dockcross-android-arm64 && chmod +x ./dockcross-android-arm64 145 | tools/generate_mavlink_headers.sh 146 | ./dockcross-android-arm64 cmake -DCMAKE_BUILD_TYPE=Debug -DBUILD_MAVSDK_SERVER=ON -DBUILD_SHARED_LIBS=OFF -Bbuild/android-arm64 -DMAVLINK_HEADERS="mavlink-headers/include" -H. -DCMAKE_INSTALL_PREFIX=build/android-arm64/install 147 | ./dockcross-android-arm64 cmake --build build/android-arm64 --target install && cp build/android-arm64/install/lib/libmavsdk_server.so ../mavsdk-android-test/mavsdk_server/src/main/prebuiltLibs/arm64-v8a/libmavsdk_server.so 148 | ``` 149 | 150 | To avoid keep getting the file overwritten, comment out the [function downloading and extracting the .so library artifacts](https://github.com/mavlink/MAVSDK-Java/blob/85a2f3d5f96d67c1919f52c67f5f6bdbc7607486/mavsdk_server/build.gradle#L24-L53). 151 | 152 | Now build, install, run the Android app again. 153 | -------------------------------------------------------------------------------- /examples/android-client/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | -------------------------------------------------------------------------------- /examples/android-client/app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /examples/android-client/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | configurations { 4 | checkstyleClasspath 5 | } 6 | 7 | android { 8 | namespace "io.mavsdk.androidclient" 9 | 10 | compileOptions { 11 | sourceCompatibility JavaVersion.VERSION_17 12 | targetCompatibility JavaVersion.VERSION_17 13 | } 14 | 15 | defaultConfig { 16 | applicationId "io.mavsdk.androidclient" 17 | minSdkVersion 21 18 | compileSdk 35 19 | targetSdkVersion 35 20 | versionCode 2 21 | versionName "1.1" 22 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 23 | resValue "string", "mapbox_access_token", project.property("mapbox_access_token") 24 | } 25 | buildTypes { 26 | release { 27 | minifyEnabled false 28 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 29 | } 30 | } 31 | } 32 | 33 | tasks.register('checkstyle', Checkstyle) { 34 | configFile = rootProject.file("config/checkstyle/checkstyle.xml") 35 | source 'src' 36 | include '**/*.java' 37 | exclude '**/gen/**' 38 | 39 | classpath = project.files() 40 | checkstyleClasspath = project.configurations.checkstyleClasspath 41 | 42 | maxWarnings 0 43 | 44 | reports { 45 | xml.destination new File("$project.buildDir/reports/checkstyle/checkstyle_report.xml") 46 | 47 | html.destination new File("$project.buildDir/reports/checkstyle/checkstyle_report.html") 48 | } 49 | } 50 | 51 | build.dependsOn 'checkstyle' 52 | 53 | dependencies { 54 | checkstyleClasspath 'com.puppycrawl.tools:checkstyle:8.17' 55 | 56 | implementation 'androidx.appcompat:appcompat:1.7.0' 57 | implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0' 58 | implementation 'com.github.tony19:logback-android:3.0.0' 59 | implementation 'com.google.android.material:material:1.12.0' 60 | implementation 'com.mapbox.mapboxsdk:mapbox-android-sdk:9.2.1' 61 | implementation 'com.mapbox.mapboxsdk:mapbox-android-plugin-annotation-v9:0.9.0' 62 | implementation 'io.mavsdk:mavsdk:2.1.0' 63 | implementation 'io.mavsdk:mavsdk-server:2.1.6' 64 | implementation 'org.slf4j:slf4j-api:2.0.17' 65 | 66 | testImplementation 'junit:junit:4.13.2' 67 | 68 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1' 69 | androidTestImplementation 'androidx.test:runner:1.6.2' 70 | } 71 | -------------------------------------------------------------------------------- /examples/android-client/app/lint.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /examples/android-client/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /examples/android-client/app/src/androidTest/java/io/mavsdk/androidclient/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package io.mavsdk.androidclient; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import android.content.Context; 6 | import androidx.test.InstrumentationRegistry; 7 | import androidx.test.runner.AndroidJUnit4; 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * @see Testing documentation 15 | */ 16 | @RunWith(AndroidJUnit4.class) 17 | public class ExampleInstrumentedTest { 18 | @Test 19 | public void useAppContext() { 20 | // Context of the app under test. 21 | Context appContext = InstrumentationRegistry.getTargetContext(); 22 | 23 | assertEquals("io.mavsdk.androidclient", appContext.getPackageName()); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /examples/android-client/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 15 | 16 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /examples/android-client/app/src/main/assets/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | %logger{0} 6 | 7 | 8 | 9 | [%thread] - %msg%n 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /examples/android-client/app/src/main/java/io/mavsdk/androidclient/MapsActivity.java: -------------------------------------------------------------------------------- 1 | package io.mavsdk.androidclient; 2 | 3 | import android.graphics.BitmapFactory; 4 | import android.graphics.Color; 5 | import android.os.Bundle; 6 | import android.view.Menu; 7 | import android.view.MenuItem; 8 | import android.widget.Button; 9 | 10 | import androidx.annotation.NonNull; 11 | import androidx.annotation.Nullable; 12 | import androidx.appcompat.app.AppCompatActivity; 13 | import androidx.appcompat.widget.Toolbar; 14 | import androidx.lifecycle.Observer; 15 | import androidx.lifecycle.ViewModelProviders; 16 | import com.google.android.material.floatingactionbutton.FloatingActionButton; 17 | import com.mapbox.mapboxsdk.Mapbox; 18 | import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; 19 | import com.mapbox.mapboxsdk.geometry.LatLng; 20 | import com.mapbox.mapboxsdk.maps.MapView; 21 | import com.mapbox.mapboxsdk.maps.MapboxMap; 22 | import com.mapbox.mapboxsdk.maps.OnMapReadyCallback; 23 | import com.mapbox.mapboxsdk.maps.Style; 24 | import com.mapbox.mapboxsdk.plugins.annotation.Circle; 25 | import com.mapbox.mapboxsdk.plugins.annotation.CircleManager; 26 | import com.mapbox.mapboxsdk.plugins.annotation.CircleOptions; 27 | import com.mapbox.mapboxsdk.plugins.annotation.Symbol; 28 | import com.mapbox.mapboxsdk.plugins.annotation.SymbolManager; 29 | import com.mapbox.mapboxsdk.plugins.annotation.SymbolOptions; 30 | import com.mapbox.mapboxsdk.utils.ColorUtils; 31 | 32 | import io.mavsdk.MavsdkEventQueue; 33 | import io.mavsdk.System; 34 | import io.mavsdk.mavsdkserver.MavsdkServer; 35 | import io.reactivex.disposables.Disposable; 36 | import java.util.ArrayList; 37 | import java.util.List; 38 | import org.slf4j.Logger; 39 | import org.slf4j.LoggerFactory; 40 | 41 | /** 42 | * Main Activity to display map and create missions. 43 | * 1. Take off 44 | * 2. Long click on map to add a waypoint 45 | * 3. Hit play to start mission. 46 | */ 47 | public class MapsActivity extends AppCompatActivity implements OnMapReadyCallback { 48 | private static final Logger logger = LoggerFactory.getLogger(MapsActivity.class); 49 | 50 | public static final String BACKEND_IP_ADDRESS = "127.0.0.1"; 51 | 52 | private CircleManager circleManager; 53 | private SymbolManager symbolManager; 54 | 55 | private MapView mapView; 56 | private MapboxMap map; 57 | 58 | private Button buttonRunDestroyMavsdkServer; 59 | 60 | private MapsViewModel viewModel; 61 | private Symbol currentPositionMarker; 62 | 63 | private MavsdkServer mavsdkServer = new MavsdkServer(); 64 | private System drone; 65 | private boolean isMavsdkServerRunning = false; 66 | private final List waypoints = new ArrayList<>(); 67 | private final List disposables = new ArrayList<>(); 68 | 69 | private Observer currentPositionObserver = this::updateVehiclePosition; 70 | private Observer> currentMissionPlanObserver = this::updateMarkers; 71 | 72 | @Override 73 | public void onCreate(Bundle savedInstanceState) { 74 | super.onCreate(savedInstanceState); 75 | Mapbox.getInstance(this, getString(R.string.mapbox_access_token)); 76 | setContentView(R.layout.activity_maps); 77 | Toolbar toolbar = findViewById(R.id.toolbar); 78 | setSupportActionBar(toolbar); 79 | mapView = findViewById(R.id.mapView); 80 | mapView.onCreate(savedInstanceState); 81 | mapView.getMapAsync(this); 82 | 83 | buttonRunDestroyMavsdkServer = findViewById(R.id.buttonRunDestroyMavsdkServer); 84 | buttonRunDestroyMavsdkServer.setOnClickListener(v -> runDestroyMavsdkServer()); 85 | 86 | viewModel = ViewModelProviders.of(this).get(MapsViewModel.class); 87 | 88 | FloatingActionButton floatingActionButton = findViewById(R.id.fab); 89 | floatingActionButton.setOnClickListener(v -> viewModel.startMission(drone)); 90 | } 91 | 92 | @Override 93 | public void onStart() { 94 | super.onStart(); 95 | mapView.onStart(); 96 | } 97 | 98 | @Override 99 | public void onResume() { 100 | super.onResume(); 101 | mapView.onResume(); 102 | viewModel.currentPositionLiveData.observe(this, currentPositionObserver); 103 | viewModel.currentMissionPlanLiveData.observe(this, currentMissionPlanObserver); 104 | } 105 | 106 | @Override 107 | public void onPause() { 108 | super.onPause(); 109 | mapView.onPause(); 110 | viewModel.currentPositionLiveData.removeObserver(currentPositionObserver); 111 | viewModel.currentMissionPlanLiveData.removeObserver(currentMissionPlanObserver); 112 | } 113 | 114 | @Override 115 | public void onStop() { 116 | super.onStop(); 117 | mapView.onStop(); 118 | } 119 | 120 | @Override 121 | public void onLowMemory() { 122 | super.onLowMemory(); 123 | mapView.onLowMemory(); 124 | } 125 | 126 | @Override 127 | public void onDestroy() { 128 | super.onDestroy(); 129 | mapView.onDestroy(); 130 | } 131 | 132 | @Override 133 | public void onSaveInstanceState(Bundle outState) { 134 | super.onSaveInstanceState(outState); 135 | mapView.onSaveInstanceState(outState); 136 | } 137 | 138 | @Override 139 | public boolean onCreateOptionsMenu(Menu menu) { 140 | getMenuInflater().inflate(R.menu.menu_maps, menu); 141 | return true; 142 | } 143 | 144 | @Override 145 | public boolean onOptionsItemSelected(MenuItem item) { 146 | // Handle item selection 147 | int itemId = item.getItemId(); 148 | if(itemId == R.id.disarm) { 149 | drone.getAction().kill().subscribe(); 150 | } else if (itemId == R.id.land) { 151 | drone.getAction().land().subscribe(); 152 | } else if (itemId == R.id.return_home) { 153 | drone.getAction().returnToLaunch().subscribe(); 154 | } else if (itemId == R.id.takeoff) { 155 | drone.getAction().arm().andThen(drone.getAction().takeoff()).subscribe(); 156 | } else { 157 | return super.onOptionsItemSelected(item); 158 | } 159 | return true; 160 | } 161 | 162 | /** 163 | * Update [currentPositionMarker] position with a new [position]. 164 | * 165 | * @param newLatLng new position of the vehicle 166 | */ 167 | private void updateVehiclePosition(@Nullable LatLng newLatLng) { 168 | if (newLatLng == null || map == null || symbolManager == null) { 169 | // Not ready 170 | return; 171 | } 172 | 173 | // Add a vehicle marker and move the camera 174 | if (currentPositionMarker == null) { 175 | SymbolOptions symbolOptions = new SymbolOptions(); 176 | symbolOptions.withLatLng(newLatLng); 177 | symbolOptions.withIconImage("marker-icon-id"); 178 | currentPositionMarker = symbolManager.create(symbolOptions); 179 | 180 | map.moveCamera(CameraUpdateFactory.tiltTo(0)); 181 | map.moveCamera(CameraUpdateFactory.newLatLngZoom(newLatLng, 14.0f)); 182 | } else { 183 | currentPositionMarker.setLatLng(newLatLng); 184 | symbolManager.update(currentPositionMarker); 185 | } 186 | } 187 | 188 | /** 189 | * Update the [map] with the current mission plan waypoints. 190 | * 191 | * @param latLngs current mission waypoints 192 | */ 193 | private void updateMarkers(@NonNull List latLngs) { 194 | if (circleManager != null) { 195 | circleManager.delete(waypoints); 196 | waypoints.clear(); 197 | } 198 | 199 | for (LatLng latLng : latLngs) { 200 | CircleOptions circleOptions = new CircleOptions() 201 | .withLatLng(latLng) 202 | .withCircleColor(ColorUtils.colorToRgbaString(Color.BLUE)) 203 | .withCircleStrokeColor(ColorUtils.colorToRgbaString(Color.BLACK)) 204 | .withCircleStrokeWidth(1.0f) 205 | .withCircleRadius(12f) 206 | .withDraggable(false); 207 | 208 | circleManager.create(circleOptions); 209 | } 210 | } 211 | 212 | /** 213 | * Manipulates the map once available. 214 | * This callback is triggered when the map is ready to be used. 215 | * This is where we can add markers or lines, add listeners or move the camera. 216 | */ 217 | @Override 218 | public void onMapReady(@NonNull MapboxMap mapboxMap) { 219 | mapboxMap.getUiSettings().setRotateGesturesEnabled(false); 220 | mapboxMap.getUiSettings().setTiltGesturesEnabled(false); 221 | mapboxMap.addOnMapLongClickListener(point -> { 222 | viewModel.addWaypoint(point); 223 | return true; 224 | }); 225 | 226 | mapboxMap.setStyle(Style.LIGHT, style -> { 227 | // Add the marker image to map 228 | style.addImage("marker-icon-id", 229 | BitmapFactory.decodeResource( 230 | MapsActivity.this.getResources(), com.mapbox.mapboxsdk.plugins.annotation.R.drawable.mapbox_marker_icon_default)); 231 | 232 | symbolManager = new SymbolManager(this.mapView, this.map, style); 233 | symbolManager.setIconAllowOverlap(true); 234 | circleManager = new CircleManager(this.mapView, this.map, style); 235 | }); 236 | 237 | map = mapboxMap; 238 | } 239 | 240 | private void runDestroyMavsdkServer() { 241 | if (!isMavsdkServerRunning) { 242 | runMavsdkServer(); 243 | } else { 244 | destroyMavsdkServer(); 245 | } 246 | } 247 | 248 | private void runMavsdkServer() { 249 | MavsdkEventQueue.executor().execute(() -> { 250 | int mavsdkServerPort = mavsdkServer.run(); 251 | drone = new System(BACKEND_IP_ADDRESS, mavsdkServerPort); 252 | 253 | disposables.add(drone.getTelemetry().getFlightMode().distinctUntilChanged() 254 | .subscribe(flightMode -> logger.debug("flight mode: " + flightMode))); 255 | disposables.add(drone.getTelemetry().getArmed().distinctUntilChanged() 256 | .subscribe(armed -> logger.debug("armed: " + armed))); 257 | disposables.add(drone.getTelemetry().getPosition().subscribe(position -> { 258 | LatLng latLng = new LatLng(position.getLatitudeDeg(), position.getLongitudeDeg()); 259 | viewModel.currentPositionLiveData.postValue(latLng); 260 | })); 261 | 262 | isMavsdkServerRunning = true; 263 | runOnUiThread(() -> buttonRunDestroyMavsdkServer.setText(R.string.destroy_mavsdk_server)); 264 | }); 265 | } 266 | 267 | private void destroyMavsdkServer() { 268 | MavsdkEventQueue.executor().execute(() -> { 269 | for (Disposable disposable : disposables) { 270 | disposable.dispose(); 271 | } 272 | disposables.clear(); 273 | drone.dispose(); 274 | drone = null; 275 | mavsdkServer.stop(); 276 | mavsdkServer.destroy(); 277 | 278 | isMavsdkServerRunning = false; 279 | runOnUiThread(() -> { 280 | symbolManager.delete(currentPositionMarker); 281 | currentPositionMarker = null; 282 | buttonRunDestroyMavsdkServer.setText(R.string.run_mavsdk_server); 283 | }); 284 | }); 285 | } 286 | } -------------------------------------------------------------------------------- /examples/android-client/app/src/main/java/io/mavsdk/androidclient/MapsViewModel.java: -------------------------------------------------------------------------------- 1 | package io.mavsdk.androidclient; 2 | 3 | import android.annotation.SuppressLint; 4 | import androidx.lifecycle.MutableLiveData; 5 | import androidx.lifecycle.ViewModel; 6 | import com.mapbox.mapboxsdk.geometry.LatLng; 7 | import io.mavsdk.System; 8 | import io.mavsdk.mission.Mission; 9 | import io.mavsdk.mission.Mission.MissionItem.VehicleAction; 10 | import io.mavsdk.mission.Mission.MissionPlan; 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | import org.slf4j.Logger; 14 | import org.slf4j.LoggerFactory; 15 | 16 | /** 17 | * ViewModel to hold objects that should be persisted. 18 | */ 19 | public class MapsViewModel extends ViewModel { 20 | private static final Logger logger = LoggerFactory.getLogger(MapsViewModel.class); 21 | 22 | private static final float MISSION_HEIGHT = 5.0f; 23 | private static final float MISSION_SPEED = 1.0f; 24 | 25 | final MutableLiveData currentPositionLiveData = new MutableLiveData<>(); 26 | final MutableLiveData> currentMissionPlanLiveData = new MutableLiveData<>(); 27 | 28 | public MapsViewModel() { 29 | currentMissionPlanLiveData.postValue(new ArrayList<>()); 30 | } 31 | 32 | @Override 33 | protected void onCleared() { 34 | super.onCleared(); 35 | } 36 | 37 | /** 38 | * Executes the current mission. 39 | */ 40 | @SuppressLint("CheckResult") 41 | void startMission(System drone) { 42 | List latLngs = currentMissionPlanLiveData.getValue(); 43 | if (latLngs != null) { 44 | List missionItems = new ArrayList<>(); 45 | for (LatLng latLng : latLngs) { 46 | Mission.MissionItem missionItem = new Mission.MissionItem( 47 | latLng.getLatitude(), 48 | latLng.getLongitude(), 49 | MISSION_HEIGHT, 50 | MISSION_SPEED, 51 | true, 52 | Float.NaN, 53 | Float.NaN, 54 | Mission.MissionItem.CameraAction.NONE, 55 | Float.NaN, 56 | 1.0, 57 | Float.NaN, 58 | Float.NaN, 59 | Float.NaN, 60 | VehicleAction.NONE); 61 | missionItems.add(missionItem); 62 | } 63 | 64 | MissionPlan missionPlan = new MissionPlan(missionItems); 65 | 66 | logger.debug("Uploading and starting mission..."); 67 | drone.getMission() 68 | .setReturnToLaunchAfterMission(true) 69 | .andThen(drone.getMission().uploadMission(missionPlan) 70 | .doOnComplete(() -> logger.debug("Upload succeeded")) 71 | .doOnError(throwable -> logger.error("Failed to upload the mission"))) 72 | .andThen(drone.getAction().arm() 73 | .onErrorComplete()) 74 | .andThen(drone.getMission().startMission() 75 | .doOnComplete(() -> logger.debug("Mission started")) 76 | .doOnError(throwable -> logger.error("Failed to start the mission"))) 77 | .subscribe(() -> { }, throwable -> { }); 78 | } 79 | } 80 | 81 | /** 82 | * Adds a waypoint to the current mission. 83 | * 84 | * @param latLng waypoint to add 85 | */ 86 | void addWaypoint(LatLng latLng) { 87 | List currentMissionItems = currentMissionPlanLiveData.getValue(); 88 | currentMissionItems.add(latLng); 89 | currentMissionPlanLiveData.postValue(currentMissionItems); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /examples/android-client/app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /examples/android-client/app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 11 | 16 | 21 | 26 | 31 | 36 | 41 | 46 | 51 | 56 | 61 | 66 | 71 | 76 | 81 | 86 | 91 | 96 | 101 | 106 | 111 | 116 | 121 | 126 | 131 | 136 | 141 | 146 | 151 | 156 | 161 | 166 | 171 | 172 | -------------------------------------------------------------------------------- /examples/android-client/app/src/main/res/drawable/ic_play_arrow_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /examples/android-client/app/src/main/res/layout/activity_maps.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 14 | 15 | 24 | 25 | 33 | 34 | 42 | 43 | 49 | 50 | -------------------------------------------------------------------------------- /examples/android-client/app/src/main/res/menu/menu_maps.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 10 | 12 | 14 | -------------------------------------------------------------------------------- /examples/android-client/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /examples/android-client/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /examples/android-client/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mavlink/MAVSDK-Java/c291d8cd9d75ec485d77f28e8fb495e1dcc02d49/examples/android-client/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /examples/android-client/app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mavlink/MAVSDK-Java/c291d8cd9d75ec485d77f28e8fb495e1dcc02d49/examples/android-client/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /examples/android-client/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mavlink/MAVSDK-Java/c291d8cd9d75ec485d77f28e8fb495e1dcc02d49/examples/android-client/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /examples/android-client/app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mavlink/MAVSDK-Java/c291d8cd9d75ec485d77f28e8fb495e1dcc02d49/examples/android-client/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /examples/android-client/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mavlink/MAVSDK-Java/c291d8cd9d75ec485d77f28e8fb495e1dcc02d49/examples/android-client/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /examples/android-client/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mavlink/MAVSDK-Java/c291d8cd9d75ec485d77f28e8fb495e1dcc02d49/examples/android-client/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /examples/android-client/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mavlink/MAVSDK-Java/c291d8cd9d75ec485d77f28e8fb495e1dcc02d49/examples/android-client/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /examples/android-client/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mavlink/MAVSDK-Java/c291d8cd9d75ec485d77f28e8fb495e1dcc02d49/examples/android-client/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /examples/android-client/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mavlink/MAVSDK-Java/c291d8cd9d75ec485d77f28e8fb495e1dcc02d49/examples/android-client/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /examples/android-client/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mavlink/MAVSDK-Java/c291d8cd9d75ec485d77f28e8fb495e1dcc02d49/examples/android-client/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /examples/android-client/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | MAVSDK Android Client 3 | 4 | Kill 5 | Return 6 | Land 7 | Takeoff 8 | Run Mavsdk Server 9 | Destroy Mavsdk Server 10 | 11 | -------------------------------------------------------------------------------- /examples/android-client/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |