first commit

This commit is contained in:
gunther82 2023-03-16 16:01:08 +01:00
commit ccafb0a309
237 changed files with 6079 additions and 0 deletions

0
README.md Normal file
View File

1
app/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/build

172
app/build.gradle Normal file
View File

@ -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()
}

21
app/proguard-rules.pro vendored Normal file
View File

@ -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

View File

@ -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());
}
}

View File

@ -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>

View File

@ -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();
}
}

View File

@ -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();
}
});
}
}

View File

@ -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);
}
}
}

View File

@ -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;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -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(",", ".");