first commit
|
@ -0,0 +1 @@
|
|||
/build
|
|
@ -0,0 +1,172 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 DJI
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlin-android-extensions'
|
||||
|
||||
android {
|
||||
lintOptions {
|
||||
abortOnError = false
|
||||
}
|
||||
compileSdkVersion 31
|
||||
buildToolsVersion "30.0.3"
|
||||
useLibrary 'org.apache.http.legacy'
|
||||
defaultConfig {
|
||||
applicationId "com.dji.ux.beta.sample"
|
||||
minSdkVersion 23
|
||||
targetSdkVersion 31
|
||||
multiDexEnabled true
|
||||
vectorDrawables.useSupportLibrary = true
|
||||
ndk {
|
||||
// On x86 devices that run Android API 23 or above, if the application is targeted with API 23 or
|
||||
// above, FFmpeg lib might lead to runtime crashes or warnings.
|
||||
abiFilters 'armeabi-v7a', 'arm64-v8a'
|
||||
}
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled true
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
/*
|
||||
dexOptions {
|
||||
javaMaxHeapSize "3g"
|
||||
}
|
||||
*/
|
||||
lintOptions{
|
||||
abortOnError false
|
||||
}
|
||||
packagingOptions{
|
||||
doNotStrip "*/*/libdjivideo.so"
|
||||
doNotStrip "*/*/libSDKRelativeJNI.so"
|
||||
doNotStrip "*/*/libFlyForbid.so"
|
||||
doNotStrip "*/*/libduml_vision_bokeh.so"
|
||||
doNotStrip "*/*/libyuv2.so"
|
||||
doNotStrip "*/*/libGroudStation.so"
|
||||
doNotStrip "*/*/libFRCorkscrew.so"
|
||||
doNotStrip "*/*/libUpgradeVerify.so"
|
||||
doNotStrip "*/*/libFR.so"
|
||||
doNotStrip "*/*/libDJIFlySafeCore.so"
|
||||
doNotStrip "*/*/libdjifs_jni.so"
|
||||
doNotStrip "*/*/libsfjni.so"
|
||||
doNotStrip "*/*/libDJICommonJNI.so"
|
||||
doNotStrip "*/*/libDJICSDKCommon.so"
|
||||
doNotStrip "*/*/libDJIUpgradeCore.so"
|
||||
doNotStrip "*/*/libDJIUpgradeJNI.so"
|
||||
doNotStrip "*/*/libDJIWaypointV2Core.so"
|
||||
doNotStrip "*/*/libDJIMOP.so"
|
||||
doNotStrip "*/*/libDJISDKLOGJNI.so"
|
||||
|
||||
pickFirst 'lib/*/libstlport_shared.so'
|
||||
pickFirst 'lib/*/libRoadLineRebuildAPI.so'
|
||||
pickFirst 'lib/*/libGNaviUtils.so'
|
||||
pickFirst 'lib/*/libGNaviMapex.so'
|
||||
pickFirst 'lib/*/libGNaviData.so'
|
||||
pickFirst 'lib/*/libGNaviMap.so'
|
||||
pickFirst 'lib/*/libGNaviSearch.so'
|
||||
exclude 'META-INF/proguard/okhttp3.pro'
|
||||
exclude 'META-INF/rxjava.properties'
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
implementation ('com.dji:dji-sdk:4.16.1', {
|
||||
/**
|
||||
* Comment the "library-anti-distortion" if your app needs Anti Distortion for Mavic 2 Pro
|
||||
* com.dji:dji-sdk:4.16
|
||||
* and Mavic 2 Zoom.
|
||||
* Comment the "fly-safe-database" if you don't need a smaller apk for release. When
|
||||
* uncommented, we will download it when DJISDKManager.getInstance().registerApp
|
||||
* is called, and it won't register success without fly-safe-database.
|
||||
* Both will greatly reduce the size of the APK.
|
||||
*/
|
||||
exclude module: 'library-anti-distortion'
|
||||
exclude module: 'fly-safe-database'
|
||||
|
||||
/**
|
||||
* Uncomment the following line to exclude Amap from the app.
|
||||
* Note that Google Play Store does not allow APKs that include this library.
|
||||
*/
|
||||
// exclude group: 'com.amap.api'
|
||||
})
|
||||
implementation 'androidx.test.ext:junit:1.1.3'
|
||||
compileOnly ('com.dji:dji-sdk-provided:4.16.1')
|
||||
|
||||
//UXSDK MOBILE HERE
|
||||
implementation 'com.github.dji-sdk:Mobile-UXSDK-Beta-Android:v0.5.1'
|
||||
|
||||
// UXSDK dependencies
|
||||
implementation 'androidx.annotation:annotation:1.3.0'
|
||||
implementation 'androidx.appcompat:appcompat:1.4.1'
|
||||
implementation 'androidx.core:core:1.7.0'
|
||||
implementation "androidx.core:core-ktx:1.7.0"
|
||||
|
||||
// UXSDK map dependencies: Only include dependencies for map providers that are used in your app
|
||||
// Amap: Do not include if publishing to Google Play Store
|
||||
//implementation 'com.amap.api:3dmap:6.9.2'
|
||||
//implementation 'com.amap.api:search:6.9.2'
|
||||
//implementation 'com.amap.api:location:4.7.0'
|
||||
// HERE maps
|
||||
//implementation files('libs/HERE-sdk-3.15.0.aar')
|
||||
// Mapbox
|
||||
implementation 'com.mapbox.mapboxsdk:mapbox-android-sdk:9.2.0'
|
||||
// Google maps
|
||||
implementation 'com.google.android.gms:play-services-base:18.0.1'
|
||||
implementation 'com.google.android.gms:play-services-location:19.0.1'
|
||||
implementation 'com.google.android.gms:play-services-maps:18.0.2'
|
||||
implementation 'com.google.android.gms:play-services-places:17.0.0'
|
||||
implementation 'com.google.maps.android:android-maps-utils:1.2.1'
|
||||
|
||||
// SDK dependencies
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
|
||||
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
|
||||
implementation 'androidx.multidex:multidex:2.0.1'
|
||||
implementation 'androidx.recyclerview:recyclerview:1.2.1'
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||
|
||||
// required to avoid crash on Android 12 API 31
|
||||
implementation 'androidx.work:work-runtime-ktx:2.7.1'
|
||||
|
||||
// Sample app dependencies
|
||||
implementation 'com.jakewharton:butterknife:10.0.0'
|
||||
annotationProcessor 'com.jakewharton:butterknife-compiler:10.0.0'
|
||||
|
||||
implementation "com.ncorti:slidetoact:0.9.0"
|
||||
implementation "com.github.emrekose26:RecordButton:1.2.4"
|
||||
implementation 'com.google.android.material:material:1.5.0'
|
||||
|
||||
api 'io.reactivex.rxjava3:rxandroid:3.0.0'
|
||||
api 'io.reactivex.rxjava3:rxjava:3.0.0'
|
||||
|
||||
|
||||
}
|
||||
repositories {
|
||||
mavenLocal()
|
||||
}
|
|
@ -0,0 +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.
|
||||
#-renamesourcefileattribute SourceFile
|
|
@ -0,0 +1,26 @@
|
|||
package com.dji.ux.beta.sample;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Instrumented test, which will execute on an Android device.
|
||||
*
|
||||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class ExampleInstrumentedTest {
|
||||
@Test
|
||||
public void useAppContext() {
|
||||
// Context of the app under test.
|
||||
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
|
||||
assertEquals("com.dji.ux.beta.sample", appContext.getPackageName());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.dji.ux.beta.sample">
|
||||
|
||||
<!-- DJI SDK needs these permissions -->
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
|
||||
<!-- Needed only if your app looks for Bluetooth devices.
|
||||
If your app doesn't use Bluetooth scan results to derive physical
|
||||
location information, you can strongly assert that your app
|
||||
doesn't derive physical location. -->
|
||||
<!-- <uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
|
||||
Needed only if your app makes the device discoverable to Bluetooth
|
||||
devices.
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
|
||||
Needed only if your app communicates with already-paired Bluetooth
|
||||
devices.
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
|
||||
-->
|
||||
<!--bibo01 : hardware option-->
|
||||
<uses-feature android:name="android.hardware.bluetooth" android:required="false"/>
|
||||
<uses-feature android:name="android.hardware.bluetooth_le" android:required="false"/>
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
|
||||
<uses-feature
|
||||
android:name="android.hardware.usb.host"
|
||||
android:required="false" />
|
||||
<uses-feature
|
||||
android:name="android.hardware.usb.accessory"
|
||||
android:required="true" />
|
||||
|
||||
<application
|
||||
android:name="com.dji.ux.beta.sample.SampleApplication"
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
tools:replace="android:label"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme"
|
||||
android:extractNativeLibs="true"
|
||||
tools:ignore="AllowBackup,GoogleAppIndexingWarning">
|
||||
|
||||
<!-- DJI SDK -->
|
||||
<uses-library android:name="org.apache.http.legacy" android:required="false"/>
|
||||
<uses-library android:name="com.android.future.usb.accessory" />
|
||||
|
||||
|
||||
<meta-data
|
||||
android:name="com.dji.sdk.API_KEY"
|
||||
android:value="9c5530eda0ad04a7e4693dcc" />
|
||||
|
||||
<meta-data
|
||||
android:name="com.google.android.geo.API_KEY"
|
||||
android:value="@string/maps_api_key"/>
|
||||
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:launchMode="singleTop"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".DJIConnectionControlActivity"
|
||||
android:theme="@android:style/Theme.Translucent"
|
||||
android:exported="false">
|
||||
<intent-filter>
|
||||
<action android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" />
|
||||
</intent-filter>
|
||||
|
||||
<meta-data
|
||||
android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED"
|
||||
android:resource="@xml/accessory_filter" />
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".cameraview.CameraActivity"
|
||||
android:screenOrientation="sensorLandscape"
|
||||
android:windowSoftInputMode="adjustNothing"
|
||||
android:exported="true"
|
||||
android:theme="@style/AppTheme"/>
|
||||
|
||||
<service
|
||||
android:name=".connection.TcpClientService"
|
||||
android:enabled="true"
|
||||
android:exported="false" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 DJI
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.dji.ux.beta.sample;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.hardware.usb.UsbManager;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
|
||||
/**
|
||||
* Controls the connection to the USB accessory. This activity listens for the USB attached action
|
||||
* and sends a broadcast with an internal code which is listened to by the
|
||||
* {@link OnDJIUSBAttachedReceiver}.
|
||||
*/
|
||||
public class DJIConnectionControlActivity extends Activity {
|
||||
|
||||
public static final String ACCESSORY_ATTACHED = "dji.ux.beta.sample.ACCESSORY_ATTACHED";
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(new View(this));
|
||||
|
||||
Intent usbIntent = getIntent();
|
||||
if (usbIntent != null) {
|
||||
String action = usbIntent.getAction();
|
||||
if (UsbManager.ACTION_USB_ACCESSORY_ATTACHED.equals(action)) {
|
||||
Intent attachedIntent = new Intent();
|
||||
attachedIntent.setAction(ACCESSORY_ATTACHED);
|
||||
sendBroadcast(attachedIntent);
|
||||
}
|
||||
}
|
||||
|
||||
finish();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,358 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 DJI
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.dji.ux.beta.sample;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||
|
||||
import com.dji.ux.beta.sample.cameraview.CameraActivity;
|
||||
import com.dji.ux.beta.sample.connection.TcpClientService;
|
||||
import com.dji.ux.beta.sample.utils.ToastUtils;
|
||||
import com.ncorti.slidetoact.SlideToActView;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import dji.common.error.DJIError;
|
||||
import dji.common.error.DJISDKError;
|
||||
import dji.sdk.base.BaseComponent;
|
||||
import dji.sdk.base.BaseProduct;
|
||||
import dji.sdk.products.Aircraft;
|
||||
import dji.sdk.sdkmanager.DJISDKInitEvent;
|
||||
import dji.sdk.sdkmanager.DJISDKManager;
|
||||
|
||||
/**
|
||||
* Handles the connection to the product and provides links to the different test activities. Also
|
||||
* shows the current connection state and displays logs for the different steps of the SDK
|
||||
* registration process.
|
||||
*/
|
||||
//@RequiresApi(api = Build.VERSION_CODES.S)
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
// SharedPreferences sharedPreferences;
|
||||
|
||||
//region Constants
|
||||
private static final int REQUEST_PERMISSION_CODE = 12345;
|
||||
private static final String[] REQUIRED_PERMISSION_LIST = new String[]{
|
||||
Manifest.permission.VIBRATE, // Gimbal rotation
|
||||
Manifest.permission.INTERNET, // API requests
|
||||
Manifest.permission.ACCESS_WIFI_STATE, // WIFI connected products
|
||||
Manifest.permission.ACCESS_COARSE_LOCATION, // Maps
|
||||
Manifest.permission.ACCESS_NETWORK_STATE, // WIFI connected products
|
||||
Manifest.permission.ACCESS_FINE_LOCATION, // Maps
|
||||
Manifest.permission.CHANGE_WIFI_STATE, // Changing between WIFI and USB connection
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE, // Log files
|
||||
Manifest.permission.BLUETOOTH, // Bluetooth connected products
|
||||
Manifest.permission.BLUETOOTH_ADMIN, // Bluetooth connected products
|
||||
Manifest.permission.READ_EXTERNAL_STORAGE, // Log files
|
||||
Manifest.permission.READ_PHONE_STATE, // Device UUID accessed upon registration
|
||||
Manifest.permission.RECORD_AUDIO, // Speaker accessory
|
||||
//Manifest.permission.BLUETOOTH_SCAN,
|
||||
//Manifest.permission.BLUETOOTH_CONNECT,
|
||||
Manifest.permission.ACCESS_FINE_LOCATION
|
||||
};
|
||||
private static final String TIME_FORMAT = "MMM dd, yyyy 'at' h:mm:ss a";
|
||||
private static final String TAG = "MainActivity";
|
||||
private static final String ACTION = "FROM_MAIN";
|
||||
|
||||
//TODO: eliminare broadcast receiver e prendere indirizzo ip server dallo shared preference
|
||||
//TODO: inviare al server stato iniziale connessione del drone
|
||||
//TODO: applicazione crasha se Bluetooth acceso (Android 12 bug)
|
||||
//TODO: eliminare il toast del log
|
||||
|
||||
//endregion
|
||||
private static boolean isAppStarted = false;
|
||||
@BindView(R.id.text_view_version)
|
||||
protected TextView versionTextView;
|
||||
@BindView(R.id.text_view_registered)
|
||||
protected TextView registeredTextView;
|
||||
@BindView(R.id.text_view_product_name)
|
||||
protected TextView productNameTextView;
|
||||
@BindView(R.id.text_view_server_ip)
|
||||
protected TextView ipServerTextView;
|
||||
@BindView(R.id.camera_button)
|
||||
protected SlideToActView cameraButton;
|
||||
//region Fields
|
||||
private AtomicBoolean isRegistrationInProgress = new AtomicBoolean(false);
|
||||
private int lastProgress = -1;
|
||||
private DJISDKManager.SDKManagerCallback registrationCallback = new DJISDKManager.SDKManagerCallback() {
|
||||
|
||||
@Override
|
||||
public void onRegister(DJIError error) {
|
||||
isRegistrationInProgress.set(false);
|
||||
if (error == DJISDKError.REGISTRATION_SUCCESS) {
|
||||
DJISDKManager.getInstance().startConnectionToProduct();
|
||||
runOnUiThread(() -> {
|
||||
registeredTextView.setText(R.string.registered);
|
||||
});
|
||||
Log.i(TAG, "Registration success");
|
||||
} else {
|
||||
showToast( "Register sdk fails, check network is available");
|
||||
Log.i(TAG, "Registration failed");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProductDisconnect() {
|
||||
runOnUiThread(() -> {
|
||||
//addLog("Disconnected from product");
|
||||
productNameTextView.setText(R.string.no_product);
|
||||
fromActivityToService(ACTION, "drone_connection", getString(R.string.no_product));
|
||||
});
|
||||
Log.i(TAG, "Drone disconnected");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProductConnect(BaseProduct product) {
|
||||
if (product != null) {
|
||||
runOnUiThread(() -> {
|
||||
//addLog("Connected to product");
|
||||
if (product.getModel() != null) {
|
||||
productNameTextView.setText(getString(R.string.product_name, product.getModel().getDisplayName()));
|
||||
fromActivityToService(ACTION, "drone_connection", getString(R.string.product_name, product.getModel().getDisplayName()));
|
||||
} else if (product instanceof Aircraft) {
|
||||
Aircraft aircraft = (Aircraft) product;
|
||||
if (aircraft.getRemoteController() != null) {
|
||||
productNameTextView.setText(getString(R.string.remote_controller));
|
||||
fromActivityToService(ACTION, "drone_connection", getString((R.string.remote_controller)));
|
||||
}
|
||||
}
|
||||
});
|
||||
Log.i(TAG, String.format("onProductConnect newProduct:%s", product));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProductChanged(BaseProduct product) {
|
||||
if (product != null) {
|
||||
runOnUiThread(() -> {
|
||||
//addLog("Product changed");
|
||||
if (product.getModel() != null) {
|
||||
productNameTextView.setText(getString(R.string.product_name, product.getModel().getDisplayName()));
|
||||
} else if (product instanceof Aircraft) {
|
||||
Aircraft aircraft = (Aircraft) product;
|
||||
if (aircraft.getRemoteController() != null) {
|
||||
productNameTextView.setText(getString(R.string.remote_controller));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComponentChange(BaseProduct.ComponentKey key,
|
||||
BaseComponent oldComponent,
|
||||
BaseComponent newComponent) {
|
||||
Log.i(TAG, key.toString() + " changed");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInitProcess(DJISDKInitEvent djisdkInitEvent, int totalProcess) {
|
||||
Log.i(TAG, djisdkInitEvent.getInitializationState().toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDatabaseDownloadProgress(long current, long total) {
|
||||
runOnUiThread(() -> {
|
||||
int progress = (int) (100 * current / total);
|
||||
if (progress == lastProgress) {
|
||||
return;
|
||||
}
|
||||
lastProgress = progress;
|
||||
//addLog("Fly safe database download progress: " + progress);
|
||||
});
|
||||
}
|
||||
};
|
||||
private List<String> missingPermission = new ArrayList<>();
|
||||
//endregion
|
||||
|
||||
/**
|
||||
* Whether the app has started.
|
||||
*
|
||||
* @return `true` if the app has been started.
|
||||
*/
|
||||
public static boolean isStarted() {
|
||||
return isAppStarted;
|
||||
}
|
||||
|
||||
//region Lifecycle
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
ButterKnife.bind(this);
|
||||
isAppStarted = true;
|
||||
checkAndRequestPermissions();
|
||||
versionTextView.setText(getResources().getString(R.string.sdk_version,
|
||||
DJISDKManager.getInstance().getSDKVersion()));
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
startForegroundService(new Intent(this, TcpClientService.class));
|
||||
} else {
|
||||
startService(new Intent(this, TcpClientService.class));
|
||||
}
|
||||
|
||||
cameraButton.setOnSlideCompleteListener(new SlideToActView.OnSlideCompleteListener() {
|
||||
@Override
|
||||
public void onSlideComplete(@NotNull SlideToActView slideToActView) {
|
||||
Intent intent = new Intent(MainActivity.this, CameraActivity.class);
|
||||
startActivity(intent);
|
||||
cameraButton.resetSlider();
|
||||
}
|
||||
});
|
||||
}
|
||||
//endregion
|
||||
|
||||
private final BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
/*
|
||||
if("FROM_SERVER".equals(intent.getAction())){
|
||||
String message = intent.getStringExtra("speed_wp");
|
||||
addLog(message);
|
||||
}*/
|
||||
if ("IP".equals(intent.getAction())){
|
||||
String ip = intent.getStringExtra("server_ip");
|
||||
ipServerTextView.setText("Status: " + ip);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private void fromActivityToService(String action, String key, String msg){
|
||||
Intent intent = new Intent(action);
|
||||
intent.putExtra(key, msg);
|
||||
LocalBroadcastManager.getInstance(MainActivity.this).sendBroadcast(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
IntentFilter filter = new IntentFilter("IP");
|
||||
filter.addAction("FROM_SERVER");
|
||||
LocalBroadcastManager.getInstance(MainActivity.this).registerReceiver(broadcastReceiver, filter);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
// Prevent memory leak by releasing DJISDKManager's references to this activity
|
||||
if (DJISDKManager.getInstance() != null) {
|
||||
DJISDKManager.getInstance().destroy();
|
||||
}
|
||||
isAppStarted = false;
|
||||
super.onDestroy();
|
||||
stopService(new Intent(this, TcpClientService.class));
|
||||
LocalBroadcastManager.getInstance(MainActivity.this).unregisterReceiver(broadcastReceiver);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if there is any missing permissions, and
|
||||
* requests runtime permission if needed.
|
||||
*/
|
||||
private void checkAndRequestPermissions() {
|
||||
// Check for permissions
|
||||
for (String eachPermission : REQUIRED_PERMISSION_LIST) {
|
||||
if (ContextCompat.checkSelfPermission(this, eachPermission) != PackageManager.PERMISSION_GRANTED) {
|
||||
missingPermission.add(eachPermission);
|
||||
}
|
||||
}
|
||||
// Request for missing permissions
|
||||
if (missingPermission.isEmpty()) {
|
||||
startSDKRegistration();
|
||||
} else {
|
||||
ActivityCompat.requestPermissions(this,
|
||||
missingPermission.toArray(new String[missingPermission.size()]),
|
||||
REQUEST_PERMISSION_CODE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Result of runtime permission request
|
||||
*/
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode,
|
||||
@NonNull String[] permissions,
|
||||
@NonNull int[] grantResults) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
// Check for granted permission and remove from missing list
|
||||
if (requestCode == REQUEST_PERMISSION_CODE) {
|
||||
for (int i = grantResults.length - 1; i >= 0; i--) {
|
||||
if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
|
||||
missingPermission.remove(permissions[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
// If there is enough permission, we will start the registration
|
||||
if (missingPermission.isEmpty()) {
|
||||
startSDKRegistration();
|
||||
} else {
|
||||
ToastUtils.setResultToToast("Missing permissions! Will not register SDK to connect to aircraft.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the SDK registration
|
||||
*/
|
||||
private void startSDKRegistration() {
|
||||
if (isRegistrationInProgress.compareAndSet(false, true)) {
|
||||
Log.i(TAG, "registering product");
|
||||
//addLog("Registering product");
|
||||
AsyncTask.execute(() -> DJISDKManager.getInstance().registerApp(getApplicationContext(), registrationCallback));
|
||||
}
|
||||
}
|
||||
|
||||
private void showToast(final String toastMsg) {
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Toast.makeText(getApplicationContext(), toastMsg, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 DJI
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.dji.ux.beta.sample;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
import dji.sdk.sdkmanager.DJISDKManager;
|
||||
|
||||
/**
|
||||
* This receiver will detect the USB attached event.
|
||||
* It will check if the app has been previously started.
|
||||
* If the app is already running, it will prevent a new activity from being started and bring the
|
||||
* existing activity to the top of the stack.
|
||||
*/
|
||||
|
||||
public class OnDJIUSBAttachedReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (!MainActivity.isStarted()) {
|
||||
Intent startIntent = context.getPackageManager()
|
||||
.getLaunchIntentForPackage(context.getPackageName());
|
||||
startIntent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT
|
||||
| Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
| Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
|
||||
startIntent.addCategory(Intent.CATEGORY_LAUNCHER);
|
||||
context.startActivity(startIntent);
|
||||
} else {
|
||||
Intent attachedIntent = new Intent();
|
||||
attachedIntent.setAction(DJISDKManager.USB_ACCESSORY_ATTACHED);
|
||||
context.sendBroadcast(attachedIntent);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 DJI
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.dji.ux.beta.sample;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.SharedPreferences;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.multidex.MultiDex;
|
||||
|
||||
import com.dji.frame.util.V_JsonUtil;
|
||||
import com.secneo.sdk.Helper;
|
||||
|
||||
import dji.sdk.base.BaseProduct;
|
||||
import dji.sdk.sdkmanager.DJISDKManager;
|
||||
import dji.ux.beta.core.communication.DefaultGlobalPreferences;
|
||||
import dji.ux.beta.core.communication.GlobalPreferencesManager;
|
||||
|
||||
import static com.dji.ux.beta.sample.DJIConnectionControlActivity.ACCESSORY_ATTACHED;
|
||||
|
||||
/**
|
||||
* An application that loads the SDK classes.
|
||||
*/
|
||||
public class SampleApplication extends Application {
|
||||
|
||||
private static Application app = null;
|
||||
private static BaseProduct product;
|
||||
private static final String TAG = SampleApplication.class.getName();
|
||||
|
||||
public static synchronized BaseProduct getProductInstance(){
|
||||
product = DJISDKManager.getInstance().getProduct();
|
||||
return product;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
//For the global preferences to take effect, this must be done before the widgets are initialized
|
||||
//If this is not done, no global preferences will take effect or persist across app restarts
|
||||
//TODO: prima era commentato e non c'era njson
|
||||
GlobalPreferencesManager.initialize(new DefaultGlobalPreferences(this));
|
||||
|
||||
BroadcastReceiver br = new OnDJIUSBAttachedReceiver();
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(ACCESSORY_ATTACHED);
|
||||
registerReceiver(br, filter);
|
||||
V_JsonUtil.DjiLog();
|
||||
|
||||
SharedPreferences sharedPreferences = getSharedPreferences(getString(R.string.my_pref), Context.MODE_PRIVATE);
|
||||
SharedPreferences.Editor editor = sharedPreferences.edit();
|
||||
editor.putBoolean(getString(R.string.disableConsole), true);
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void attachBaseContext(Context paramContext) {
|
||||
super.attachBaseContext(paramContext);
|
||||
Helper.install(SampleApplication.this);
|
||||
MultiDex.install(this);
|
||||
app = this;
|
||||
}
|
||||
|
||||
public static Application getInstance() {
|
||||
return SampleApplication.app;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,498 @@
|
|||
package com.dji.ux.beta.sample.connection;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.Service;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import androidx.core.content.res.ResourcesCompat;
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||
|
||||
import com.dji.ux.beta.sample.R;
|
||||
import com.dji.ux.beta.sample.cameraview.CameraActivity;
|
||||
import com.dji.ux.beta.sample.mission.GPSPlancia;
|
||||
import com.dji.ux.beta.sample.utils.DroneState;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.net.Socket;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class TcpClientService extends Service {
|
||||
private final AtomicBoolean working = new AtomicBoolean(true);
|
||||
private final AtomicBoolean workingJetson = new AtomicBoolean(true);
|
||||
private Socket socket;
|
||||
private Thread connectThread;
|
||||
// private Handler handler = new Handler();
|
||||
BufferedReader bufferedReader;//Declare the input stream object
|
||||
OutputStream outputStream;//Declare the output stream object
|
||||
public final String _ip = "192.168.1.104"; //console TP-link
|
||||
// public final String _ip = "192.168.2.5"; //console RUBICON
|
||||
// public final String _ip = "192.168.200.185"; //console livorno
|
||||
// public final String _ip = "213.82.97.234"; //console livorno remoto
|
||||
//public final String _ip = "192.168.1.105"; //localhost plancia
|
||||
private final String port = "11000";
|
||||
// private final String port = "8089"; //porta console livorno
|
||||
// private final String TAG = TcpClientService.class.getSimpleName();
|
||||
private final String TAG = "TcpClientService";
|
||||
Boolean isconnectBoolean = false;
|
||||
|
||||
Boolean isconnectBooleanJetson = false;
|
||||
private Socket socketJetson;
|
||||
private Thread connectThreadJetson;
|
||||
BufferedReader bufferedReaderJetson; //Declare the input stream object
|
||||
OutputStream outputStreamJetson; //Declare the output stream object
|
||||
public final String _ipJetson = "192.168.1.100"; //python jetson TP-link
|
||||
// public final String _ipJetson = "192.168.2.8"; //python jetson RUBICON
|
||||
// public final String _ipJetson = "192.168.200.22"; //python jetson livorno
|
||||
// public final String _ipJetson = "146.48.39.44"; //python jetson remoto
|
||||
private final String portJetson = "65432"; //port python
|
||||
|
||||
private String[] request;
|
||||
private String[] requestJetson;
|
||||
private String message = "Hello Server";
|
||||
private String messageJetson = "Hello Jetson";
|
||||
private static final String ACTION = "FROM_SERVER";
|
||||
private static final String KEY = "mission";
|
||||
SharedPreferences sharedPreferences;
|
||||
|
||||
private final Runnable runnable = new Runnable() {
|
||||
//TODO: connettere tramite pulsante
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.N)
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
sharedPreferences = getSharedPreferences(getString(R.string.my_pref), Context.MODE_PRIVATE);
|
||||
socket = new Socket(_ip, Integer.parseInt(port));
|
||||
|
||||
bufferedReader=new BufferedReader(new InputStreamReader(socket.getInputStream()));
|
||||
outputStream=socket.getOutputStream();
|
||||
|
||||
//char[] buffer=new char[256];//Define the array to receive the input stream data
|
||||
StringBuilder bufferString= new StringBuilder();//Define a character to receive array data
|
||||
//int tag=0;//First know where to write to the array
|
||||
|
||||
while (working.get()){
|
||||
Log.i(TAG, "Connected to " + _ip);
|
||||
fromServiceToActivity("IP", "server_ip", "Connected to " + _ip);
|
||||
|
||||
// outputStream.write((message +"\n").getBytes(StandardCharsets.UTF_8));
|
||||
// //The output stream is sent to the server
|
||||
// outputStream.flush();
|
||||
|
||||
//while (bufferedReader.read(buffer) >0){
|
||||
String line;
|
||||
|
||||
while ((line = bufferedReader.readLine()) != null){
|
||||
bufferString.append(line);
|
||||
|
||||
handleRequest(bufferString.toString());
|
||||
|
||||
//Log.i(TAG, message);
|
||||
|
||||
if(bufferString.toString().equals("status")){
|
||||
boolean disableConsole = sharedPreferences.getBoolean(getString(R.string.disableConsole), true);
|
||||
StringBuilder bufferStatus = DroneState.getDroneStatePlancia(disableConsole);
|
||||
//StringBuilder bufferStatus = DroneState.getDroneState();
|
||||
//bufferStatus.append("\n").append(DroneState.getStreamInfo());
|
||||
//TODO status string
|
||||
// Log.i(TAG, "State: " + bufferStatus.toString());
|
||||
outputStream.write((bufferStatus.toString()).getBytes(StandardCharsets.UTF_8));
|
||||
outputStream.flush();
|
||||
}
|
||||
|
||||
if(bufferString.toString().equals("closing connection")){
|
||||
//Log.i(TAG, "Status connection --> " + bufferStatus.toString());
|
||||
fromServiceToActivity("IP", "server_ip", getString(R.string.server_disc));
|
||||
break;
|
||||
}
|
||||
|
||||
//fromServiceToActivity("FROM_SERVER", "server_msg", bufferString.toString());
|
||||
bufferString.setLength(0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
socket.close();
|
||||
bufferedReader.close();
|
||||
outputStream.close();
|
||||
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private final Runnable runnableJetson = new Runnable() {
|
||||
@RequiresApi(api = Build.VERSION_CODES.N)
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
try {
|
||||
socketJetson = new Socket(_ipJetson, Integer.parseInt(portJetson));
|
||||
|
||||
bufferedReaderJetson=new BufferedReader(new InputStreamReader(socketJetson.getInputStream()));
|
||||
outputStreamJetson=socketJetson.getOutputStream();
|
||||
|
||||
StringBuilder bufferStringJetson= new StringBuilder();//Define a character to receive array data
|
||||
|
||||
while (workingJetson.get()){
|
||||
|
||||
Log.i(TAG, "Connected to " + _ipJetson);
|
||||
fromServiceToActivity("IP", "jetson_ip", "Jetson connected");
|
||||
// outputStreamJetson.write((messageJetson +"\n").getBytes(StandardCharsets.UTF_8));
|
||||
// outputStreamJetson.flush();
|
||||
|
||||
String line;
|
||||
while ((line=bufferedReaderJetson.readLine()) != null){
|
||||
bufferStringJetson.append(line);
|
||||
handleRequest(bufferStringJetson.toString());
|
||||
|
||||
if(bufferStringJetson.toString().equals("gps")){
|
||||
StringBuilder bufferStatus = DroneState.getGPSJetson();
|
||||
// Log.i(TAG, "Jetson gps: " + bufferStatus.toString());
|
||||
//outputStreamJetson.write((bufferStatus.toString()).getBytes(StandardCharsets.UTF_8));
|
||||
//outputStreamJetson.flush();
|
||||
sendMessageJetson(bufferStatus.toString());
|
||||
}
|
||||
bufferStringJetson.setLength(0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
socketJetson.close();
|
||||
bufferedReaderJetson.close();
|
||||
outputStreamJetson.close();
|
||||
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
|
||||
IntentFilter filter = new IntentFilter("FROM CAMERA");
|
||||
LocalBroadcastManager.getInstance(this).registerReceiver(broadcastReceiver, filter);
|
||||
|
||||
if(!isconnectBoolean){
|
||||
connectThread = new Thread(runnable);
|
||||
connectThread.start();
|
||||
Log.d(TAG, "Connected to the server successfully");
|
||||
isconnectBoolean=true;
|
||||
}
|
||||
|
||||
if(!isconnectBooleanJetson){
|
||||
connectThreadJetson = new Thread(runnableJetson);
|
||||
connectThreadJetson.start();
|
||||
Log.d(TAG, "Connected to the jetson successfully");
|
||||
isconnectBooleanJetson=true;
|
||||
}
|
||||
startMeForeground();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
if (isconnectBoolean){
|
||||
working.set(false);
|
||||
//connectThread.interrupt();
|
||||
if(socket!=null && !socket.isClosed()){
|
||||
try{
|
||||
if(outputStream!=null){
|
||||
outputStream.close();
|
||||
}
|
||||
if(bufferedReader!=null){
|
||||
bufferedReader.close();
|
||||
}
|
||||
socket.close();
|
||||
socket=null;
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Logger.getLogger(TcpClientService.class.getName()).log(Level.SEVERE, "Can't close a System.in based BufferedReader", e);
|
||||
}
|
||||
}
|
||||
connectThread.interrupt();
|
||||
Log.i(TAG, "Closing connection!");
|
||||
isconnectBoolean=false;
|
||||
}
|
||||
LocalBroadcastManager.getInstance(this).unregisterReceiver(broadcastReceiver);
|
||||
}
|
||||
|
||||
private void startMeForeground() {
|
||||
if (Build.VERSION.SDK_INT >= 26) {
|
||||
String NOTIFICATION_CHANNEL_ID = this.getPackageName();
|
||||
String channelName = "Tcp Client Background Service";
|
||||
NotificationChannel notificationChannel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, channelName, NotificationManager.IMPORTANCE_DEFAULT);
|
||||
notificationChannel.setLightColor(ResourcesCompat.getColor(getResources(), R.color.dark_gray, null));
|
||||
notificationChannel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
|
||||
NotificationManager notificationManager = getSystemService(NotificationManager.class);
|
||||
notificationManager.createNotificationChannel(notificationChannel);
|
||||
|
||||
Notification builder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
|
||||
.setOngoing(true)
|
||||
.setSmallIcon(R.drawable.icon_slider) //TODO:cambiare icona notifica (mettere mini-drone)
|
||||
.setContentTitle("Tcp Client is running in background")
|
||||
.setPriority(NotificationManager.IMPORTANCE_MAX)
|
||||
.setCategory(Notification.CATEGORY_SERVICE)
|
||||
.build();
|
||||
|
||||
startForeground(2, builder);
|
||||
} else {startForeground(1, new Notification());}
|
||||
}
|
||||
|
||||
private void fromServiceToActivity(String action, String key, String msg){
|
||||
Intent intent = new Intent(action);
|
||||
intent.putExtra(key, msg);
|
||||
LocalBroadcastManager.getInstance(TcpClientService.this).sendBroadcast(intent);
|
||||
}
|
||||
|
||||
private void handleRequest(String req){
|
||||
sharedPreferences = this.getSharedPreferences(getString(R.string.my_pref), Context.MODE_PRIVATE);
|
||||
SharedPreferences.Editor editor = sharedPreferences.edit();
|
||||
boolean disableConsole = sharedPreferences.getBoolean(getString(R.string.disableConsole), true);
|
||||
|
||||
if(!req.equals("gps") && !req.equals("") && !req.equals("status"))
|
||||
Log.i(TAG, "Received " + req);
|
||||
request = req.split("-");
|
||||
|
||||
if(disableConsole && !request[0].startsWith("hotpoint_jetson") && !request[0].startsWith("warning") && !req.equals("gps") && !req.equals("") && !req.equals("status")) {
|
||||
Log.i(TAG, "Ignoring command because the console is disabled");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (request[0]){
|
||||
case "waypoint_speed":
|
||||
setSpeed(editor);
|
||||
break;
|
||||
case "interdiction_area":
|
||||
setInterdictionRadius(editor);
|
||||
break;
|
||||
case "warning":
|
||||
setWarning(editor);
|
||||
break;
|
||||
case "waypoint_coordinates":
|
||||
//setCoordinate(editor);
|
||||
request[1] = request[1].replace(",", ".");
|
||||
request[2] = request[2].replace(",", ".");
|
||||
GPSPlancia.populateGPSPlancia(request[1], request[2], request[3]);
|
||||
//fromServiceToActivity(ACTION, KEY, "coordinates_wp");
|
||||
break;
|
||||
case "follow_coordinates":
|
||||
request[1] = request[1].replace(",", ".");
|
||||
request[2] = request[2].replace(",", ".");
|
||||
setFMCoordinate(editor);
|
||||
fromServiceToActivity(ACTION, KEY, "start_follow");
|
||||
break;
|
||||
case "go_to_ship":
|
||||
request[1] = request[1].replace(",", ".");
|
||||
request[2] = request[2].replace(",", ".");
|
||||
setShipCoordinate(editor);
|
||||
fromServiceToActivity(ACTION, KEY, "go_ship");
|
||||
break;
|
||||
case "update_coordinates":
|
||||
request[1] = request[1].replace(",", ".");
|
||||
request[2] = request[2].replace(",", ".");
|
||||
// Log.i(TAG, "Converted update coordinates: " + request[1] + ", " + request[2]);
|
||||
updateFMCoordinate(editor);
|
||||
//fromServiceToActivity(ACTION, KEY, "start_follow"); //TODO: cambiare MSG, serve un msg?
|
||||
break;
|
||||
case "hotpoint_coordinates": //from command NADSearchAtPos
|
||||
request[1] = request[1].replace(",", ".");
|
||||
request[2] = request[2].replace(",", ".");
|
||||
setHotCoordinate(editor);
|
||||
fromServiceToActivity(ACTION, KEY, "start_hotpoint");
|
||||
break;
|
||||
case "hotpoint_jetson":
|
||||
setHotCoordinate(editor);
|
||||
//TODO: check if it is needed fromServiceToActivity() here too
|
||||
break;
|
||||
default:
|
||||
fromServiceToActivity(ACTION, KEY, request[0]);
|
||||
}
|
||||
}
|
||||
|
||||
private void setSpeed(SharedPreferences.Editor editor){
|
||||
Log.i(TAG, "Setting speed to: " + request[1]);
|
||||
float speed = Float.parseFloat(request[1]);
|
||||
if(checkSpeed(speed, request[1])){
|
||||
editor.putFloat(getString(R.string.speed_waypoint), speed);
|
||||
editor.apply();
|
||||
fromServiceToActivity(ACTION, KEY, "speed_wp");
|
||||
}
|
||||
}
|
||||
|
||||
private void setInterdictionRadius(SharedPreferences.Editor editor){
|
||||
Log.i(TAG, "New interdiction radius: " + request[1]);
|
||||
request[1] = request[1].replace(",", ".");
|
||||
if(isNumeric(request[1])){
|
||||
// CameraActivity.setInterdictionRadius(Float.parseFloat(request[1]));
|
||||
|
||||
float interdictionArea = Float.parseFloat(request[1]);
|
||||
editor.putFloat(getString(R.string.interdiction_area), interdictionArea);
|
||||
editor.apply();
|
||||
fromServiceToActivity(ACTION, KEY, "interdiction_radius");
|
||||
}
|
||||
else {
|
||||
try {
|
||||
outputStream.write(("Incorrect or null interdiction_area value! Please send a numeric value\n").getBytes(StandardCharsets.UTF_8));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setWarning(SharedPreferences.Editor editor){
|
||||
Log.i(TAG, "warning " + request[1]);
|
||||
editor.putString(getString(R.string.warning), request[1]);
|
||||
editor.apply();
|
||||
fromServiceToActivity(ACTION, KEY, "warning");
|
||||
}
|
||||
|
||||
private void setCoordinate(SharedPreferences.Editor editor){
|
||||
editor.putString(getString(R.string.latitude), request[1]);
|
||||
editor.putString(getString(R.string.longitude), request[2]);
|
||||
editor.putString(getString(R.string.altitude), request[3]);
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
private void setHotCoordinate(SharedPreferences.Editor editor){
|
||||
editor.putString(getString(R.string.latitude_pos), request[1]);
|
||||
editor.putString(getString(R.string.longitude_pos), request[2]);
|
||||
editor.putString(getString(R.string.altitude_pos), request[3]);
|
||||
editor.putString(getString(R.string.radius_pos), request[4]);
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
private void setFMCoordinate(SharedPreferences.Editor editor){
|
||||
editor.putString(getString(R.string.latitude_fm), request[1]);
|
||||
editor.putString(getString(R.string.longitude_fm), request[2]);
|
||||
editor.putString(getString(R.string.altitude_fm), request[3]);
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
private void setShipCoordinate(SharedPreferences.Editor editor){
|
||||
editor.putString(getString(R.string.latitude_ship), request[1]);
|
||||
editor.putString(getString(R.string.longitude_ship), request[2]);
|
||||
editor.putString(getString(R.string.altitude_ship), request[3]);
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
private void updateFMCoordinate(SharedPreferences.Editor editor){
|
||||
// //TODO check if the replace is needed or not
|
||||
// request[1] = request[1].replace(",", ".");
|
||||
// request[2] = request[2].replace(",", ".");
|
||||
editor.putString(getString(R.string.latitude_fm), request[1]);
|
||||
editor.putString(getString(R.string.longitude_fm), request[2]);
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
private boolean checkSpeed(float speed, String msg){
|
||||
if (speed <= 0.0f || speed > 15.0f || !isNumeric(msg)){
|
||||
try {
|
||||
outputStream.write(("Incorrect or null speed value! Please send a value in the range [0.0f-15.0f] " + "\n").getBytes(StandardCharsets.UTF_8));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean isNumeric(String strNum) {
|
||||
if (strNum == null) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
Float.parseFloat(strNum);
|
||||
} catch (NumberFormatException nfe) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean checkGpsCoordinates(double latitude, double longitude) {
|
||||
return (latitude > -90 && latitude < 90 && longitude > -180 && longitude < 180) && (latitude != 0f && longitude != 0f);
|
||||
}
|
||||
|
||||
private final BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
|
||||
Log.i(TAG, "Broadcaster: " + intent.getStringExtra("state"));
|
||||
String msg= intent.getStringExtra("state");
|
||||
if(msg.equals("isStreaming")){
|
||||
sendMessageJetson(msg);
|
||||
}
|
||||
if(msg.equals("mob_mission")){
|
||||
sendMessagePlancia(msg);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private void sendMessageJetson(String msg) {
|
||||
Thread thread = new Thread(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
if(outputStreamJetson!=null){
|
||||
// Log.i(TAG, "sending to jetson" + msg);
|
||||
outputStreamJetson.write((msg + "\n").getBytes(StandardCharsets.UTF_8));
|
||||
outputStreamJetson.flush();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
thread.start();
|
||||
}
|
||||
|
||||
private void sendMessagePlancia(String msg) {
|
||||
Thread thread = new Thread(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
if(outputStream!=null){
|
||||
Log.i(TAG, "sending to ship" + msg);
|
||||
outputStream.write((msg).getBytes(StandardCharsets.UTF_8));
|
||||
outputStream.flush();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
thread.start();
|
||||
}
|
||||
|
||||
//TODO: cambiare thread (ogni nuovo messaggio apre un thread-no buono)
|
||||
}
|
|
@ -0,0 +1,216 @@
|
|||
package com.dji.ux.beta.sample.mission;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.location.Location;
|
||||
import android.location.LocationManager;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import com.dji.mapkit.core.models.DJIBitmapDescriptor;
|
||||
import com.dji.mapkit.core.models.DJIBitmapDescriptorFactory;
|
||||
import com.dji.mapkit.core.models.DJILatLng;
|
||||
import com.dji.mapkit.core.models.annotations.DJIMarkerOptions;
|
||||
import com.dji.ux.beta.sample.R;
|
||||
import com.dji.ux.beta.sample.SampleApplication;
|
||||
import com.dji.ux.beta.sample.utils.DroneState;
|
||||
import com.dji.ux.beta.sample.utils.ToastUtils;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import dji.common.error.DJIError;
|
||||
import dji.common.flightcontroller.FlightControllerState;
|
||||
import dji.common.mission.followme.FollowMeHeading;
|
||||
import dji.common.mission.followme.FollowMeMission;
|
||||
import dji.common.mission.followme.FollowMeMissionEvent;
|
||||
import dji.common.mission.followme.FollowMeMissionState;
|
||||
import dji.common.model.LocationCoordinate2D;
|
||||
import dji.common.util.CommonCallbacks;
|
||||
import dji.sdk.base.BaseProduct;
|
||||
import dji.sdk.flightcontroller.FlightController;
|
||||
import dji.sdk.mission.followme.FollowMeMissionOperator;
|
||||
import dji.sdk.mission.followme.FollowMeMissionOperatorListener;
|
||||
import dji.sdk.products.Aircraft;
|
||||
import dji.sdk.sdkmanager.DJISDKManager;
|
||||
import dji.ux.beta.map.widget.map.MapWidget;
|
||||
|
||||
public class FollowMission {
|
||||
|
||||
private static final String TAG = FollowMission.class.getSimpleName();
|
||||
private final Context mContext;
|
||||
|
||||
private FollowMeMissionOperator instance;
|
||||
private double latitude;
|
||||
private double longitude;
|
||||
Thread locationUpdateThread;
|
||||
private boolean missionRunning = false;
|
||||
|
||||
//TODO: disegnare icona nave che si muove quando cambia posizione
|
||||
|
||||
public FollowMission(Context mContext){
|
||||
this.mContext = mContext;
|
||||
}
|
||||
|
||||
private void setResultToToast(String s) {
|
||||
ToastUtils.setResultToToast(s);
|
||||
}
|
||||
|
||||
public FollowMeMissionOperator getFollowM(){
|
||||
if(instance == null){
|
||||
if(DJISDKManager.getInstance().getMissionControl()!=null){
|
||||
instance=DJISDKManager.getInstance().getMissionControl().getFollowMeMissionOperator();
|
||||
}
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
public void addListenerFollow() {
|
||||
if (getFollowM() != null){
|
||||
getFollowM().addListener(followEventNotificationListener);
|
||||
}
|
||||
}
|
||||
|
||||
public void removeListenerFollow(){
|
||||
if (getFollowM() != null){
|
||||
getFollowM().removeListener(followEventNotificationListener);
|
||||
}
|
||||
}
|
||||
|
||||
private final FollowMeMissionOperatorListener followEventNotificationListener = new FollowMeMissionOperatorListener() {
|
||||
@Override
|
||||
public void onExecutionUpdate(@NonNull FollowMeMissionEvent followMeMissionEvent) {
|
||||
Log.i(TAG, "FollowingMission: onExecutionUpdate state is: " + followMeMissionEvent.getCurrentState() + " distance to target is: " + followMeMissionEvent.getDistanceToTarget());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onExecutionStart() {
|
||||
Log.i(TAG, "FollowMission started.");
|
||||
setResultToToast("FollowMission started");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onExecutionFinish(@Nullable DJIError djiError) {
|
||||
// locationUpdateThread.interrupt();
|
||||
missionRunning = false;
|
||||
Log.i(TAG, "FollowMission Execution finished: " + (djiError == null ? "Success!" : djiError.getDescription()));
|
||||
setResultToToast("FollowMission Execution finished: " + (djiError == null ? "Success!" : djiError.getDescription()));
|
||||
}
|
||||
};
|
||||
|
||||
public void startFollowShip(SharedPreferences preferences){
|
||||
Log.i(TAG, "Starting FollowMission, current state: " + getFollowState());
|
||||
latitude = Double.parseDouble(preferences.getString(mContext.getString(R.string.latitude_fm), ""));
|
||||
longitude = Double.parseDouble(preferences.getString(mContext.getString(R.string.longitude_fm), ""));
|
||||
float altitude = Float.parseFloat(preferences.getString(mContext.getString(R.string.altitude_fm), ""));
|
||||
|
||||
if (getFollowM().getCurrentState().toString().equals(FollowMeMissionState.READY_TO_EXECUTE.toString())) {
|
||||
setResultToToast("Ready to follow");
|
||||
|
||||
FollowMeMission followMeMission = new FollowMeMission(FollowMeHeading.TOWARD_FOLLOW_POSITION, latitude, longitude, altitude);
|
||||
//followMeEvent = new FollowMeMissionEvent.Builder().distanceToTarget(34);
|
||||
Log.i(TAG, "Altitude: " + altitude + "\nLatitude: " + latitude + "\nLongitude: " + longitude);
|
||||
Log.i(TAG, "FollowMeInfo: " + followMeMission.getLatitude() + " " + followMeMission.getLongitude());
|
||||
|
||||
//starts the new mission just created
|
||||
getFollowM().startMission(followMeMission, new CommonCallbacks.CompletionCallback() {
|
||||
@Override
|
||||
public void onResult(DJIError djiError) {
|
||||
if(djiError==null){
|
||||
missionRunning = true;
|
||||
// setResultToToast("Follow mission created successfully, starting follow thread...");
|
||||
Log.i(TAG, "Follow mission created successfully with location destination: " + getFollowM().getFollowingTarget() + ", starting follow thread...");
|
||||
locationUpdateThread = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// while (!Thread.currentThread().isInterrupted()) {
|
||||
while (missionRunning) {
|
||||
// if(getFollowState().equals(FollowMeMissionState.EXECUTING.toString()))
|
||||
// {
|
||||
latitude = Double.parseDouble(preferences.getString(mContext.getString(R.string.latitude_fm), ""));
|
||||
longitude = Double.parseDouble(preferences.getString(mContext.getString(R.string.longitude_fm), ""));
|
||||
Log.i(TAG, "FollowMission: " + getFollowState() + ", following target: " + getFollowM().getFollowingTarget());
|
||||
// Log.i(TAG, "FollowMeInfo: " + followMeMission.getLatitude() + " " + followMeMission.getLongitude());
|
||||
|
||||
getFollowM().updateFollowingTarget(new LocationCoordinate2D(latitude, longitude), new CommonCallbacks.CompletionCallback() {
|
||||
@Override
|
||||
public void onResult(DJIError djiError) {
|
||||
if (djiError != null) {
|
||||
// Log.i(TAG, "FollowMission: Failed to update target GPS: " + djiError.getDescription());
|
||||
// setResultToToast("Failed to update target GPS: " + djiError.getDescription());
|
||||
} else {
|
||||
Log.i(TAG, "Location updating " + latitude + " " + longitude);
|
||||
setResultToToast("Location updating " + latitude + " " + longitude);
|
||||
}
|
||||
}
|
||||
});
|
||||
try {
|
||||
//TODO: cambiare millisecondi in sleep (consigliati 10Hz)
|
||||
Thread.sleep(1500);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
// }
|
||||
Log.i(TAG, "Update thread ended.");
|
||||
}
|
||||
});
|
||||
locationUpdateThread.start();
|
||||
} else {
|
||||
setResultToToast("FollowMission: error: " + djiError.getDescription());
|
||||
Log.i(TAG, "FollowMission: error: " + djiError.getDescription());
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
setResultToToast("Not ready to execute FollowMission");
|
||||
}
|
||||
}
|
||||
|
||||
public void stopFollowShip(){
|
||||
Log.i(TAG, "Stopping FollowMission, current state: " + getFollowState());
|
||||
getFollowM().stopMission(new CommonCallbacks.CompletionCallback() {
|
||||
@Override
|
||||
public void onResult(DJIError djiError) {
|
||||
Log.i(TAG, "FollowMission Stopped: " + (djiError == null ? "Successfully" : djiError.getDescription()));
|
||||
setResultToToast("FollowMission Stop: " + (djiError == null ? "Successfully" : djiError.getDescription()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void provaFollow(SharedPreferences preferences){
|
||||
Thread locationUpdate = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
while (true) {
|
||||
|
||||
latitude = Double.parseDouble(preferences.getString(mContext.getString(R.string.latitude_fm), ""));
|
||||
longitude = Double.parseDouble(preferences.getString(mContext.getString(R.string.longitude_fm), ""));
|
||||
Log.i(TAG, "Location Value: " + "\n" + "Latitude: " + latitude
|
||||
+ "\n" + "Longitude: " + longitude + "\n");
|
||||
|
||||
try {
|
||||
Thread.sleep(5000);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
locationUpdate.start();
|
||||
|
||||
}
|
||||
|
||||
public String getFollowState() {
|
||||
return getFollowM().getCurrentState().toString();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
package com.dji.ux.beta.sample.mission;
|
||||
|
||||
import android.location.Location;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.dji.ux.beta.sample.R;
|
||||
import com.dji.ux.beta.sample.utils.ToastUtils;
|
||||
import com.google.android.gms.maps.model.LatLng;
|
||||
import com.google.maps.android.SphericalUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
public class GPSPlancia {
|
||||
|
||||
private static final String TAG = GPSPlancia.class.getSimpleName();
|
||||
private double latitude;
|
||||
private double longitude;
|
||||
private float altitude;
|
||||
private static CopyOnWriteArrayList<GPSPlancia> gpsPlanciaList = new CopyOnWriteArrayList<>();
|
||||
private static HashSet<GPSPlancia> gpsPlanciaHashSet = new HashSet<>();
|
||||
|
||||
public GPSPlancia(double latitude, double longitude, float altitude){
|
||||
this.latitude = latitude;
|
||||
this.longitude = longitude;
|
||||
this.altitude = altitude;
|
||||
}
|
||||
|
||||
public double getLatitude() {
|
||||
return latitude;
|
||||
}
|
||||
|
||||
public float getAltitude() {
|
||||
return altitude;
|
||||
}
|
||||
|
||||
public double getLongitude() {
|
||||
return longitude;
|
||||
}
|
||||
|
||||
public static CopyOnWriteArrayList<GPSPlancia> getGPSPlanciaList() {
|
||||
return gpsPlanciaList;
|
||||
}
|
||||
|
||||
public static void populateGPSPlancia(String latitudeString, String longitudeString, String altitudeString) {
|
||||
double latitude = Double.parseDouble(latitudeString);
|
||||
double longitude = Double.parseDouble(longitudeString);
|
||||
float altitude = Float.parseFloat(altitudeString);
|
||||
GPSPlancia gpsPlancia = new GPSPlancia(latitude, longitude, altitude);
|
||||
if(!gpsPlanciaList.contains(gpsPlancia)){
|
||||
gpsPlanciaList.add(new GPSPlancia(latitude, longitude, altitude));
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean checkPosDistance(LatLng oldPos, LatLng currentPos, double distanceMeters){
|
||||
double distance = SphericalUtil.computeDistanceBetween(oldPos, currentPos);
|
||||
return distance >= distanceMeters;
|
||||
}
|
||||
|
||||
//remove waypoints that are closer than interdictionRadius between them
|
||||
public static List<GPSPlancia> getCleanedGPSList(double interdictionRadius){
|
||||
Log.i(TAG, "Initial GPS list length: " + gpsPlanciaList.size());
|
||||
List<GPSPlancia> cleanedList = new ArrayList<>();
|
||||
if(!gpsPlanciaList.isEmpty()){
|
||||
cleanedList.add(gpsPlanciaList.get(0));
|
||||
LatLng oldPos = new LatLng((gpsPlanciaList.get(0).getLatitude()), cleanedList.get(0).getLongitude());
|
||||
for (GPSPlancia gps: gpsPlanciaList) {
|
||||
LatLng currentPos = new LatLng(gps.getLatitude(), gps.getLongitude());
|
||||
if(checkPosDistance(oldPos, currentPos, interdictionRadius)){
|
||||
cleanedList.add(gps);
|
||||
oldPos = currentPos;
|
||||
}
|
||||
if(cleanedList.size()==99){
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ToastUtils.setResultToToast("GPS list empty!");
|
||||
// Toast.makeText(getApplicationContext(), "GPS list empty!", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
Log.i(TAG, "Cleaned GPS list length: " + cleanedList.size());
|
||||
return cleanedList;
|
||||
}
|
||||
|
||||
//remove first waypointCount element from gpsPlanciaList
|
||||
public static void updateGPSList(int waypointCount) {
|
||||
// int wpToRemove = waypointCount/2;
|
||||
//TODO capire perche' serve il /2
|
||||
int wpToRemove = waypointCount + 1; //the parameter is the number of waypoints to be deleted
|
||||
Log.i(TAG, "Initial GPS list length: " + gpsPlanciaList.size() + ", waypointCount: " + waypointCount + ", therefore removing " + wpToRemove + " waypoints");
|
||||
for (int i = 0; i < wpToRemove; i++) {
|
||||
gpsPlanciaList.remove(0);
|
||||
}
|
||||
Log.i(TAG, "Updated GPS list length: " + gpsPlanciaList.size());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,352 @@
|
|||
package com.dji.ux.beta.sample.mission;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||
|
||||
import com.dji.ux.beta.sample.R;
|
||||
import com.dji.ux.beta.sample.cameraview.CameraActivity;
|
||||
import com.dji.ux.beta.sample.connection.TcpClientService;
|
||||
import com.dji.ux.beta.sample.utils.ToastUtils;
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import dji.common.error.DJIError;
|
||||
import dji.common.mission.waypoint.Waypoint;
|
||||
import dji.common.mission.waypoint.WaypointAction;
|
||||
import dji.common.mission.waypoint.WaypointActionType;
|
||||
import dji.common.mission.waypoint.WaypointMission;
|
||||
import dji.common.mission.waypoint.WaypointMissionDownloadEvent;
|
||||
import dji.common.mission.waypoint.WaypointMissionExecuteState;
|
||||
import dji.common.mission.waypoint.WaypointMissionExecutionEvent;
|
||||
import dji.common.mission.waypoint.WaypointMissionFinishedAction;
|
||||
import dji.common.mission.waypoint.WaypointMissionFlightPathMode;
|
||||
import dji.common.mission.waypoint.WaypointMissionHeadingMode;
|
||||
import dji.common.mission.waypoint.WaypointMissionState;
|
||||
import dji.common.mission.waypoint.WaypointMissionUploadEvent;
|
||||
import dji.common.util.CommonCallbacks;
|
||||
import dji.sdk.mission.waypoint.WaypointMissionOperator;
|
||||
import dji.sdk.mission.waypoint.WaypointMissionOperatorListener;
|
||||
import dji.sdk.sdkmanager.DJISDKManager;
|
||||
|
||||
|
||||
public class PointMission {
|
||||
|
||||
private static final String TAG = PointMission.class.getSimpleName();
|
||||
|
||||
private WaypointMissionOperator instance;
|
||||
public static WaypointMission.Builder waypointMissionBuilder;
|
||||
private final WaypointMissionFinishedAction mFinishedAction = WaypointMissionFinishedAction.NO_ACTION;
|
||||
//private final WaypointMissionFinishedAction mFinishedAction = WaypointMissionFinishedAction.GO_HOME; this action if you want the drone to come back to first WP when execution finished
|
||||
private final WaypointMissionHeadingMode mHeadingMode = WaypointMissionHeadingMode.AUTO;
|
||||
private int wayPointCount = 0;
|
||||
private int nextWaypointIndex = -1;
|
||||
|
||||
//private float waypointSpeed = 10.0f; //range [2,15] m/s.
|
||||
|
||||
private final Context mContext;
|
||||
|
||||
//TODO: decidere se rimuovere i controlli di stato prima di start(pause/resume/stop (sono presenti in start di follow e pos)
|
||||
|
||||
public PointMission(Context mContext){
|
||||
this.mContext = mContext;
|
||||
}
|
||||
|
||||
// private void setResultToToast(Context mContext, String s) {
|
||||
// Toast.makeText(mContext, s, Toast.LENGTH_SHORT).show();
|
||||
// }
|
||||
|
||||
private void setResultToToast(String s) {
|
||||
ToastUtils.setResultToToast(s);
|
||||
}
|
||||
|
||||
public WaypointMissionOperator getWaypointM (){
|
||||
if (instance == null) {
|
||||
if(DJISDKManager.getInstance().getMissionControl()!= null) {
|
||||
instance = DJISDKManager.getInstance().getMissionControl().getWaypointMissionOperator();
|
||||
}
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
public void addListenerWaypoint() {
|
||||
if (getWaypointM() != null){
|
||||
getWaypointM().addListener(waypointEventNotificationListener);
|
||||
}
|
||||
}
|
||||
|
||||
public void removeListenerWaypoint(){
|
||||
if (getWaypointM() != null){
|
||||
getWaypointM().removeListener(waypointEventNotificationListener);
|
||||
}
|
||||
}
|
||||
|
||||
private final WaypointMissionOperatorListener waypointEventNotificationListener = new WaypointMissionOperatorListener() {
|
||||
@Override
|
||||
public void onDownloadUpdate(@NonNull WaypointMissionDownloadEvent waypointMissionDownloadEvent) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUploadUpdate(@NonNull WaypointMissionUploadEvent waypointMissionUploadEvent) {
|
||||
if (waypointMissionUploadEvent.getProgress() != null
|
||||
&& waypointMissionUploadEvent.getProgress().isSummaryUploaded
|
||||
&& waypointMissionUploadEvent.getProgress().uploadedWaypointIndex == (waypointMissionUploadEvent.getProgress().totalWaypointCount) - 1) {
|
||||
Log.i(TAG, "onUploadUpdate: Mission uploaded successfully, current state: " + getWaypointMissionState());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onExecutionUpdate(@NonNull WaypointMissionExecutionEvent waypointMissionExecutionEvent) {
|
||||
if(waypointMissionExecutionEvent.getProgress().isWaypointReached){
|
||||
nextWaypointIndex = waypointMissionExecutionEvent.getProgress().targetWaypointIndex;
|
||||
Log.i(TAG, "Waypoint reached " + nextWaypointIndex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onExecutionStart() {
|
||||
Log.i(TAG, "Execution started, number of waypoints: " + waypointMissionBuilder.getWaypointCount());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onExecutionFinish(@Nullable @org.jetbrains.annotations.Nullable DJIError djiError) {
|
||||
Log.i(TAG, "Execution ended with nextWaypointIndex: " + nextWaypointIndex + " and waypointMissionBuilder.getWaypointCount(): " + waypointMissionBuilder.getWaypointCount());
|
||||
if(nextWaypointIndex >= 0 && nextWaypointIndex == waypointMissionBuilder.getWaypointCount()-1) {
|
||||
Log.i(TAG, "All waypoints reached");
|
||||
setResultToToast("Execution finished: " + (djiError == null ? "Success!" : djiError.getDescription()));
|
||||
deleteWaypoint();
|
||||
|
||||
CameraActivity activity = (CameraActivity) mContext;
|
||||
activity.deleteMarkers();
|
||||
activity.setPersonNotification(false);
|
||||
}
|
||||
// waypointMissionBuilder = null;
|
||||
nextWaypointIndex = -1;
|
||||
}
|
||||
};
|
||||
|
||||
public void createWaypointFromList(List<GPSPlancia> cleanedList){
|
||||
if(waypointMissionBuilder == null){
|
||||
waypointMissionBuilder = new WaypointMission.Builder();
|
||||
}
|
||||
if (cleanedList.isEmpty()){
|
||||
setResultToToast("List of positions is empty!");
|
||||
} else {
|
||||
for (GPSPlancia gps: cleanedList) {
|
||||
|
||||
Waypoint waypoint = new Waypoint(gps.getLatitude(), gps.getLongitude(), gps.getAltitude());
|
||||
//waypoint.addAction(new WaypointAction(WaypointActionType.STAY, 0));
|
||||
waypoint.gimbalPitch = -90.0f; //from = -135.0, to = 45.0)
|
||||
waypointMissionBuilder.addWaypoint(waypoint);
|
||||
}
|
||||
}
|
||||
Log.i(TAG, "WP MissBuildList " + waypointMissionBuilder.getWaypointList().toString());
|
||||
Log.i(TAG, "WP MissBuildList size: " + waypointMissionBuilder.getWaypointList().size());
|
||||
// Log.i(TAG, "WP WaypointCount(): " + waypointMissionBuilder.getWaypointCount());
|
||||
if(waypointMissionBuilder!= null && !waypointMissionBuilder.getWaypointList().isEmpty()){
|
||||
setResultToToast("Positions added to the map!");
|
||||
}
|
||||
}
|
||||
|
||||
public void configWaypointMission(float waypointSpeed){
|
||||
if (waypointMissionBuilder == null){
|
||||
waypointMissionBuilder = new WaypointMission.Builder()
|
||||
.finishedAction(mFinishedAction)
|
||||
.headingMode(mHeadingMode)
|
||||
.autoFlightSpeed(waypointSpeed)
|
||||
.maxFlightSpeed(waypointSpeed)
|
||||
.flightPathMode(WaypointMissionFlightPathMode.NORMAL)
|
||||
.setGimbalPitchRotationEnabled(true); //per controllare gimbal pitch
|
||||
} else{
|
||||
waypointMissionBuilder
|
||||
.finishedAction(mFinishedAction)
|
||||
.headingMode(mHeadingMode)
|
||||
.autoFlightSpeed(waypointSpeed)
|
||||
.maxFlightSpeed(waypointSpeed)
|
||||
.flightPathMode(WaypointMissionFlightPathMode.NORMAL)
|
||||
.setGimbalPitchRotationEnabled(true); //per controllare gimbal pitch
|
||||
}
|
||||
|
||||
Log.i(TAG, "is gimbal rotation enabled? " + waypointMissionBuilder.isGimbalPitchRotationEnabled());
|
||||
Log.i(TAG, "Auto Flight Speed " + waypointMissionBuilder.getAutoFlightSpeed());
|
||||
|
||||
DJIError error = getWaypointM().loadMission(waypointMissionBuilder.build());
|
||||
if(error == null){
|
||||
// setResultToToast("loadMission success");
|
||||
} else {
|
||||
setResultToToast("loadMission failed " + error.getDescription());
|
||||
Log.i(TAG, "loadMission failed " + error.getDescription());
|
||||
}
|
||||
}
|
||||
|
||||
public String getWaypointMissionState(){
|
||||
return getWaypointM().getCurrentState().toString();
|
||||
}
|
||||
|
||||
public void uploadAndStartWayPointMission(){
|
||||
Log.i(TAG, "Uploading WP mission, current state is: " + getWaypointMissionState());
|
||||
getWaypointM().uploadMission(new CommonCallbacks.CompletionCallback() {
|
||||
@Override
|
||||
public void onResult(DJIError djiError) {
|
||||
if(djiError == null){
|
||||
Log.i(TAG, "Mission uploaded successfully, current state: " + getWaypointMissionState());
|
||||
setResultToToast("Mission upload successfully!");
|
||||
startWaypointMission();
|
||||
} else {
|
||||
Log.i(TAG, "Mission upload failed, error: " + djiError.getDescription() + " retrying...");
|
||||
setResultToToast("Mission upload failed, error: " + djiError.getDescription() + " retrying...");
|
||||
getWaypointM().retryUploadMission(null);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void setWaypointMissionSpeed(float waypointSpeed){ //(@FloatRange(from = -15.0f, to = 15.0f)
|
||||
getWaypointM().setAutoFlightSpeed(waypointSpeed, new CommonCallbacks.CompletionCallback() {
|
||||
@Override
|
||||
public void onResult(DJIError djiError) {
|
||||
if(djiError == null){
|
||||
setResultToToast("Speed set successfully!");
|
||||
} else {
|
||||
setResultToToast("Unable to set the desired speed: " + djiError.getDescription());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void startWaypointMission(){
|
||||
Log.i(TAG, "Starting WP mission, current state: " + getWaypointMissionState());
|
||||
getWaypointM().startMission(new CommonCallbacks.CompletionCallback() {
|
||||
@Override
|
||||
public void onResult(DJIError djiError) {
|
||||
if(djiError == null){
|
||||
Log.i(TAG, "WaypointMission started successfully!");
|
||||
setResultToToast("Mission start successfully!");
|
||||
}
|
||||
else {
|
||||
Log.i(TAG, "Error while starting WaypointMission: " + djiError.getDescription());
|
||||
setResultToToast("Error while starting WaypointMission: " + djiError.getDescription());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public int pauseWaypointMission(){
|
||||
if (getWaypointMissionState().equals(WaypointMissionState.EXECUTING.toString())){
|
||||
getWaypointM().pauseMission(new CommonCallbacks.CompletionCallback() {
|
||||
@Override
|
||||
public void onResult(DJIError djiError) {
|
||||
Log.i(TAG, "WaypointMission Pause: " + (djiError == null ? "Successfully" : djiError.getDescription()));
|
||||
setResultToToast("WaypointMission Pause: " + (djiError == null ? "Successfully" : djiError.getDescription()));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
setResultToToast("Couldn't pause WaypointMission because state is not executing, current state: " + getWaypointMissionState());
|
||||
}
|
||||
return nextWaypointIndex;
|
||||
}
|
||||
|
||||
public void resumeWaypointMission(){
|
||||
if (getWaypointMissionState().equals(WaypointMissionState.EXECUTION_PAUSED.toString())){
|
||||
getWaypointM().resumeMission(new CommonCallbacks.CompletionCallback() {
|
||||
@Override
|
||||
public void onResult(DJIError djiError) {
|
||||
Log.i(TAG, "WaypointMission Resume: " + (djiError == null ? "Successfully" : djiError.getDescription()));
|
||||
setResultToToast("WaypointMission Resume: " + (djiError == null ? "Successfully" : djiError.getDescription()));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
setResultToToast("Couldn't resume WaypointMission because state is not paused, current state: " + getWaypointMissionState());
|
||||
}
|
||||
}
|
||||
|
||||
public int stopWaypointMission(){
|
||||
if (getWaypointMissionState().equals(WaypointMissionState.EXECUTING.toString()) || getWaypointMissionState().equals(WaypointMissionState.EXECUTION_PAUSED.toString())){
|
||||
getWaypointM().stopMission(new CommonCallbacks.CompletionCallback() {
|
||||
@Override
|
||||
public void onResult(DJIError djiError) {
|
||||
Log.i(TAG, "WaypointMission Stop: " + (djiError == null ? "Successfully" : djiError.getDescription()));
|
||||
setResultToToast("WaypointMission Stop: " + (djiError == null ? "Successfully" : djiError.getDescription()));
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
setResultToToast("Couldn't stop WaypointMission because state is not executing or paused, current state: " + getWaypointMissionState());
|
||||
}
|
||||
return nextWaypointIndex;
|
||||
}
|
||||
|
||||
private boolean deleteWaypointsFromMissionBuilder() {
|
||||
if(waypointMissionBuilder!=null) {
|
||||
Log.i(TAG, "Deleting: " + waypointMissionBuilder.getWaypointCount() + " elements in waypointMissionBuilder.getWaypointList()");
|
||||
int totalWp = waypointMissionBuilder.getWaypointCount();
|
||||
|
||||
for (int i = 0; i < totalWp; i++) {
|
||||
waypointMissionBuilder.removeWaypoint(0);
|
||||
}
|
||||
Log.i(TAG, "WP MissBuildList after delete" + waypointMissionBuilder.getWaypointList().toString());
|
||||
Log.i(TAG, "WP MissBuildCount after delete: " + waypointMissionBuilder.getWaypointCount());
|
||||
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
Log.i(TAG, "waypointMissionBuilder is null, nothing to delete");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//remove all waypoints from waypointMissionBuilder (and set it tu null) and from GPSPlancia
|
||||
public void deleteWaypoint(){
|
||||
boolean deleted = deleteWaypointsFromMissionBuilder();
|
||||
|
||||
if (deleted) {
|
||||
GPSPlancia.getGPSPlanciaList().clear();
|
||||
nextWaypointIndex = -1;
|
||||
setResultToToast("All positions deleted");
|
||||
} else {
|
||||
setResultToToast("No position to delete");
|
||||
//return "No position to delete";
|
||||
}
|
||||
// wayPointCount = 0;
|
||||
}
|
||||
|
||||
public int getWayPointCount(){
|
||||
return this.wayPointCount;
|
||||
}
|
||||
|
||||
public int getNextWaypointIndex() {
|
||||
return this.nextWaypointIndex;
|
||||
}
|
||||
|
||||
// remove visited waypoints from waypointMissionBuilder and from GPSPlancia
|
||||
public void deleteWPCount(){
|
||||
// if(wayPointCount>0 && !waypointMissionBuilder.getWaypointList().isEmpty()){
|
||||
// waypointMissionBuilder.getWaypointList().subList(0, wayPointCount/2).clear();
|
||||
// //TODO capire perche' serve il /2
|
||||
// }
|
||||
|
||||
boolean deleted = deleteWaypointsFromMissionBuilder();
|
||||
|
||||
// if (deleted) {
|
||||
// setResultToToast("Visited positions deleted");
|
||||
// }
|
||||
if(nextWaypointIndex>=0 && !GPSPlancia.getGPSPlanciaList().isEmpty()) {
|
||||
GPSPlancia.updateGPSList(nextWaypointIndex);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,163 @@
|
|||
package com.dji.ux.beta.sample.mission;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Looper;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.dji.ux.beta.sample.R;
|
||||
import com.dji.ux.beta.sample.utils.ToastUtils;
|
||||
|
||||
import dji.common.error.DJIError;
|
||||
import dji.common.mission.hotpoint.HotpointHeading;
|
||||
import dji.common.mission.hotpoint.HotpointMission;
|
||||
import dji.common.mission.hotpoint.HotpointMissionEvent;
|
||||
import dji.common.mission.hotpoint.HotpointMissionState;
|
||||
import dji.common.mission.hotpoint.HotpointStartPoint;
|
||||
import dji.common.mission.waypoint.WaypointMissionState;
|
||||
import dji.common.model.LocationCoordinate2D;
|
||||
import dji.common.util.CommonCallbacks;
|
||||
import dji.sdk.mission.hotpoint.HotpointMissionOperator;
|
||||
import dji.sdk.mission.hotpoint.HotpointMissionOperatorListener;
|
||||
import dji.sdk.sdkmanager.DJISDKManager;
|
||||
//TODO: qui ho utilizzato il toast di ToastUtils
|
||||
//TODO: mandare gli stati delle missioni alla plancia quando viene avviata/stoppata ecc..
|
||||
|
||||
public class PosMission {
|
||||
|
||||
private static final String TAG = PosMission.class.getSimpleName();
|
||||
|
||||
private HotpointMissionOperator instance;
|
||||
private final HotpointStartPoint hotpointStartPoint = HotpointStartPoint.NEAREST;
|
||||
private final HotpointHeading hotpointHeading = HotpointHeading.TOWARDS_HOT_POINT;
|
||||
|
||||
private final Context mContext;
|
||||
|
||||
public PosMission(Context mContext) {
|
||||
this.mContext = mContext;
|
||||
}
|
||||
|
||||
private void setResultToToast(String s) {
|
||||
ToastUtils.setResultToToast(s);
|
||||
}
|
||||
|
||||
public HotpointMissionOperator getHotpointM(){
|
||||
if (instance==null){
|
||||
if(DJISDKManager.getInstance().getMissionControl()!=null){
|
||||
instance = DJISDKManager.getInstance().getMissionControl().getHotpointMissionOperator();
|
||||
}
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
public String getHPState(){
|
||||
return getHotpointM().getCurrentState().toString();
|
||||
}
|
||||
|
||||
public void addListenerHotpoint() {
|
||||
if (getHotpointM() != null){
|
||||
getHotpointM().addListener(hotpointEventNotificationListener);
|
||||
}
|
||||
}
|
||||
|
||||
public void removeListenerHotpoint(){
|
||||
if (getHotpointM() != null){
|
||||
getHotpointM().removeListener(hotpointEventNotificationListener);
|
||||
}
|
||||
}
|
||||
|
||||
private final HotpointMissionOperatorListener hotpointEventNotificationListener = new HotpointMissionOperatorListener() {
|
||||
@Override
|
||||
public void onExecutionUpdate(@NonNull HotpointMissionEvent hotpointMissionEvent) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onExecutionStart() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onExecutionFinish(@Nullable DJIError djiError) {
|
||||
setResultToToast("HotpointMission finished: " + (djiError == null ? "Success!" : djiError.getDescription()));
|
||||
}
|
||||
};
|
||||
|
||||
public void startHotPoint(SharedPreferences preferences){
|
||||
Log.i(TAG, "Starting HotpointMission, current state: " + getHPState());
|
||||
if (getHPState().equals(HotpointMissionState.READY_TO_EXECUTE.toString())){
|
||||
double latitude = Double.parseDouble(preferences.getString(mContext.getString(R.string.latitude_pos), ""));
|
||||
double longitude = Double.parseDouble(preferences.getString(mContext.getString(R.string.longitude_pos), ""));
|
||||
double altitude = Double.parseDouble(preferences.getString(mContext.getString(R.string.altitude_pos), ""));
|
||||
double radius = Double.parseDouble(preferences.getString(mContext.getString(R.string.radius_pos), "5.0"));
|
||||
float angularVel = 20.0f;
|
||||
|
||||
HotpointMission mHotpointMission = new HotpointMission(new LocationCoordinate2D(latitude, longitude), altitude, radius,
|
||||
angularVel, true, hotpointStartPoint, hotpointHeading);
|
||||
|
||||
Log.i(TAG, "Altitude: " + altitude + "\nLatitude: " + latitude + "\nLongitude: " + longitude + "\nradius: " + radius);
|
||||
Log.i(TAG, "Hotpoint " + mHotpointMission.getHotpoint().toString());
|
||||
|
||||
getHotpointM().startMission(mHotpointMission, new CommonCallbacks.CompletionCallback() {
|
||||
@Override
|
||||
public void onResult(DJIError djiError) {
|
||||
if (djiError != null) {
|
||||
Log.i(TAG, "Error while starting HotpointMission, current state: " + getHPState() + ", ERROR: " + djiError.getDescription());
|
||||
setResultToToast("Could not start hotpoint mission: " + djiError.getDescription());
|
||||
} else {
|
||||
Log.i(TAG, "HotpointMission started Successfully! Current state: " + getHPState());
|
||||
setResultToToast("HotpointMission started Successfully!");
|
||||
//TODO: inviare alla plancia
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void pauseHotPoint(){
|
||||
if (getHPState().equals(WaypointMissionState.EXECUTING.toString())){
|
||||
getHotpointM().pause(new CommonCallbacks.CompletionCallback() {
|
||||
@Override
|
||||
public void onResult(DJIError djiError) {
|
||||
Log.i(TAG, "HotpointMission Paused: " + (djiError == null ? "Successfully" : djiError.getDescription()));
|
||||
setResultToToast("HotpointMission Paused: " + (djiError == null ? "Successfully" : djiError.getDescription()));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
setResultToToast("Couldn't pause HotpointMission because state is not executing, current state: " + getHPState());
|
||||
}
|
||||
}
|
||||
|
||||
public void resumeHotPoint(){
|
||||
if (getHPState().equals(WaypointMissionState.EXECUTION_PAUSED.toString())){
|
||||
getHotpointM().resume(new CommonCallbacks.CompletionCallback() {
|
||||
@Override
|
||||
public void onResult(DJIError djiError) {
|
||||
Log.i(TAG, "HotpointMission Resumed: " + (djiError == null ? "Successfully" : djiError.getDescription()));
|
||||
setResultToToast("HotpointMission Resumed: " + (djiError == null ? "Successfully" : djiError.getDescription()));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
setResultToToast("Couldn't resume HotpointMission because state is not paused, current state: " + getHPState());
|
||||
}
|
||||
}
|
||||
|
||||
public void stopHotPoint(){
|
||||
if (getHPState().equals(WaypointMissionState.INITIAL_PHASE.toString()) || getHPState().equals(WaypointMissionState.EXECUTING.toString()) || getHPState().equals(WaypointMissionState.EXECUTION_PAUSED.toString())){
|
||||
getHotpointM().stop(new CommonCallbacks.CompletionCallback() {
|
||||
@Override
|
||||
public void onResult(DJIError djiError) {
|
||||
Log.i(TAG, "HotpointMission Stopped: " + (djiError == null ? "Successfully" : djiError.getDescription()));
|
||||
setResultToToast("HotpointMission Stopped: " + (djiError == null ? "Successfully" : djiError.getDescription()));
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
setResultToToast("Couldn't stop HotpointMission because state is not executing or paused, current state: " + getHPState());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,295 @@
|
|||
package com.dji.ux.beta.sample.utils;
|
||||
|
||||
/*
|
||||
Util class that provides info onm drone status such as battery charge, altitude, speed, etc..
|
||||
*/
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import com.dji.mapkit.core.models.DJIBitmapDescriptor;
|
||||
import com.dji.mapkit.core.models.DJIBitmapDescriptorFactory;
|
||||
import com.dji.ux.beta.sample.R;
|
||||
import com.dji.ux.beta.sample.SampleApplication;
|
||||
import com.dji.ux.beta.sample.mission.GPSPlancia;
|
||||
import com.google.android.gms.maps.model.LatLng;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import dji.common.battery.BatteryState;
|
||||
import dji.common.error.DJIError;
|
||||
import dji.common.flightcontroller.FlightControllerState;
|
||||
import dji.common.model.LocationCoordinate2D;
|
||||
import dji.common.util.CommonCallbacks;
|
||||
import dji.sdk.base.BaseProduct;
|
||||
import dji.sdk.flightcontroller.FlightController;
|
||||
import dji.sdk.flighthub.FlightHubManager;
|
||||
import dji.sdk.flighthub.model.RealTimeFlightData;
|
||||
import dji.sdk.products.Aircraft;
|
||||
import dji.sdk.sdkmanager.DJISDKManager;
|
||||
|
||||
|
||||
public class DroneState {
|
||||
|
||||
private static final String TAG = DroneState.class.getSimpleName();
|
||||
protected static StringBuilder stringState = new StringBuilder();
|
||||
protected static StringBuilder stringStatePlancia = new StringBuilder();
|
||||
protected static StringBuilder stringStream = new StringBuilder();
|
||||
protected static StringBuilder stringJetson;
|
||||
protected static String altitude;
|
||||
protected static String latitude;
|
||||
protected static String longitude;
|
||||
protected static String whichLatitude;
|
||||
protected static int batteryCharge;
|
||||
private static FlightController flightController;
|
||||
private static String tracking;
|
||||
private static String searching;
|
||||
private static String following;
|
||||
private static String serialNumber;
|
||||
private static String speed;
|
||||
private static LatLng homeLocation;
|
||||
|
||||
public static LatLng getHomeLocation(){
|
||||
BaseProduct product = SampleApplication.getProductInstance();
|
||||
if (product instanceof Aircraft) { flightController = ((Aircraft) product).getFlightController(); }
|
||||
if(flightController != null){
|
||||
flightController.getHomeLocation(new CommonCallbacks.CompletionCallbackWith<LocationCoordinate2D>() {
|
||||
@Override
|
||||
public void onSuccess(LocationCoordinate2D locationCoordinate2D) {
|
||||
homeLocation = new LatLng(locationCoordinate2D.getLatitude(), locationCoordinate2D.getLongitude());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(DJIError djiError) {
|
||||
|
||||
}
|
||||
});
|
||||
} else{
|
||||
homeLocation = new LatLng(43.719259015524976, 10.421048893480233);
|
||||
}
|
||||
return homeLocation;
|
||||
}
|
||||
|
||||
|
||||
private static void getDroneLocation(BaseProduct product){
|
||||
|
||||
if (product instanceof Aircraft) { flightController = ((Aircraft) product).getFlightController(); }
|
||||
|
||||
if (flightController !=null){
|
||||
flightController.setStateCallback(new FlightControllerState.Callback() {
|
||||
@Override
|
||||
public void onUpdate(@NonNull @NotNull FlightControllerState flightControllerState) {
|
||||
|
||||
if (flightControllerState.getAircraftLocation() != null) {
|
||||
if(!Double.isNaN(flightControllerState.getAircraftLocation().getLatitude()) && !Double.isNaN(flightControllerState.getAircraftLocation().getLongitude())) {
|
||||
latitude = String.valueOf(flightControllerState.getAircraftLocation().getLatitude());
|
||||
longitude = String.valueOf(flightControllerState.getAircraftLocation().getLongitude());
|
||||
altitude = String.valueOf(flightControllerState.getAircraftLocation().getAltitude());
|
||||
} else {
|
||||
latitude = "0.0";
|
||||
longitude = "0.0";
|
||||
latitude = "0.0";
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static void getDroneBattery(){
|
||||
|
||||
try {
|
||||
SampleApplication.getProductInstance().getBattery().setStateCallback(new BatteryState.Callback() {
|
||||
@Override
|
||||
public void onUpdate(BatteryState batteryState) {
|
||||
batteryCharge = batteryState.getChargeRemainingInPercent();
|
||||
}
|
||||
});
|
||||
} catch (Exception ignored){
|
||||
}
|
||||
}
|
||||
|
||||
public static void setTracking(){
|
||||
whichLatitude = "Drone Latitude: ";
|
||||
//tracking = DJISDKManager.getInstance().getMissionControl().getWaypointMissionOperator().getCurrentState().toString();
|
||||
tracking = DJISDKManager.getInstance().getMissionControl().getHotpointMissionOperator().getCurrentState().toString();
|
||||
searching = DJISDKManager.getInstance().getMissionControl().getWaypointMissionOperator().getCurrentState().toString();
|
||||
if(searching.equals("EXECUTING")){
|
||||
whichLatitude = "Searching Drone Latitude: ";
|
||||
}
|
||||
following = DJISDKManager.getInstance().getMissionControl().getFollowMeMissionOperator().getCurrentState().toString();
|
||||
}
|
||||
|
||||
public static StringBuilder getDroneState(){
|
||||
|
||||
BaseProduct product = SampleApplication.getProductInstance();
|
||||
if (product != null && product.isConnected()) {
|
||||
|
||||
getDroneLocation(product);
|
||||
getDroneBattery();
|
||||
setTracking();
|
||||
stringState.delete(0, stringState.length());
|
||||
stringState.append("\n");
|
||||
stringState.append("Product Connected!").append("\n");
|
||||
stringState.append("Product Name: ").append(product.getModel().getDisplayName()).append("\n");
|
||||
stringState.append("Battery charge: ").append(batteryCharge).append("%\n");
|
||||
stringState.append(whichLatitude).append(altitude).append("m\n");
|
||||
stringState.append("Drone latitude: ").append(latitude).append("\n");
|
||||
stringState.append("Drone longitude: ").append(longitude).append("\n");
|
||||
stringState.append("Flight State: ").append(tracking).append("\n");
|
||||
stringState.append("Following State: ").append(following).append("\n");
|
||||
|
||||
} else {
|
||||
stringState.delete(0, stringState.length());
|
||||
stringState.append("No product connected.").append("\n");
|
||||
}
|
||||
return stringState;
|
||||
}
|
||||
|
||||
public static StringBuilder getStreamInfo(){
|
||||
BaseProduct product = SampleApplication.getProductInstance();
|
||||
if (product != null && product.isConnected()) {
|
||||
if(DJISDKManager.getInstance().getLiveStreamManager().isStreaming()) {
|
||||
stringStream.append("Streaming live on: ").append(DJISDKManager.getInstance().getLiveStreamManager().getLiveUrl()).append("\n");
|
||||
long startTime = DJISDKManager.getInstance().getLiveStreamManager().getStartTime();
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault());
|
||||
String sd = sdf.format(new Date(Long.parseLong(String.valueOf(startTime))));
|
||||
stringStream.append("Start Time: ").append(sd).append("\n");;
|
||||
stringStream.append("Video Resolution: ").append(DJISDKManager.getInstance().getLiveStreamManager().getLiveVideoResolution()).append("\n");;
|
||||
stringStream.append("Video BitRate:").append(DJISDKManager.getInstance().getLiveStreamManager().getLiveVideoBitRate()).append(" kpbs\n");
|
||||
stringStream.append("Audio BitRate:").append(DJISDKManager.getInstance().getLiveStreamManager().getLiveAudioBitRate()).append(" kpbs\n");
|
||||
stringStream.append("Video FPS:").append(DJISDKManager.getInstance().getLiveStreamManager().getLiveVideoFps()).append("\n");
|
||||
stringStream.append("Video Cache size:").append(DJISDKManager.getInstance().getLiveStreamManager().getLiveVideoCacheSize()).append(" frame");
|
||||
} else {
|
||||
stringStream.append("No streaming available.").append("\n");
|
||||
}
|
||||
} else {
|
||||
stringStream.append("No product connected.").append("\n");
|
||||
}
|
||||
return stringStream;
|
||||
}
|
||||
|
||||
public static StringBuilder getDroneStatePlancia(boolean disableConsole){
|
||||
BaseProduct product = SampleApplication.getProductInstance();
|
||||
if (product != null && product.isConnected()) {
|
||||
|
||||
getDroneLocation(product);
|
||||
getDroneBattery();
|
||||
setTracking();
|
||||
// getRealTimeData();
|
||||
getSpeed();
|
||||
stringStatePlancia.delete(0, stringStatePlancia.length());
|
||||
// stringStatePlancia.append("\n");
|
||||
stringStatePlancia.append("true").append("#");
|
||||
stringStatePlancia.append(disableConsole).append("#");
|
||||
stringStatePlancia.append(batteryCharge).append("#");
|
||||
stringStatePlancia.append(speed).append("#");
|
||||
// stringStatePlancia.append("0").append("#");
|
||||
stringStatePlancia.append(altitude).append("#");
|
||||
stringStatePlancia.append(latitude).append("#");
|
||||
stringStatePlancia.append(longitude).append("#");
|
||||
if(tracking != null && tracking.equals("EXECUTING"))
|
||||
stringStatePlancia.append("true").append("#");
|
||||
else
|
||||
stringStatePlancia.append("false").append("#");
|
||||
|
||||
if(following != null && following.equals("EXECUTING"))
|
||||
stringStatePlancia.append("true").append("#");
|
||||
else
|
||||
stringStatePlancia.append("false").append("#");
|
||||
|
||||
if(searching != null && searching.equals("EXECUTING"))
|
||||
stringStatePlancia.append("true").append("#");
|
||||
else
|
||||
stringStatePlancia.append("false").append("#");
|
||||
if(DJISDKManager.getInstance().getLiveStreamManager().isStreaming()) {
|
||||
stringStatePlancia.append(DJISDKManager.getInstance().getLiveStreamManager().getLiveUrl()).append("#");
|
||||
} else {
|
||||
stringStatePlancia.append("NoStream").append("#");
|
||||
}
|
||||
} else {
|
||||
stringStatePlancia.delete(0, stringStatePlancia.length());
|
||||
stringStatePlancia.append("False").append("#");
|
||||
}
|
||||
return stringStatePlancia;
|
||||
}
|
||||
|
||||
public static StringBuilder getGPSJetson(){
|
||||
stringJetson = new StringBuilder();
|
||||
BaseProduct product = SampleApplication.getProductInstance();
|
||||
if (product != null && product.isConnected()) {
|
||||
getDroneLocation(product);
|
||||
stringJetson.delete(0, stringJetson.length());
|
||||
//stringJetson.append("hotpoint_coordinates").append("-"); if we want to automatic search
|
||||
stringJetson.append("hotpoint_jetson").append("-");
|
||||
stringJetson.append(latitude).append("-");
|
||||
stringJetson.append(longitude).append("-");
|
||||
stringJetson.append(altitude).append("-");
|
||||
stringJetson.append("5").append("\n\r");
|
||||
|
||||
} else {
|
||||
stringJetson.append("no_connected").append("\n\r");
|
||||
}
|
||||
return stringJetson;
|
||||
}
|
||||
|
||||
private static void getSpeed() {
|
||||
String velX = Float.toString(flightController.getState().getVelocityX());
|
||||
String velY = Float.toString(flightController.getState().getVelocityY());
|
||||
String velZ = Float.toString(flightController.getState().getVelocityZ());
|
||||
speed = velX + ":" + velY + ":" + velZ;
|
||||
// Log.i(TAG, "speed " + speed);
|
||||
}
|
||||
|
||||
// gets real time flight data of the aircraft with the given serial number
|
||||
private static void getRealTimeData() {
|
||||
//TODO:se non va togliere commento
|
||||
BaseProduct product = SampleApplication.getProductInstance();
|
||||
if (product instanceof Aircraft) { flightController = ((Aircraft) product).getFlightController(); }
|
||||
flightController.getSerialNumber(new CommonCallbacks.CompletionCallbackWith<String>() {
|
||||
@Override
|
||||
public void onSuccess(String s) {
|
||||
serialNumber = s;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(DJIError djiError) {
|
||||
|
||||
}
|
||||
});
|
||||
final ArrayList<String> serialNumbers = new ArrayList<>(1);
|
||||
serialNumbers.add(serialNumber);
|
||||
|
||||
try {
|
||||
DJISDKManager.getInstance().getFlightHubManager().getAircraftRealTimeFlightData(serialNumbers, new CommonCallbacks.CompletionCallbackWith<List<RealTimeFlightData>>() {
|
||||
@Override
|
||||
public void onSuccess(List<RealTimeFlightData> realTimeFlightData) {
|
||||
for (RealTimeFlightData s : realTimeFlightData) {
|
||||
speed = String.valueOf(s.getSpeed());
|
||||
Log.i(TAG, "speed " + s.getSpeed());
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void onFailure(DJIError error) {
|
||||
Log.i(TAG, "getAircraftRealTimeFlightData failed: " + error.getDescription());
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
package com.dji.ux.beta.sample.utils;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.util.Pair;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.dji.ux.beta.sample.SampleApplication;
|
||||
|
||||
public class ToastUtils {
|
||||
private static final int MESSAGE_UPDATE = 1;
|
||||
private static final int MESSAGE_TOAST = 2;
|
||||
private static Handler mUIHandler = new Handler(Looper.getMainLooper()) {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
//Get the message string
|
||||
switch ((msg.what)) {
|
||||
case MESSAGE_UPDATE:
|
||||
showMessage((Pair<TextView, String>) msg.obj);
|
||||
break;
|
||||
case MESSAGE_TOAST:
|
||||
showToast((String) msg.obj);
|
||||
break;
|
||||
default:
|
||||
super.handleMessage(msg);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private static void showMessage(Pair<TextView, String> msg) {
|
||||
if (msg != null) {
|
||||
if (msg.first == null) {
|
||||
Toast.makeText(SampleApplication.getInstance(), "tv is null", Toast.LENGTH_SHORT).show();
|
||||
} else {
|
||||
msg.first.setText(msg.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void showToast(String msg) {
|
||||
Toast.makeText(SampleApplication.getInstance(), msg, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
|
||||
public static void setResultToToast(final String string) {
|
||||
Message msg = new Message();
|
||||
msg.what = MESSAGE_TOAST;
|
||||
msg.obj = string;
|
||||
mUIHandler.sendMessage(msg);
|
||||
}
|
||||
|
||||
public static void setResultToText(final TextView tv, final String s) {
|
||||
Message msg = new Message();
|
||||
msg.what = MESSAGE_UPDATE;
|
||||
msg.obj = new Pair<>(tv, s);
|
||||
mUIHandler.sendMessage(msg);
|
||||
}
|
||||
|
||||
// public static void seToToast(Context mContext, String s) {
|
||||
// Toast.makeText(mContext, s, Toast.LENGTH_SHORT).show();
|
||||
// }
|
||||
}
|
After Width: | Height: | Size: 319 B |
After Width: | Height: | Size: 348 B |
After Width: | Height: | Size: 371 B |
After Width: | Height: | Size: 574 B |
After Width: | Height: | Size: 666 B |
After Width: | Height: | Size: 373 B |
After Width: | Height: | Size: 404 B |
After Width: | Height: | Size: 435 B |
After Width: | Height: | Size: 670 B |
After Width: | Height: | Size: 780 B |
After Width: | Height: | Size: 386 B |
After Width: | Height: | Size: 412 B |
After Width: | Height: | Size: 444 B |
After Width: | Height: | Size: 671 B |
After Width: | Height: | Size: 784 B |
After Width: | Height: | Size: 330 B |
After Width: | Height: | Size: 363 B |
After Width: | Height: | Size: 382 B |
After Width: | Height: | Size: 566 B |
After Width: | Height: | Size: 662 B |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 9.1 KiB |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 6.9 KiB |
After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 47 KiB |
After Width: | Height: | Size: 88 KiB |
After Width: | Height: | Size: 383 B |
After Width: | Height: | Size: 418 B |
After Width: | Height: | Size: 460 B |
After Width: | Height: | Size: 723 B |
After Width: | Height: | Size: 885 B |
|
@ -0,0 +1,33 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2018-2020 DJI
|
||||
~
|
||||
~ Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
~ of this software and associated documentation files (the "Software"), to deal
|
||||
~ in the Software without restriction, including without limitation the rights
|
||||
~ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
~ copies of the Software, and to permit persons to whom the Software is
|
||||
~ furnished to do so, subject to the following conditions:
|
||||
~
|
||||
~ The above copyright notice and this permission notice shall be included in all
|
||||
~ copies or substantial portions of the Software.
|
||||
~
|
||||
~ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
~ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
~ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
~ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
~ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
~ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
~ SOFTWARE.
|
||||
~
|
||||
-->
|
||||
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
|
||||
<gradient
|
||||
android:angle="90"
|
||||
android:endColor="@color/transparent"
|
||||
android:startColor="@color/uxsdk_black_40_percent" />
|
||||
|
||||
</shape>
|
|
@ -0,0 +1,32 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2018-2020 DJI
|
||||
~
|
||||
~ Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
~ of this software and associated documentation files (the "Software"), to deal
|
||||
~ in the Software without restriction, including without limitation the rights
|
||||
~ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
~ copies of the Software, and to permit persons to whom the Software is
|
||||
~ furnished to do so, subject to the following conditions:
|
||||
~
|
||||
~ The above copyright notice and this permission notice shall be included in all
|
||||
~ copies or substantial portions of the Software.
|
||||
~
|
||||
~ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
~ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
~ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
~ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
~ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
~ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
~ SOFTWARE.
|
||||
~
|
||||
-->
|
||||
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
|
||||
<gradient
|
||||
android:endColor="@color/transparent"
|
||||
android:startColor="@color/uxsdk_black_40_percent" />
|
||||
|
||||
</shape>
|
|
@ -0,0 +1,32 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2018-2020 DJI
|
||||
~
|
||||
~ Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
~ of this software and associated documentation files (the "Software"), to deal
|
||||
~ in the Software without restriction, including without limitation the rights
|
||||
~ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
~ copies of the Software, and to permit persons to whom the Software is
|
||||
~ furnished to do so, subject to the following conditions:
|
||||
~
|
||||
~ The above copyright notice and this permission notice shall be included in all
|
||||
~ copies or substantial portions of the Software.
|
||||
~
|
||||
~ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
~ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
~ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
~ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
~ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
~ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
~ SOFTWARE.
|
||||
~
|
||||
-->
|
||||
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
|
||||
<gradient
|
||||
android:endColor="@color/uxsdk_black_40_percent"
|
||||
android:startColor="@color/transparent" />
|
||||
|
||||
</shape>
|
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 32 KiB |
After Width: | Height: | Size: 227 B |
After Width: | Height: | Size: 268 B |
After Width: | Height: | Size: 251 B |
After Width: | Height: | Size: 371 B |
After Width: | Height: | Size: 448 B |
After Width: | Height: | Size: 258 B |
After Width: | Height: | Size: 298 B |
After Width: | Height: | Size: 282 B |
After Width: | Height: | Size: 435 B |
After Width: | Height: | Size: 520 B |
After Width: | Height: | Size: 261 B |
After Width: | Height: | Size: 314 B |
After Width: | Height: | Size: 284 B |
After Width: | Height: | Size: 444 B |
After Width: | Height: | Size: 517 B |
After Width: | Height: | Size: 232 B |
After Width: | Height: | Size: 273 B |
After Width: | Height: | Size: 254 B |
After Width: | Height: | Size: 382 B |
After Width: | Height: | Size: 444 B |
After Width: | Height: | Size: 814 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 4.8 KiB |
After Width: | Height: | Size: 589 B |
After Width: | Height: | Size: 4.5 KiB |
After Width: | Height: | Size: 2.4 KiB |
After Width: | Height: | Size: 27 KiB |
After Width: | Height: | Size: 50 KiB |
After Width: | Height: | Size: 262 B |
After Width: | Height: | Size: 275 B |
After Width: | Height: | Size: 291 B |
After Width: | Height: | Size: 460 B |
After Width: | Height: | Size: 606 B |
|
@ -0,0 +1,30 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:endX="85.84757"
|
||||
android:endY="92.4963"
|
||||
android:startX="42.9492"
|
||||
android:startY="49.59793"
|
||||
android:type="linear">
|
||||
<item
|
||||
android:color="#44000000"
|
||||
android:offset="0.0" />
|
||||
<item
|
||||
android:color="#00000000"
|
||||
android:offset="1.0" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
|
||||
android:strokeWidth="1"
|
||||
android:strokeColor="#00000000" />
|
||||
</vector>
|
After Width: | Height: | Size: 371 B |
After Width: | Height: | Size: 442 B |
After Width: | Height: | Size: 448 B |
After Width: | Height: | Size: 666 B |
After Width: | Height: | Size: 898 B |
After Width: | Height: | Size: 435 B |
After Width: | Height: | Size: 509 B |
After Width: | Height: | Size: 520 B |
After Width: | Height: | Size: 780 B |