diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml
index 1f83832..c5a0a37 100644
--- a/.idea/jarRepositories.xml
+++ b/.idea/jarRepositories.xml
@@ -36,5 +36,10 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/README.md b/README.md
index 18adecc..0374b61 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# RosettaDrone 2 is now updated to DJI SDK 4.14 beta 1...
+# RosettaDrone 2 is now updated to DJI SDK 4.16.1...
Rosetta Drone 2 tested on **DJI Air, Mavic 2 x and Matrice 210 V2, Mavic Pro, Mavic AIR series
and supports Android 5.1 and newer, with DJI SDK 4.14 and Androidx lib
diff --git a/app/build.gradle b/app/build.gradle
index 5a5afe8..f92e29f 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -1,17 +1,28 @@
apply plugin: 'com.android.application'
android {
+ signingConfigs {
+ release {
+ }
+ }
compileSdkVersion 28
defaultConfig {
applicationId "sq.rogue.rosettadrone"
minSdkVersion 22
targetSdkVersion 28
versionCode 1
- versionName '5.1.1'
+ versionName '5.2.0' // This is the one to modify...
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
multiDexEnabled true
}
+ repositories {
+ jcenter()
+ maven {
+ url "https://maven.java.net/content/groups/public/"
+ }
+ }
+
dexOptions {
javaMaxHeapSize "4g"
}
@@ -19,16 +30,20 @@ android {
buildTypes {
release {
minifyEnabled false
+ debuggable false
+ jniDebuggable false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
debug {
minifyEnabled false
debuggable true
- jniDebuggable true
+ jniDebuggable false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ renderscriptOptimLevel 0
}
}
packagingOptions {
+ pickFirst 'META-INF/LICENSE.txt' // picks the JavaMail license file
exclude 'META-INF/rxjava.properties'
}
@@ -76,8 +91,9 @@ android {
defaultConfig {
ndk {
// abiFilters 'armeabi-v7a'
-// abiFilters 'armeabi-v7a', 'arm64-v8a'
- abiFilters 'armeabi-v7a', 'x86', 'arm64-v8a'
+ abiFilters 'armeabi-v7a', 'arm64-v8a'
+ // DJI has removed x86 support...
+// abiFilters 'armeabi-v7a', 'x86', 'arm64-v8a'
}
}
@@ -85,20 +101,21 @@ android {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
+// ndkVersion '21.4.7075529'
// We need to be restrict as we get error on newer versions...
- buildToolsVersion '30.0.2'
+ // buildToolsVersion '30.0.2'
ndkVersion '22.0.7026061'
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
- androidTestImplementation('androidx.test.espresso:espresso-core:3.3.0', {
+ androidTestImplementation('androidx.test.espresso:espresso-core:3.4.0', {
exclude group: 'com.android.support', module: 'support-annotations'
})
implementation 'com.squareup:otto:1.3.8'
- implementation('com.dji:dji-sdk:4.14-trial1', {
+ implementation('com.dji:dji-sdk:4.16.1', {
/**
* Uncomment the "library-anti-distortion" if your app does not need Anti Distortion for Mavic 2 Pro and Mavic 2 Zoom.
* Uncomment the "fly-safe-database" if you need database for release, or we will download it when DJISDKManager.getInstance().registerApp
@@ -108,38 +125,42 @@ dependencies {
exclude module: 'library-anti-distortion'
exclude module: 'fly-safe-database'
})
- compileOnly 'com.dji:dji-sdk-provided:4.14-trial1'
+ compileOnly 'com.dji:dji-sdk-provided:4.16.1'
- implementation 'com.dji:dji-uxsdk:4.14-trial1'
+ implementation 'com.dji:dji-uxsdk:4.16'
- implementation 'androidx.annotation:annotation:1.1.0'
- implementation 'androidx.appcompat:appcompat:1.2.0'
- implementation 'androidx.preference:preference:1.1.1'
+ implementation 'androidx.annotation:annotation:1.3.0'
+ implementation 'androidx.appcompat:appcompat:1.3.1'
+ implementation 'androidx.preference:preference:1.2.0'
implementation 'androidx.legacy:legacy-preference-v14:1.0.0'
- implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
- implementation 'com.google.android.material:material:1.2.1'
- testImplementation 'junit:junit:4.13.1'
+ implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
+ implementation 'com.google.android.material:material:1.4.0'
+ testImplementation 'junit:junit:4.13.2'
testImplementation 'org.mockito:mockito-core:2.7.22'
testImplementation 'org.mockito:mockito-android:2.7.22'
implementation 'androidx.multidex:multidex:2.0.1'
- implementation 'androidx.core:core:1.3.2'
- implementation 'androidx.recyclerview:recyclerview:1.1.0'
+ implementation 'androidx.core:core:1.6.0'
+ implementation 'androidx.recyclerview:recyclerview:1.2.1'
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
- implementation 'com.google.android.gms:play-services-maps:17.0.0'
- implementation 'com.google.android.gms:play-services-basement:17.5.0'
- implementation 'com.google.android.gms:play-services-base:17.5.0'
+ implementation 'com.google.android.gms:play-services-maps:18.0.2'
+ implementation 'com.google.android.gms:play-services-basement:18.0.2'
+ implementation 'com.google.android.gms:play-services-base:18.0.1'
implementation 'com.google.android.gms:play-services-gcm:17.0.0'
- implementation 'com.google.android.gms:play-services-location:17.1.0'
+ implementation 'com.google.android.gms:play-services-location:19.0.1'
- implementation 'com.google.maps:google-maps-services:0.15.0'
+ implementation 'com.google.maps:google-maps-services:0.18.0'
implementation 'com.jakewharton:butterknife:10.2.3'
annotationProcessor 'com.jakewharton:butterknife-compiler:10.2.3'
- implementation 'androidx.core:core-ktx:1.3.2'
- implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.0-beta01'
+ implementation 'androidx.core:core-ktx:1.6.0'
+ implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.1'
- implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.4.21'
+ implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.4.31'
+
+ implementation 'javax.mail:mail:1.4.7'
+ // exclude 'javax.activation:activation:1.1'
+ // implementation 'com.sun.mail:android-activation:1.6.6'
}
diff --git a/app/src/main/java/sq/rogue/rosettadrone/ConnectionActivity.java b/app/src/main/java/sq/rogue/rosettadrone/ConnectionActivity.java
index bb17f4f..e5054d0 100755
--- a/app/src/main/java/sq/rogue/rosettadrone/ConnectionActivity.java
+++ b/app/src/main/java/sq/rogue/rosettadrone/ConnectionActivity.java
@@ -7,6 +7,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
+import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.hardware.usb.UsbManager;
import android.media.Ringtone;
@@ -67,6 +68,7 @@ public class ConnectionActivity extends Activity implements View.OnClickListener
private Button mBtnTest;
private int hiddenkey = 0;
private Handler mUIHandler;
+ private static boolean running = false;
private KeyListener firmVersionListener = new KeyListener() {
@Override
@@ -158,14 +160,26 @@ public class ConnectionActivity extends Activity implements View.OnClickListener
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+
+ if(this.running == false){
+ Log.v(TAG, "First time ... Registrer Receiver");
+ this.running = true;
+ }
+
checkAndRequestPermissions();
+
+ //---------------- Force Landscape ether ways...
+ this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
+
setContentView(R.layout.activity_connection);
initUI();
// Register the broadcast receiver for receiving the device connection's changes.
+ Log.v(TAG, "Registrer Receiver");
IntentFilter filter = new IntentFilter();
filter.addAction(DJISimulatorApplication.FLAG_CONNECTION_CHANGE);
registerReceiver(mReceiver, filter);
+
}
protected BroadcastReceiver mReceiver = new BroadcastReceiver() {
@@ -197,6 +211,7 @@ public class ConnectionActivity extends Activity implements View.OnClickListener
if (KeyManager.getInstance() != null) {
KeyManager.getInstance().removeListener(firmVersionListener);
}
+ unregisterReceiver(mReceiver);
super.onDestroy();
}
@@ -270,6 +285,7 @@ public class ConnectionActivity extends Activity implements View.OnClickListener
mTextModelAvailable = (TextView) findViewById(R.id.text_model_available);
mTextProduct = (TextView) findViewById(R.id.text_product_info);
+
mBtnOpen = (Button) findViewById(R.id.btn_start);
mBtnOpen.setOnClickListener(this);
mBtnOpen.setEnabled(false);
@@ -299,7 +315,7 @@ public class ConnectionActivity extends Activity implements View.OnClickListener
@Override
public void run() {
Uri notification = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
- Ringtone r = RingtoneManager.getRingtone(getApplicationContext(), notification);
+ Ringtone r = RingtoneManager. getRingtone(getApplicationContext(), notification);
r.play();
mBtnOpen.setEnabled(true);
@@ -322,9 +338,9 @@ public class ConnectionActivity extends Activity implements View.OnClickListener
mTextConnectionStatus.setText("Status: " + str + " connected");
if (null != mProduct.getModel()) {
- mTextProduct.setText("" + mProduct.getModel().getDisplayName());
+ mTextModelAvailable.setText("" + mProduct.getModel().getDisplayName());
} else {
- mTextProduct.setText(R.string.product_information);
+ mTextModelAvailable.setText("Model Not Available");
}
if (KeyManager.getInstance() != null) {
KeyManager.getInstance().addListener(firmkey, firmVersionListener);
@@ -356,7 +372,16 @@ public class ConnectionActivity extends Activity implements View.OnClickListener
lTextConnectionStatus.setText("TestMode");
mUIHandler = new Handler(Looper.getMainLooper());
mUIHandler.postDelayed(startApp, 50);
- hiddenkey = 6;
+ // hiddenkey = 6;
+ }
+ if (hiddenkey >= 10) {
+ showToast("TestMode disabled...");
+ TextView lTextConnectionStatus = (TextView) findViewById(R.id.text_model_test);
+ lTextConnectionStatus.setText("NormalMode");
+
+// mUIHandler = new Handler(Looper.getMainLooper());
+// mUIHandler.postDelayed(startApp, 50);
+ hiddenkey = 0;
}
break;
@@ -380,6 +405,7 @@ public class ConnectionActivity extends Activity implements View.OnClickListener
mBtnOpen.setEnabled(false);
unregisterReceiver(mReceiver);
+ Log.v(TAG, "Start Maintask");
Intent intent = new Intent(this, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivityIfNeeded(intent,0);
diff --git a/app/src/main/java/sq/rogue/rosettadrone/DroneModel.java b/app/src/main/java/sq/rogue/rosettadrone/DroneModel.java
index 9fdfa89..132b7c8 100644
--- a/app/src/main/java/sq/rogue/rosettadrone/DroneModel.java
+++ b/app/src/main/java/sq/rogue/rosettadrone/DroneModel.java
@@ -1,10 +1,14 @@
package sq.rogue.rosettadrone;
import android.app.AlertDialog;
+import android.content.Intent;
import android.content.SharedPreferences;
+import android.net.Uri;
+import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
+import android.widget.Toast;
import com.MAVLink.MAVLinkPacket;
import com.MAVLink.Messages.MAVLinkMessage;
@@ -13,16 +17,15 @@ import com.MAVLink.common.msg_attitude;
import com.MAVLink.common.msg_autopilot_version;
import com.MAVLink.common.msg_battery_status;
import com.MAVLink.common.msg_command_ack;
+import com.MAVLink.common.msg_file_transfer_protocol;
import com.MAVLink.common.msg_global_position_int;
import com.MAVLink.common.msg_gps_raw_int;
import com.MAVLink.common.msg_heartbeat;
import com.MAVLink.common.msg_home_position;
import com.MAVLink.common.msg_mission_ack;
import com.MAVLink.common.msg_mission_count;
-import com.MAVLink.common.msg_mission_item;
import com.MAVLink.common.msg_mission_item_int;
import com.MAVLink.common.msg_mission_item_reached;
-import com.MAVLink.common.msg_mission_request;
import com.MAVLink.common.msg_mission_request_int;
import com.MAVLink.common.msg_mission_request_list;
import com.MAVLink.common.msg_param_value;
@@ -45,6 +48,7 @@ import com.MAVLink.enums.MAV_RESULT;
import com.MAVLink.enums.MAV_STATE;
import com.MAVLink.enums.MAV_TYPE;
+import java.io.File;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
@@ -52,6 +56,8 @@ import java.net.PortUnreachableException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
import java.util.Objects;
import java.util.Timer;
import java.util.TimerTask;
@@ -88,14 +94,22 @@ import dji.common.model.LocationCoordinate2D;
import dji.common.product.Model;
import dji.common.remotecontroller.HardwareState;
import dji.common.util.CommonCallbacks;
+import dji.log.DJILog;
+import dji.sdk.base.BaseProduct;
import dji.sdk.battery.Battery;
+import dji.sdk.camera.Camera;
import dji.sdk.flightcontroller.FlightController;
import dji.sdk.gimbal.Gimbal;
+import dji.sdk.media.DownloadListener;
+import dji.sdk.media.FetchMediaTaskScheduler;
+import dji.sdk.media.MediaFile;
+import dji.sdk.media.MediaManager;
import dji.sdk.mission.MissionControl;
import dji.sdk.mission.followme.FollowMeMissionOperator;
import dji.sdk.mission.waypoint.WaypointMissionOperator;
import dji.sdk.products.Aircraft;
import dji.sdk.sdkmanager.DJISDKManager;
+import sq.rogue.rosettadrone.settings.MailReport;
import static com.MAVLink.common.msg_set_position_target_global_int.MAVLINK_MSG_ID_SET_POSITION_TARGET_GLOBAL_INT;
import static com.MAVLink.enums.MAV_CMD.MAV_CMD_COMPONENT_ARM_DISARM;
@@ -106,6 +120,7 @@ import static com.MAVLink.enums.MAV_CMD.MAV_CMD_NAV_TAKEOFF;
import static com.MAVLink.enums.MAV_CMD.MAV_CMD_VIDEO_START_CAPTURE;
import static com.MAVLink.enums.MAV_CMD.MAV_CMD_VIDEO_STOP_CAPTURE;
import static com.MAVLink.enums.MAV_COMPONENT.MAV_COMP_ID_AUTOPILOT1;
+import static com.google.android.material.snackbar.BaseTransientBottomBar.LENGTH_LONG;
import static sq.rogue.rosettadrone.util.getTimestampMicroseconds;
import static sq.rogue.rosettadrone.util.safeSleep;
@@ -180,6 +195,7 @@ public class DroneModel implements CommonCallbacks.CompletionCallback {
private FlightMode lastMode = FlightMode.ATTI_HOVER;
private HardwareState.FlightModeSwitch rcmode = HardwareState.FlightModeSwitch.POSITION_ONE;
private static HardwareState.FlightModeSwitch avtivemode = HardwareState.FlightModeSwitch.POSITION_ONE; // Change this to decide what position the mode switch should be inn.
+ public msg_home_position home_position = new msg_home_position();
private SendVelocityDataTask mSendVirtualStickDataTask = null;
private Timer mSendVirtualStickDataTimer = null;
@@ -199,16 +215,51 @@ public class DroneModel implements CommonCallbacks.CompletionCallback {
private Gimbal mGimbal = null;
private Rotation mRotation = null;
- private Model m_model;
+ private List m_mailToAddress = null;
+
+ public float mission_alt = 0;
+
+ Model m_model;
+ Camera m_camera;
+ Aircraft m_aircraft;
+
+ File destDir = new File(Environment.getExternalStorageDirectory().getPath() + "/DroneApp/");
+ String m_directory;
+
+ private MailReport SendMail;
private int mAIfunction_activation = 0;
public boolean mAutonomy = false;
public int mAirBorn = 0;
- int mission_loaded = -1;
+
+ public int mission_started = 0;
+
+ // FTP...
+ public List mediaFileList = new ArrayList();
+ public int numFiles;
+ private MediaManager mMediaManager;
+ private MediaManager.FileListState currentFileListState = MediaManager.FileListState.UNKNOWN;
+ private FetchMediaTaskScheduler scheduler;
+ public String last_downloaded_file;
+ public int lastDownloadedIndex = -1;
+ public int currentProgress = -1;
+
+ enum camera_mode {
+ IDLE,
+ SUCCSESS,
+ DISCONNECTED,
+ MEDIAMANAGER,
+ DOWNLOADMODESUPPORTED,
+ FAILURE,
+ TIMEOUT
+ }
+
+ public camera_mode switch_camera_mode = camera_mode.IDLE;
DroneModel(MainActivity parent, DatagramSocket socket, boolean sim) {
this.parent = parent;
this.socket = socket;
+ this.numFiles = 0;
initFlightController(sim);
}
@@ -235,19 +286,25 @@ public class DroneModel implements CommonCallbacks.CompletionCallback {
miniPIDSide = new MiniPID(0.35, 0.0, 0.0);
miniPIDFwd = new MiniPID(0.35, 0.0, 0.0);
- Aircraft aircraft = (Aircraft) RDApplication.getProductInstance(); //DJISimulatorApplication.getAircraftInstance();
- if (aircraft == null || !aircraft.isConnected()) {
+ //------------------------------------------------------------
+ // Prepare mail handler and add the catalog for images...
+ // m_directory = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/accident");
+ SendMail = new MailReport(parent,parent.getApplicationContext().getContentResolver());
+
+ m_aircraft = (Aircraft) RDApplication.getProductInstance(); //DJISimulatorApplication.getAircraftInstance();
+ if (m_aircraft == null || !m_aircraft.isConnected()) {
parent.logMessageDJI("No target...");
mFlightController = null;
return;
} else {
- m_model = aircraft.getModel();
+ m_model = m_aircraft.getModel();
if (m_model.equals("INSPIRE_1") || m_model.equals("INSPIRE_1_PRO") || m_model.equals("INSPIRE_1_RAW")) {
avtivemode = HardwareState.FlightModeSwitch.POSITION_THREE;
}
- mGimbal = aircraft.getGimbal();
- mFlightController = aircraft.getFlightController();
+ mGimbal = m_aircraft.getGimbal();
+ m_camera = RDApplication.getCameraInstance();
+ mFlightController = m_aircraft.getFlightController();
mFlightController.setRollPitchControlMode(RollPitchControlMode.VELOCITY);
mFlightController.setYawControlMode(YawControlMode.ANGULAR_VELOCITY);
mFlightController.setVerticalControlMode(VerticalControlMode.VELOCITY);
@@ -294,7 +351,7 @@ public class DroneModel implements CommonCallbacks.CompletionCallback {
// SetMesasageBox("Controller Ready!!!!!");
}
- LocationCoordinate3D getSimPos3D(){
+ LocationCoordinate3D getSimPos3D() {
sharedPreferences = android.preference.PreferenceManager.getDefaultSharedPreferences(parent.getApplicationContext());
LocationCoordinate3D pos = new LocationCoordinate3D(
Double.parseDouble(Objects.requireNonNull(sharedPreferences.getString("pref_sim_pos_lat", "-1"))),
@@ -303,24 +360,24 @@ public class DroneModel implements CommonCallbacks.CompletionCallback {
);
// If this is the first time the app is running...
- if(pos.getLatitude() == -1){
+ if (pos.getLatitude() == -1) {
sharedPreferences.getStringSet("pref_sim_pos_lat", Collections.singleton("60.4094"));
pos.setLatitude(60.4094);
}
- if(pos.getLongitude() == -1){
+ if (pos.getLongitude() == -1) {
sharedPreferences.getStringSet("pref_sim_pos_lon", Collections.singleton("10.4911"));
pos.setLongitude(10.4911);
}
- if(pos.getAltitude() == -1){ // Not Used...
+ if (pos.getAltitude() == -1) { // Not Used...
sharedPreferences.getStringSet("pref_sim_pos_alt", Collections.singleton("210.0"));
- pos.setAltitude((float)210.0);
+ pos.setAltitude((float) 210.0);
}
return (pos);
}
- LocationCoordinate2D getSimPos2D(){
+ LocationCoordinate2D getSimPos2D() {
LocationCoordinate3D pos3d = getSimPos3D();
- LocationCoordinate2D pos = new LocationCoordinate2D(pos3d.getLatitude(),pos3d.getLongitude());
+ LocationCoordinate2D pos = new LocationCoordinate2D(pos3d.getLatitude(), pos3d.getLongitude());
return (pos);
}
@@ -343,6 +400,17 @@ public class DroneModel implements CommonCallbacks.CompletionCallback {
});
}
+ // These are no long used as this is an internal process now, sending data to RemoteConfig...
+ void setAiIP(final String ip)
+ {}
+ void setAiPort(final int port)
+ {}
+ void setAIenable( final boolean enable)
+ {
+ parent.mMavlinkReceiver.AIenabled = enable;
+ parent.Set_ai_mode();
+ }
+
void setMaxHeight(final int height) {
djiAircraft.getFlightController().setMaxFlightHeight(height, djiError -> {
if (djiError == null) {
@@ -475,33 +543,33 @@ public class DroneModel implements CommonCallbacks.CompletionCallback {
}
void setWaypointMission(final WaypointMission wpMission) {
- mission_loaded = -1;
DJIError load_error = getWaypointMissionOperator().loadMission(wpMission);
if (load_error != null)
parent.logMessageDJI("loadMission() returned error: " + load_error.toString());
else {
+ // We can not wait until we know the results as it takes too long for QGC on large missions...
+ send_mission_ack(MAV_MISSION_RESULT.MAV_MISSION_ACCEPTED);
+
parent.logMessageDJI("Uploading mission");
getWaypointMissionOperator().uploadMission(
djiError -> {
if (djiError == null) {
while (getWaypointMissionOperator().getCurrentState() == WaypointMissionState.UPLOADING) {
- safeSleep(200);
+ safeSleep(100);
}
if (getWaypointMissionOperator().getCurrentState() == WaypointMissionState.READY_TO_EXECUTE) {
+ send_mission_ack(MAV_MISSION_RESULT.MAV_MISSION_ACCEPTED);
+ send_text("Mission uploaded and ready to execute!");
parent.logMessageDJI("Mission uploaded and ready to execute!");
- mission_loaded = MAV_MISSION_RESULT.MAV_MISSION_ACCEPTED;
- send_mission_ack(mission_loaded);
} else {
+ send_mission_ack(MAV_MISSION_RESULT.MAV_MISSION_ERROR);
+ send_text("Error uploading waypoint mission to drone.");
parent.logMessageDJI("Error uploading waypoint mission to drone.");
- mission_loaded = MAV_MISSION_RESULT.MAV_MISSION_ERROR;
- send_mission_ack(mission_loaded);
}
-
} else {
+ send_mission_ack(MAV_MISSION_RESULT.MAV_MISSION_ERROR);
+ send_text("\"Error uploading: \" + djiError.getDescription()");
parent.logMessageDJI("Error uploading: " + djiError.getDescription());
- parent.logMessageDJI(("Please try re-uploading"));
- mission_loaded = MAV_MISSION_RESULT.MAV_MISSION_ERROR;
- send_mission_ack(mission_loaded);
}
//parent.logMessageDJI("New state: " + getWaypointMissionOperator().getCurrentState().getName());
});
@@ -594,16 +662,13 @@ public class DroneModel implements CommonCallbacks.CompletionCallback {
mControllerVoltage_pr = (batteryState.getRemainingChargeInPercent());
if (mControllerVoltage_pr > 90) {
lastState = 100;
- }
- else if (mControllerVoltage_pr < 20 && lastState == 100) {
+ } else if (mControllerVoltage_pr < 20 && lastState == 100) {
lastState = 20;
SetMesasageBox("Controller Battery Warning 20% !!!!!");
- }
- else if (mControllerVoltage_pr < 10 && lastState == 20) {
+ } else if (mControllerVoltage_pr < 10 && lastState == 20) {
lastState = 10;
SetMesasageBox("Controller Battery Warning 10% !!!!!");
- }
- else if (mControllerVoltage_pr < 5 && lastState == 10) {
+ } else if (mControllerVoltage_pr < 5 && lastState == 10) {
lastState = 5;
SetMesasageBox("Controller Battery Warning 5% !!!!!");
}
@@ -631,16 +696,13 @@ public class DroneModel implements CommonCallbacks.CompletionCallback {
if (mCVoltage_pr > 90) {
mlastState = 100;
- }
- else if (mCVoltage_pr <= 20 && mlastState == 100) {
+ } else if (mCVoltage_pr <= 20 && mlastState == 100) {
mlastState = 20;
SetMesasageBox("Drone Battery Warning 20% !!!!!");
- }
- else if (mCVoltage_pr <= 10 && mlastState == 20) {
+ } else if (mCVoltage_pr <= 10 && mlastState == 20) {
mlastState = 10;
SetMesasageBox("Drone Battery Warning 10% !!!!!");
- }
- else if (mCVoltage_pr <= 5 && mlastState == 10) {
+ } else if (mCVoltage_pr <= 5 && mlastState == 10) {
mlastState = 5;
SetMesasageBox("Drone Battery Warning 5% !!!!!");
}
@@ -742,6 +804,7 @@ public class DroneModel implements CommonCallbacks.CompletionCallback {
void armMotors() {
if (mSafetyEnabled) {
+ send_text(parent.getResources().getString(R.string.safety_launch));
parent.logMessageDJI(parent.getResources().getString(R.string.safety_launch));
send_command_ack(MAV_CMD_COMPONENT_ARM_DISARM, MAV_RESULT.MAV_RESULT_DENIED);
} else {
@@ -822,6 +885,9 @@ public class DroneModel implements CommonCallbacks.CompletionCallback {
msg_autopilot_version msg = new msg_autopilot_version();
msg.capabilities = MAV_PROTOCOL_CAPABILITY.MAV_PROTOCOL_CAPABILITY_COMMAND_INT;
msg.capabilities |= MAV_PROTOCOL_CAPABILITY.MAV_PROTOCOL_CAPABILITY_MISSION_INT;
+ msg.os_sw_version = 0x040107;
+ msg.middleware_sw_version = 0x040107;
+ msg.flight_sw_version = 0x040107;
sendMessage(msg);
}
@@ -989,7 +1055,7 @@ public class DroneModel implements CommonCallbacks.CompletionCallback {
private void send_altitude() {
msg_altitude msg = new msg_altitude();
LocationCoordinate3D coord = djiAircraft.getFlightController().getState().getAircraftLocation();
- msg.altitude_relative = (int) (coord.getAltitude() * 1000);
+ msg.altitude_relative = (float) (coord.getAltitude() ); //* 1000);
m_alt = msg.altitude_relative;
sendMessage(msg);
}
@@ -1014,6 +1080,67 @@ public class DroneModel implements CommonCallbacks.CompletionCallback {
sendMessage(msg);
}
+ void send_command_ftp_string_ack(int message_id, String data, byte[] header) {
+ msg_file_transfer_protocol msg = new msg_file_transfer_protocol();
+ byte[] byteString = data.getBytes();
+ short[] shortArray = new short[251];
+
+
+ if (header != null) {
+ for (int i = 0; i < header.length; i++) {
+ short s = (short) (header[i] & 0xFF);
+ shortArray[i] = s;
+ }
+ }
+
+ int sizeCounter = 0;
+ for (int i = 0; i < byteString.length; i++) {
+ int added = 12 + i;
+ short s = byteString[i];
+ shortArray[added] = s;
+ sizeCounter += 1;
+ }
+ shortArray[3] = (short) 128;
+ shortArray[4] = (short) sizeCounter;
+ msg.payload = shortArray;
+ msg.msgid = message_id;
+
+ sendMessage(msg);
+ }
+
+ void send_command_ftp_bytes_ack(int message_id, byte[] data) {
+ msg_file_transfer_protocol msg = new msg_file_transfer_protocol();
+ short[] shortArray = new short[data.length];
+ for (int i = 0; i < data.length; i++) {
+ short s = (short) (data[i] & 0xFF);
+ shortArray[i] = s;
+ }
+
+ shortArray[3] = (short) 128;
+
+ msg.payload = shortArray;
+ msg.msgid = message_id;
+
+ sendMessage(msg);
+ }
+
+ void send_command_ftp_nak(int message_id, int errorCode, int failCode) {
+ msg_file_transfer_protocol msg = new msg_file_transfer_protocol();
+ short[] shortArray = new short[251];
+
+ shortArray[3] = (short) 129; //nak code
+ if (failCode != 0) {
+ shortArray[4] = (short) 2; //error size
+ shortArray[13] = (short) failCode;
+ } else {
+ shortArray[4] = (short) 1; //error size
+ }
+ shortArray[12] = (short) errorCode; // the error code
+
+ msg.msgid = message_id;
+ sendMessage(msg);
+ }
+
private void send_global_position_int() {
msg_global_position_int msg = new msg_global_position_int();
@@ -1021,6 +1148,9 @@ public class DroneModel implements CommonCallbacks.CompletionCallback {
msg.lat = (int) (coord.getLatitude() * Math.pow(10, 7));
msg.lon = (int) (coord.getLongitude() * Math.pow(10, 7));
+ // This is a bad idea, but the best we can do...
+ msg.alt = (int) ((coord.getAltitude()+mission_alt) * Math.pow(10, 3));
+
// NOTE: Commented out this field, because msg.relative_alt seems to be intended for altitude above the current terrain,
// but DJI reports altitude above home point.
// Mavlink: Millimeters above ground (unspecified: presumably above home point?)
@@ -1048,20 +1178,42 @@ public class DroneModel implements CommonCallbacks.CompletionCallback {
}
public double get_current_lat() {
+ if (mFlightController == null) {
+ Log.d(TAG, "getSimPos3D().getLatitude()="+getSimPos3D().getLatitude());
+ return getSimPos3D().getLatitude();
+ }
+
LocationCoordinate3D coord = djiAircraft.getFlightController().getState().getAircraftLocation();
+ Log.d(TAG, "coord.getLatitude()="+coord.getLatitude());
return coord.getLatitude();
}
public double get_current_lon() {
+ if (mFlightController == null)
+ return getSimPos3D().getLongitude();
+
LocationCoordinate3D coord = djiAircraft.getFlightController().getState().getAircraftLocation();
return coord.getLongitude();
}
public float get_current_alt() {
+ if (mFlightController == null)
+ return getSimPos3D().getAltitude();
+
LocationCoordinate3D coord = djiAircraft.getFlightController().getState().getAircraftLocation();
return coord.getAltitude();
}
+ public float get_current_head() {
+ if (mFlightController == null)
+ return (float) 123.45;
+
+ double yaw = djiAircraft.getFlightController().getState().getAttitude().yaw;
+ if (yaw < 0)
+ yaw += 360;
+ return (float) yaw;
+ }
+
private void send_gps_raw_int() {
msg_gps_raw_int msg = new msg_gps_raw_int();
@@ -1146,8 +1298,11 @@ public class DroneModel implements CommonCallbacks.CompletionCallback {
mAIfunction_activation = 0;
}
- mAutonomy = false;
- // pauseWaypointMission(); // TODO:: halt mission for safety...
+ mAutonomy = false; // TODO:: This variable needs to be checked elseware in the code...
+
+ if(mission_started == 1) {
+ pauseWaypointMission(); // TODO:: halt mission for safety...
+ }
}
msg.chan8_raw = (mAIfunction_activation * 100) + 1000;
@@ -1210,12 +1365,10 @@ public class DroneModel implements CommonCallbacks.CompletionCallback {
}
void send_home_position() {
- msg_home_position msg = new msg_home_position();
-
- msg.latitude = (int) (djiAircraft.getFlightController().getState().getHomeLocation().getLatitude() * Math.pow(10, 7));
- msg.longitude = (int) (djiAircraft.getFlightController().getState().getHomeLocation().getLongitude() * Math.pow(10, 7));
- // msg.altitude = (int) (djiAircraft.getFlightController().getState().getHomePointAltitude());
- msg.altitude = (int) (djiAircraft.getFlightController().getState().getGoHomeHeight());
+ home_position.latitude = (int) (djiAircraft.getFlightController().getState().getHomeLocation().getLatitude() * Math.pow(10, 7));
+ home_position.longitude = (int) (djiAircraft.getFlightController().getState().getHomeLocation().getLongitude() * Math.pow(10, 7));
+ home_position.altitude = (int) (djiAircraft.getFlightController().getState().getTakeoffLocationAltitude());
+ //home_position.altitude = (int) (djiAircraft.getFlightController().getState().getGoHomeHeight());
// msg.x = 0;
// msg.y = 0;
@@ -1223,7 +1376,7 @@ public class DroneModel implements CommonCallbacks.CompletionCallback {
// msg.approach_x = 0;
// msg.approach_y = 0;
// msg.approach_z = 0;
- sendMessage(msg);
+ sendMessage(home_position);
}
void set_home_position(double lat, double lon) {
@@ -1423,19 +1576,18 @@ public class DroneModel implements CommonCallbacks.CompletionCallback {
msg.mission_type = MAV_MISSION_TYPE.MAV_MISSION_TYPE_MISSION;
// Get the full expanded list...
msg.count = send_mission_item(-1);
- Log.d(TAG, "Mission Count: "+msg.count);
+ Log.d(TAG, "Mission Count: " + msg.count);
sendMessage(msg);
}
// This is a bit tricky, as DJI and MAVlink pack mission ithems very different...
// Firste we expand the full mission list and then we pick the one we need...
- int send_mission_item(int i)
- {
+ int send_mission_item(int i) {
ArrayList msglist = new ArrayList<>();
WaypointMission x = getWaypointMissionOperator().getLoadedMission();
// If a mission loaded...
- if(x!=null) {
+ if (x != null) {
// For all DJI mission items...
for (Waypoint wp : Objects.requireNonNull(x).getWaypointList()) {
@@ -1523,10 +1675,10 @@ public class DroneModel implements CommonCallbacks.CompletionCallback {
}
}
}
- if(i>=0)
+ if (i >= 0)
sendMessage(msglist.get(i));
}
- Log.d(TAG, "Mission return: "+i);
+ Log.d(TAG, "Mission return: " + i);
return (msglist.size());
}
@@ -1545,6 +1697,18 @@ public class DroneModel implements CommonCallbacks.CompletionCallback {
sendMessage(msg);
}
+ // Send mission accepted back to Mavlink...
+ void send_text(String status) {
+ parent.logMessageDJI("Mission status: " + status);
+ msg_statustext msg = new msg_statustext();
+ int i = 0;
+ for( char x:status.toCharArray()){
+ msg.text[i++]=(byte)x;
+ }
+ sendMessage(msg);
+ }
+
+
public void fetch_gcs_mission() {
request_mission_list();
}
@@ -1567,12 +1731,14 @@ public class DroneModel implements CommonCallbacks.CompletionCallback {
void startWaypointMission() {
mAutonomy = false;
+ mission_started = 1;
parent.logMessageDJI("start WaypointMission()");
if (getWaypointMissionOperator() == null) {
parent.logMessageDJI("start WaypointMission() - WaypointMissionOperator null");
return;
}
+
if (getWaypointMissionOperator().getCurrentState() == WaypointMissionState.READY_TO_EXECUTE) {
parent.logMessageDJI("Ready to execute mission!\n");
} else {
@@ -1582,7 +1748,11 @@ public class DroneModel implements CommonCallbacks.CompletionCallback {
}
if (mSafetyEnabled) {
parent.logMessageDJI("You must turn off the safety to start mission");
+ send_text("You must turn off the safety to start mission");
+
} else {
+ // Set default camera angle at 45deg down...
+ do_set_Gimbal(9, (float)((-45.0*5.5)+1500.0));
getWaypointMissionOperator().startMission(djiError -> {
if (djiError != null)
parent.logMessageDJI("Error: " + djiError.toString());
@@ -1594,6 +1764,7 @@ public class DroneModel implements CommonCallbacks.CompletionCallback {
public void stopWaypointMission() {
mAutonomy = false;
+ mission_started = 0;
if (getWaypointMissionOperator() == null) {
parent.logMessageDJI("stopWaypointMission() - mWaypointMissionOperator null");
return;
@@ -1612,6 +1783,7 @@ public class DroneModel implements CommonCallbacks.CompletionCallback {
void pauseWaypointMission() {
mAutonomy = false;
+ mission_started = 0;
if (getWaypointMissionOperator() == null) {
parent.logMessageDJI("pauseWaypointMission() - mWaypointMissionOperator null");
return;
@@ -1630,11 +1802,6 @@ public class DroneModel implements CommonCallbacks.CompletionCallback {
void resumeWaypointMission() {
mAutonomy = false;
- if (isSafetyEnabled()) {
- parent.logMessageDJI(parent.getResources().getString(R.string.safety_launch));
- send_command_ack(MAV_CMD_NAV_TAKEOFF, MAV_RESULT.MAV_RESULT_DENIED);
- return;
- }
if (getWaypointMissionOperator() == null) {
parent.logMessageDJI("resumeWaypointMission() - mWaypointMissionOperator null");
@@ -1648,6 +1815,7 @@ public class DroneModel implements CommonCallbacks.CompletionCallback {
parent.logMessageDJI("Error: " + djiError.toString());
else {
parent.logMessageDJI("Mission resumed!\n");
+ mission_started = 1;
mGCSCommandedMode = NOT_USING_GCS_COMMANDED_MODE;
}
});
@@ -1661,6 +1829,7 @@ public class DroneModel implements CommonCallbacks.CompletionCallback {
if (mSafetyEnabled) {
parent.logMessageDJI(parent.getResources().getString(R.string.safety_launch));
+ send_text(parent.getResources().getString(R.string.safety_launch));
send_command_ack(MAV_CMD_NAV_TAKEOFF, MAV_RESULT.MAV_RESULT_DENIED);
return;
}
@@ -1672,84 +1841,28 @@ public class DroneModel implements CommonCallbacks.CompletionCallback {
send_command_ack(MAV_CMD_NAV_TAKEOFF, MAV_RESULT.MAV_RESULT_DENIED);
return;
}
-/*
- if (getWaypointMissionOperator().getCurrentState() == WaypointMissionState.READY_TO_EXECUTE) {
- startWaypointMission();
- send_command_ack(MAV_CMD_NAV_TAKEOFF, MAV_RESULT.MAV_RESULT_ACCEPTED);
- } else {
- parent.logMessageDJI("Initiating takeoff");
- djiAircraft.getFlightController().startTakeoff(djiError -> {
- if (djiError != null) {
- parent.logMessageDJI("Error: " + djiError.toString());
- // send_command_ack(MAV_CMD_NAV_TAKEOFF, MAV_RESULT.MAV_RESULT_FAILED);
- mAirBorn = 1;
- } else {
- parent.logMessageDJI("Takeoff successful!\n");
- // send_command_ack(MAV_CMD_NAV_TAKEOFF, MAV_RESULT.MAV_RESULT_ACCEPTED);
- mAirBorn = 2;
- }
- mGCSCommandedMode = NOT_USING_GCS_COMMANDED_MODE;
- });
+
+ if (getWaypointMissionOperator().getCurrentState() == WaypointMissionState.EXECUTION_PAUSED) {
+ resumeWaypointMission();
}
-
- // Wait for command to be executed...
- Log.d(TAG,"Wait for takeoff...");
- while( mAirBorn == 0) {
- safeSleep(250);
- }
-
- // Check if ok or failed...
- if(mAirBorn == 1) {
- Log.d(TAG,"Takeoff failed...");
- send_command_ack(MAV_CMD_NAV_TAKEOFF, MAV_RESULT.MAV_RESULT_FAILED);
- }
- else{
- send_command_ack(MAV_CMD_NAV_TAKEOFF, MAV_RESULT.MAV_RESULT_IN_PROGRESS);
-
- int timeout = 0;
-
- // If we took off, wait for command co complete...
- Log.d(TAG,"Wait for first altitude...");
- while (m_alt < 1000.0 || timeout > (20*(1/0.250))) {
- timeout+= 1;
- safeSleep(250);
+ else {
+ if (getWaypointMissionOperator().getCurrentState() == WaypointMissionState.READY_TO_EXECUTE) {
+ Log.d(TAG, "Start Mission...");
+ // But how about takeoff....
+ startWaypointMission();
+ send_command_ack(MAV_CMD_NAV_TAKEOFF, MAV_RESULT.MAV_RESULT_ACCEPTED);
+ } else {
+ FlightControllerState coord = djiAircraft.getFlightController().getState();
+ TimeLine.TimeLinetakeOff(coord.getAircraftLocation().getLatitude(), coord.getAircraftLocation().getLongitude(), alt, 0);
+ TimeLine.startTimeline();
+ Log.d(TAG, "Takeoff started...");
}
-
- Log.d(TAG,"Takeoff success...");
- safeSleep(500);
-
- timeout = 0;
- // If we are airborne, or was airborne from the start...
- if (m_alt >= 800) {
- float desired_alt = alt - m_alt;
- Log.d(TAG, "Climb out...from: "+m_alt+" to: "+desired_alt+" set: "+alt);
- // Climb out to 99% of requested altitude, however 100% is set, just to avoid deadlock...
- while (m_alt < desired_alt || timeout > (30*(1/0.250))){
- do_set_motion_relative(MAV_CMD_NAV_TAKEOFF, 0, 0, (alt - m_alt) / (float) 1000.0, 0, 0, 0, 0, 0, 0b00011111111000);
- timeout+= 1;
- safeSleep(250);
- }
- Log.d(TAG, "Climb out completed..."+m_alt);
- }else{
- Log.d(TAG, "Climb out failed...");
- }
- }
-
- */
- if (getWaypointMissionOperator().getCurrentState() == WaypointMissionState.READY_TO_EXECUTE) {
- // But how about takeoff....
- startWaypointMission();
- send_command_ack(MAV_CMD_NAV_TAKEOFF, MAV_RESULT.MAV_RESULT_ACCEPTED);
- } else {
- FlightControllerState coord = djiAircraft.getFlightController().getState();
- TimeLine.TimeLinetakeOff(coord.getAircraftLocation().getLatitude(), coord.getAircraftLocation().getLongitude(), alt, 0);
- TimeLine.startTimeline();
- Log.d(TAG, "Takeoff started...");
}
}
void do_land() {
mAutonomy = false;
+ mission_started = 0;
parent.logMessageDJI("Initiating landing");
djiAircraft.getFlightController().startLanding(djiError -> {
if (djiError != null)
@@ -1764,6 +1877,7 @@ public class DroneModel implements CommonCallbacks.CompletionCallback {
void do_go_home() {
mAutonomy = false;
+ mission_started = 0;
parent.logMessageDJI("Initiating Go Home");
djiAircraft.getFlightController().startGoHome(djiError -> {
if (djiError != null)
@@ -1776,7 +1890,6 @@ public class DroneModel implements CommonCallbacks.CompletionCallback {
/********************************************
* Motion implementation *
********************************************/
-
void do_set_Gimbal(float channel, float value) {
Rotation.Builder builder = new Rotation.Builder().mode(RotationMode.ABSOLUTE_ANGLE).time(2);
float param = (value - (float) 1500.0) / (float) 5.5;
@@ -1797,7 +1910,7 @@ public class DroneModel implements CommonCallbacks.CompletionCallback {
}
m_ServoSet = builder.build();
-
+ Log.e(TAG, "Set Gimbal Pos: " + value + " " +m_ServoPos_pitch + " : " + m_ServoPos_yaw);
mGimbal.rotate(m_ServoSet, djiError -> {
if (djiError != null)
parent.logMessageDJI("Error: " + djiError.toString());
@@ -2477,4 +2590,240 @@ public class DroneModel implements CommonCallbacks.CompletionCallback {
parent.logMessageDJI("Was: lat " + lat + " lon " + lon + " newlat " + newlat + " newlon " + newlon);
return new double[]{newlat, newlon};
}
+
+//---------------------------------------------------------------------------------------
+//---------------------------------------------------------------------------------------
+//---------------------------------------------------------------------------------------
+// FTP and file related functions
+ public void initMediaManager(List address)
+ {
+ m_mailToAddress = address;
+ DJILog.e(TAG, "Addresses: "+address.toString());
+
+ parent.logMessageDJI("Image retrival started...");
+ switch_camera_mode = camera_mode.IDLE;
+
+ if (m_aircraft == null)
+ {
+ mediaFileList.clear();
+ DJILog.e(TAG, "Product disconnected");
+ switch_camera_mode = camera_mode.DISCONNECTED;
+ return;
+
+ } else {
+ if (null != m_camera && m_camera.isMediaDownloadModeSupported())
+ {
+ mMediaManager = m_camera.getMediaManager();
+
+ // Is actually of no interest...
+ if(!m_camera.isSSDSupported()){
+ parent.logMessageDJI("Internal ssd is not suported");
+ }
+ if (null != mMediaManager) {
+ mMediaManager.addUpdateFileListStateListener(this.updateFileListStateListener);
+ //switchCameraMode(SettingsDefinitions.CameraMode.MEDIA_DOWNLOAD);
+
+ Objects.requireNonNull(RDApplication.getCameraInstance()).setMode(SettingsDefinitions.CameraMode.MEDIA_DOWNLOAD, error -> {
+ if (error == null) {
+ parent.logMessageDJI("Set camera to MEDIA_DOWNLOAD success");
+ getFileList();
+ } else {
+ parent.logMessageDJI("Set camera to MEDIA_DOWNLOAD failed");
+ parent.logMessageDJI(error.toString());
+ }
+ });
+
+ // Is actually of no interest...
+ if (mMediaManager.isVideoPlaybackSupported()) {
+ parent.logMessageDJI("Camera support video playback!");
+ } else {
+ parent.logMessageDJI("Camera does not support video playback!");
+ }
+
+ scheduler = mMediaManager.getScheduler();
+ } else{
+ switch_camera_mode = camera_mode.MEDIAMANAGER;
+ }
+ } else if (null != m_camera && !m_camera.isMediaDownloadModeSupported()) {
+ parent.logMessageDJI("Media Download Mode not Supported");
+ switch_camera_mode = camera_mode.DOWNLOADMODESUPPORTED;
+ }
+ }
+ return;
+ }
+
+ public void getFileList() {
+ BaseProduct product = null;
+ if (RDApplication.getSim() == false) product = m_aircraft;
+
+ if (product != null){
+ if(product.isConnected()) {
+ mMediaManager = m_camera.getMediaManager();
+ if (mMediaManager != null) {
+ parent.logMessageDJI(currentFileListState.name());
+ if ((currentFileListState == MediaManager.FileListState.SYNCING) || (currentFileListState == MediaManager.FileListState.DELETING)) {
+ DJILog.e(TAG, "Media Manager is busy.");
+ parent.logMessageDJI("Media Manager is busy.");
+ } else {
+ mMediaManager.refreshFileListOfStorageLocation(SettingsDefinitions.StorageLocation.SDCARD, djiError -> {
+ if (null == djiError) {
+ //Reset data
+ if (currentFileListState != MediaManager.FileListState.INCOMPLETE) {
+ mediaFileList.clear();
+ }
+ mediaFileList = mMediaManager.getSDCardFileListSnapshot();
+ Collections.sort(mediaFileList, (lhs, rhs) -> {
+ if (lhs.getTimeCreated() < rhs.getTimeCreated()) {
+ return -1;
+ } else if (lhs.getTimeCreated() > rhs.getTimeCreated()) {
+ return 1;
+ }
+ return 0;
+ });
+ scheduler.resume(error -> {
+ if (error == null) {
+ }
+ });
+
+ if(numFiles != mediaFileList.size()){
+ numFiles = mediaFileList.size();
+ }
+
+ parent.logMessageDJI("Got Media Files: " + mediaFileList.size());
+ for(int i=0; i 0) {
+ downloadFileByIndex(numFiles - 1);
+ }
+ }
+ }
+ }
+ };
+ public void downloadFileByName(final String name) {
+ for(int i=0; i() {
+ @Override
+ public void onFailure(DJIError error) {
+ parent.logMessageDJI( "Download File Failed" + error.getDescription());
+ }
+
+ @Override
+ public void onProgress(long total, long current) {
+ }
+
+ @Override
+ public void onRateUpdate(long total, long current, long persize) {
+ int tmpProgress = (int) (1.0 * current / total * 100);
+ if (tmpProgress != currentProgress) {
+ currentProgress = tmpProgress;
+ }
+ }
+
+ @Override
+ public void onRealtimeDataUpdate(byte[] bytes, long l, boolean b) {
+ }
+
+ @Override
+ public void onStart() {
+ currentProgress = 0;
+ }
+
+ @Override
+ public void onSuccess(String filePath) {
+ m_directory = filePath;
+ parent.logMessageDJI( "Download File Success: " + filePath + "/" + mediaFileList.get(index).getFileName());
+// last_downloaded_file = filePath+ "/" + mediaFileList.get(index).getFileName();
+ last_downloaded_file = mediaFileList.get(index).getFileName();
+
+ RDApplication.getCameraInstance().setMode(SettingsDefinitions.CameraMode.SHOOT_PHOTO, error -> {
+ if (error == null) {
+ parent.logMessageDJI("Set camera to SHOOT_PHOTO success");
+ sendmail(last_downloaded_file);
+ NotificationHandler.notifySnackbar(parent.findViewById(R.id.snack),R.string.release, LENGTH_LONG);
+ } else {
+ parent.logMessageDJI("Set camera to SHOOT_PHOTO failed");
+ parent.logMessageDJI(error.toString());
+ }
+ });
+ lastDownloadedIndex = index;
+ }
+ });
+ return true;
+ }
+
+ //--------------------------------------------------------------
+ void sendmail(String file_toSend)
+ {
+ Log.i(TAG, "File to send: "+file_toSend);
+ try {
+ Intent email = SendMail.createEmail(
+ m_mailToAddress,
+ "Status report",
+ "There is an issue: ",
+ get_current_lat(),
+ get_current_lon(),
+ get_current_alt(),
+ get_current_head(),
+ file_toSend,
+ m_directory
+ );
+
+ if(email != null) {
+ try {
+ parent.startActivity(Intent.createChooser(email, "Send mail..."));
+ Log.i(TAG, "Finished sending email...");
+ } catch (android.content.ActivityNotFoundException ex) {
+ Toast.makeText(parent, "There is no email client installed.", Toast.LENGTH_SHORT).show();
+ }
+ }
+ }catch (Exception e) {
+ Toast.makeText(parent, "Can not send email: "+ e.toString(), Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ void onFileListStateChange(MediaManager.FileListState state){
+ parent.logMessageDJI( "Files changed?");
+ }
+ // FTP and file related functions
+ //---------------------------------------------------------------------------------------
}
\ No newline at end of file
diff --git a/app/src/main/java/sq/rogue/rosettadrone/FTPManager.java b/app/src/main/java/sq/rogue/rosettadrone/FTPManager.java
new file mode 100644
index 0000000..af1dae0
--- /dev/null
+++ b/app/src/main/java/sq/rogue/rosettadrone/FTPManager.java
@@ -0,0 +1,234 @@
+package sq.rogue.rosettadrone;
+
+import com.MAVLink.common.msg_file_transfer_protocol;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.math.BigInteger;
+import java.nio.ByteBuffer;
+
+import static com.MAVLink.common.msg_file_transfer_protocol.MAVLINK_MSG_ID_FILE_TRANSFER_PROTOCOL;
+import static sq.rogue.rosettadrone.util.safeSleep;
+
+public class FTPManager {
+ private MainActivity parent;
+ private DroneModel mModel;
+ private File currentFile;
+ private byte[][] currentFileInPacket;
+
+ public FTPManager(MainActivity parent, DroneModel mModel) {
+ this.parent = parent;
+ this.mModel = mModel;
+ this.currentFile = null;
+ this.currentFileInPacket = null;
+ }
+
+ public void manage_ftp(msg_file_transfer_protocol msg_ftp_item){
+ int session_id = new Short(msg_ftp_item.payload[2]).intValue();
+ int opCode = new Short(msg_ftp_item.payload[3]).intValue();
+ int offset = getOffset(msg_ftp_item.payload);
+// DEBUG
+// parent.logMessageDJI("opCode: " + opCode);
+// parent.logMessageDJI("session_id: " + session_id);
+// parent.logMessageDJI("Offset: " + offset);
+// parent.logMessageDJI("code was " + opCode);
+ switch (opCode) {
+ case 3: // Return list
+ fetchFiles(offset);
+ break;
+ case 4: // Open file for reading
+ int file_id = new Short(msg_ftp_item.payload[12]).intValue();
+ openFile(file_id, session_id);
+ break;
+ case 5: // Read file
+ readFile(session_id, offset, msg_ftp_item.payload);
+ break;
+ case 8: // Remove file
+ break;
+ }
+ }
+ public int getOffset(short[] payload){
+// parent.logMessageDJI("CAME HERE!");
+ byte[] byteArray = new byte[4];
+
+ for(int i = 0; i <= 3; i++){
+ int added = 8 + i;
+ short x = new Short(payload[added]);
+ byteArray[i] = (byte)(x & 0xff);
+ };
+
+ int offset = ByteBuffer.wrap(byteArray).getInt();
+ return offset;
+ }
+
+ public void fetchFiles(int offset){
+
+ mModel.initMediaManager(null);
+ // TODO: Wait for answer hmm will this work???...
+ do {
+ safeSleep(500);
+ }while(mModel.switch_camera_mode == DroneModel.camera_mode.IDLE);
+
+ parent.logMessageDJI("Getting files");
+ //parent.getFilesDir();
+ if(parent.mediaFileList.size() < offset) {
+ mModel.send_command_ftp_nak(MAVLINK_MSG_ID_FILE_TRANSFER_PROTOCOL, 10, 0);
+ } else {
+ String dir_items = "";
+ String add_dir = "";
+ for (int i = offset; i < parent.mediaFileList.size(); i++) {
+ add_dir = "F" + parent.mediaFileList.get(i).getFileName() + "\\t" + parent.mediaFileList.get(i).getFileSize() + "\\0";
+ if (dir_items.getBytes().length + add_dir.getBytes().length < (251 - 12)) {
+ dir_items += add_dir;
+ }
+ }
+ parent.logMessageDJI("total: " + dir_items.getBytes().length + ": " + dir_items);
+ mModel.send_command_ftp_string_ack(MAVLINK_MSG_ID_FILE_TRANSFER_PROTOCOL, dir_items, null);
+ }
+ }
+
+ public void openFile(int file_id, int session_id){
+ parent.logMessageDJI("Download file by index " + file_id);
+ if(parent.lastDownloadedIndex != file_id) {
+ mModel.downloadFileByIndex(file_id);
+ // wait for done
+ while (parent.mModel.currentProgress != -1){
+ safeSleep(1000);
+ parent.logMessageDJI("Waiting for current progress: " + parent.mModel.currentProgress);
+ };
+ }
+ if(!parent.downloadError){
+ parent.logMessageDJI("No error while downloading");
+ currentFileInPacket = new byte[0][0];
+ currentFile = null;
+ parent.logMessageDJI("Reset currentfile and currentfileinpackets");
+ byte[] header = new byte[12];
+ header[2] = (byte)session_id;
+ header[4] = 4;
+ parent.logMessageDJI(parent.last_downloaded_file);
+ try {
+ currentFile = new File(parent.last_downloaded_file);
+ } catch (Exception e){
+ parent.logMessageDJI(e.toString());
+ }
+ // convert to byte array
+ try (InputStream is = new FileInputStream(currentFile)) {
+ if (currentFile.length() > Integer.MAX_VALUE) {
+ mModel.send_command_ftp_nak(MAVLINK_MSG_ID_FILE_TRANSFER_PROTOCOL, 1, 0);
+ parent.logMessageDJI("File to big!");
+ }
+ parent.logMessageDJI("read file to bytes...");
+
+ byte[] fetchedFile = readFileToBytes(currentFile);
+ // byte [x,2,e,d,f,ge,e,e,s,df,s]
+
+ parent.logMessageDJI("Current file in bytes: " + fetchedFile.length);
+
+ int payload_data_size = (251-12);
+ int total_file_size = (int) currentFile.length();
+ float total_packets_float = (float) total_file_size/payload_data_size;
+ int total_packets = (int) Math.ceil(total_packets_float);
+ int bytes_to_add_size = 0;
+ int current_byte = 0;
+
+ parent.logMessageDJI("Getting packets: " + total_packets);
+ currentFileInPacket = new byte[total_packets][];
+ try {
+ for (int i = 0; i < total_packets; i++) {
+ current_byte = payload_data_size * i; // 5.802.442 (+13 = 5.802.455)
+ bytes_to_add_size = payload_data_size; //239
+ if (current_byte + payload_data_size > fetchedFile.length) {
+ bytes_to_add_size = fetchedFile.length - current_byte;
+ parent.logMessageDJI("Getting a total of last byte size " + bytes_to_add_size); //13
+ }
+ currentFileInPacket[i] = new byte[bytes_to_add_size];
+
+ for (int j = 0; j < bytes_to_add_size; j++) {
+ currentFileInPacket[i][j] = fetchedFile[current_byte + j];
+ }
+ }
+ parent.logMessageDJI("Current file in bytes: " + total_file_size + ", total packets: " + total_packets);
+ } catch (Exception e){
+ parent.logMessageDJI("Exception: " + e.toString());
+ }
+
+ } catch (FileNotFoundException e) {
+ mModel.send_command_ftp_nak(MAVLINK_MSG_ID_FILE_TRANSFER_PROTOCOL, 10, 0);
+ parent.logMessageDJI("FileNotFoundException: " + e.toString());
+ return;
+ } catch (IOException e) {
+ mModel.send_command_ftp_nak(MAVLINK_MSG_ID_FILE_TRANSFER_PROTOCOL, 1, 0);
+ parent.logMessageDJI("IOException: " + e.toString());
+ return;
+ }
+ int file_size = (int)currentFile.length();
+ parent.logMessageDJI("File size: " + file_size);
+ mModel.send_command_ftp_string_ack(MAVLINK_MSG_ID_FILE_TRANSFER_PROTOCOL, file_size + "", null);
+ parent.logMessageDJI("Opened image " + currentFile.getName());
+ } else {
+ parent.logMessageDJI("There was an error");
+ mModel.send_command_ftp_nak(MAVLINK_MSG_ID_FILE_TRANSFER_PROTOCOL, 1, 0);
+ }
+ }
+
+ public void readFile(int session_id, int offset, short[] payload){
+ try { //remove this try catch block after debugging
+ int bytes_to_add_size = currentFileInPacket[offset].length;
+ int bytes_with_header_size = bytes_to_add_size + 12;
+
+ byte[] data = new byte[bytes_with_header_size];
+
+// Set offset from original payload
+ for (int i = 8; i <= 12; i++) {
+ short x = new Short(payload[i]);
+ data[i] = (byte) (x & 0xff);
+ }
+// Set session
+ data[2] = (byte) session_id;
+ data[4] = (byte) bytes_to_add_size;
+
+ if (currentFile == null || currentFile.length() == 0) {
+ parent.logMessageDJI("File is null, sending error code");
+ mModel.send_command_ftp_nak(MAVLINK_MSG_ID_FILE_TRANSFER_PROTOCOL, 10, 0);
+ return;
+ }
+ if (offset > (currentFileInPacket.length)) {
+ parent.logMessageDJI("offset > currentFileInPacket.length");
+ mModel.send_command_ftp_nak(MAVLINK_MSG_ID_FILE_TRANSFER_PROTOCOL, 6, 0);
+ return;
+ }
+ try {
+// We only need to rewrite byte 12 to bytes_to_add_size (which is the total bytes in currentFileInPacket[packet])
+// Then we just read a packet byte by using the offset as packet_id and i from bytes_to_add_size
+ for (int i = 0; i < bytes_to_add_size; i++) {
+ int added = i + 12;
+ data[added] = currentFileInPacket[offset][i];
+ }
+ mModel.send_command_ftp_bytes_ack(MAVLINK_MSG_ID_FILE_TRANSFER_PROTOCOL, data);
+ } catch (Exception e) {
+ parent.logMessageDJI(e.toString());
+ mModel.send_command_ftp_nak(MAVLINK_MSG_ID_FILE_TRANSFER_PROTOCOL, 6, 0);
+ }
+ } catch (Exception e) {
+ parent.logMessageDJI(e.toString());
+ }
+ }
+
+ private byte[] readFileToBytes(File file) throws IOException {
+ byte[] bytes = new byte[(int) file.length()];
+ FileInputStream fis = null;
+ try {
+ fis = new FileInputStream(file);
+ //read file into bytes[]
+ fis.read(bytes);
+ } finally {
+ if (fis != null) {
+ fis.close();
+ }
+ }
+ return bytes;
+ }
+}
diff --git a/app/src/main/java/sq/rogue/rosettadrone/MAVLinkReceiver.java b/app/src/main/java/sq/rogue/rosettadrone/MAVLinkReceiver.java
index cdfba59..2b908fa 100644
--- a/app/src/main/java/sq/rogue/rosettadrone/MAVLinkReceiver.java
+++ b/app/src/main/java/sq/rogue/rosettadrone/MAVLinkReceiver.java
@@ -15,11 +15,23 @@ import com.MAVLink.common.msg_param_request_read;
import com.MAVLink.common.msg_param_set;
import com.MAVLink.common.msg_set_mode;
import com.MAVLink.common.msg_set_position_target_global_int;
+import com.MAVLink.common.msg_file_transfer_protocol;
import com.MAVLink.common.msg_set_position_target_local_ned;
import com.MAVLink.enums.MAV_CMD;
+import com.MAVLink.enums.MAV_MISSION_RESULT;
import com.MAVLink.enums.MAV_MISSION_TYPE;
import com.MAVLink.enums.MAV_RESULT;
+import java.io.IOException;
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
+
+import java.io.File;
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
+import java.net.InetAddress;
+import java.net.SocketException;
+import java.net.UnknownHostException;
import java.util.ArrayList;
import dji.common.flightcontroller.FlightControllerState;
@@ -61,6 +73,7 @@ import static com.MAVLink.enums.MAV_CMD.MAV_CMD_DO_SET_MODE;
import static com.MAVLink.enums.MAV_CMD.MAV_CMD_DO_SET_SERVO;
import static com.MAVLink.enums.MAV_CMD.MAV_CMD_GET_HOME_POSITION;
import static com.MAVLink.enums.MAV_CMD.MAV_CMD_MISSION_START;
+import static com.MAVLink.enums.MAV_CMD.MAV_CMD_OVERRIDE_GOTO;
import static com.MAVLink.enums.MAV_CMD.MAV_CMD_NAV_LAND;
import static com.MAVLink.enums.MAV_CMD.MAV_CMD_NAV_LOITER_UNLIM;
import static com.MAVLink.enums.MAV_CMD.MAV_CMD_NAV_RETURN_TO_LAUNCH;
@@ -70,20 +83,33 @@ import static com.MAVLink.enums.MAV_CMD.MAV_CMD_REQUEST_AUTOPILOT_CAPABILITIES;
import static com.MAVLink.enums.MAV_CMD.MAV_CMD_VIDEO_START_CAPTURE;
import static com.MAVLink.enums.MAV_CMD.MAV_CMD_VIDEO_STOP_CAPTURE;
import static com.MAVLink.enums.MAV_MISSION_TYPE.MAV_MISSION_TYPE_MISSION;
+import static com.MAVLink.common.msg_file_transfer_protocol.MAVLINK_MSG_ID_FILE_TRANSFER_PROTOCOL;
+import static com.MAVLink.enums.MAV_GOTO.MAV_GOTO_HOLD_AT_SPECIFIED_POSITION;
+import static com.MAVLink.enums.MAV_GOTO.MAV_GOTO_DO_CONTINUE;
+import static com.MAVLink.enums.MAV_GOTO.MAV_GOTO_HOLD_AT_CURRENT_POSITION;
import static sq.rogue.rosettadrone.util.TYPE_WAYPOINT_DISTANCE;
import static sq.rogue.rosettadrone.util.TYPE_WAYPOINT_MAX_ALTITUDE;
import static sq.rogue.rosettadrone.util.TYPE_WAYPOINT_MAX_SPEED;
import static sq.rogue.rosettadrone.util.TYPE_WAYPOINT_MIN_ALTITUDE;
import static sq.rogue.rosettadrone.util.TYPE_WAYPOINT_MIN_SPEED;
+import static com.MAVLink.enums.MAV_PROTOCOL_CAPABILITY.MAV_PROTOCOL_CAPABILITY_FTP;
+import static com.google.android.material.snackbar.BaseTransientBottomBar.LENGTH_LONG;
+
import static sq.rogue.rosettadrone.util.safeSleep;
public class MAVLinkReceiver {
+
+ public int AIactivityPort = 7001;
+ public String AIactivityIP = "127.0.0.1";
+
+ public boolean AIenabled = false;
+ public boolean AIstat = false;
+
private static final int MAV_MISSION_ACCEPTED = 0;
private final String TAG = this.getClass().getSimpleName();
private float m_autoFlightSpeed = 2.0f;
- private float m_maxFlightSpeed = 5.0f;
-
+ private float m_maxFlightSpeed = 12.0f;
private final int WP_STATE_INACTIVE = 0;
private final int WP_STATE_REQ_COUNT = 1;
@@ -92,6 +118,7 @@ public class MAVLinkReceiver {
public boolean curvedFlightPath = true;
public float flightPathRadius = .2f;
DroneModel mModel;
+ FTPManager ftpManager;
private long mTimeStampLastGCSHeartbeat = 0;
private int mNumGCSWaypoints = 0;
private int wpState = 0;
@@ -100,6 +127,9 @@ public class MAVLinkReceiver {
private ArrayList mMissionItemList;
private boolean isHome = true;
+// private ArrayList aiWP = new ArrayList();
+ private String[] aiWP = new String[100]; // Max 100 wp in an AI mission for now...
+
public MAVLinkReceiver(MainActivity parent, DroneModel model) {
this.parent = parent;
@@ -111,7 +141,7 @@ public class MAVLinkReceiver {
// IS 0 is hart beat...
if (msg.msgid != 0) {
Log.d(TAG, msg.toString());
- Log.d(TAG, "Message: " + msg);
+ // Log.d(TAG, "Message: " + msg);
// Log.d(TAG, String.valueOf(msg));
// Log.d(TAG, String.valueOf(msg.msgid));
}
@@ -181,11 +211,57 @@ public class MAVLinkReceiver {
break;
case MAV_CMD_DO_DIGICAM_CONTROL:
// DEPRECATED but still used by QGC
+ // mModel.do_set_Gimbal(9, (float)((-30.0*5.5)+1500.0)); // TODO:: Default point somewhat down... To be removed when Camera setting in mission works....
mModel.takePhoto();
break;
case MAV_CMD_MISSION_START:
mModel.startWaypointMission();
break;
+ case MAV_CMD_OVERRIDE_GOTO:
+ parent.logMessageDJI("Received MAV: MAV_CMD_OVERRIDE_GOTO");
+ if (mModel.getSystemId() != msg_cmd.target_system) {
+ break;
+ }
+ mModel.pauseWaypointMission();
+ if (msg_cmd.param2 == MAV_GOTO_HOLD_AT_CURRENT_POSITION) {
+ int x = (int) mModel.get_current_lat();
+ int y = (int) mModel.get_current_lon();
+ int z = (int) mModel.get_current_alt();
+
+ Log.d(TAG, "Lat = " + x);
+ Log.d(TAG, "Lon = " + y);
+ Log.d(TAG, "ALT = " + z);
+ mModel.do_set_motion_absolute(
+ (double) x,
+ (double) y,
+ z,
+ msg_cmd.param4,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0b0000111111111000);
+ } else if (msg_cmd.param2 == MAV_GOTO_HOLD_AT_SPECIFIED_POSITION) {
+
+ Log.d(TAG, "Lat = " + msg_cmd.param5);
+ Log.d(TAG, "Lon = " + msg_cmd.param6);
+ Log.d(TAG, "ALT = " + msg_cmd.param7);
+ mModel.do_set_motion_absolute(
+ (double) msg_cmd.param5,
+ (double) msg_cmd.param6,
+ msg_cmd.param7,
+ msg_cmd.param4,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0b0000111111111000);
+ }
+ if(msg_cmd.param1 == MAV_GOTO_DO_CONTINUE) {
+ mModel.resumeWaypointMission();
+ }
+ mModel.send_command_ack(MAV_CMD_OVERRIDE_GOTO, MAV_RESULT.MAV_RESULT_ACCEPTED);
+ break;
case MAV_CMD_CONDITION_YAW:
Log.d(TAG, "Yaw = " + msg_cmd.param1);
@@ -209,6 +285,11 @@ public class MAVLinkReceiver {
case MAV_CMD_DO_SET_SERVO:
mModel.do_set_Gimbal(msg_cmd.param1, msg_cmd.param2);
break;
+ // CUSTOM
+ case MAV_PROTOCOL_CAPABILITY_FTP:
+ parent.logMessageDJI("Received MAV: MAV_PROTOCOL_CAPABILITY_FTP");
+ mModel.send_command_ack(MAV_PROTOCOL_CAPABILITY_FTP, MAV_RESULT.MAV_RESULT_ACCEPTED);
+ break;
// JUMP is just a test function to enter the Timeline...
case MAV_CMD_DO_JUMP:
Log.d(TAG, "Start Timeline...");
@@ -409,6 +490,8 @@ public class MAVLinkReceiver {
// We are done fetching a complete mission from the GCS...
if (msg_item.seq == mNumGCSWaypoints - 1) {
+ // mModel.send_command_ack(MAVLINK_MSG_ID_MISSION_ACK, MAV_RESULT.MAV_RESULT_ACCEPTED);
+ Log.d(TAG, "Mission Loaded from host...!");
wpState = WP_STATE_INACTIVE;
finalizeNewMission();
} else {
@@ -491,6 +574,14 @@ public class MAVLinkReceiver {
ym.getWaypointList().clear();
}
break;
+ case MAVLINK_MSG_ID_FILE_TRANSFER_PROTOCOL:
+ msg_file_transfer_protocol msg_ftp_item = (msg_file_transfer_protocol) msg;
+ ftpManager.manage_ftp(msg_ftp_item);
+ break;
+ default:
+ parent.logMessageDJI("Received (unkown) message");
+ parent.logMessageDJI( String.valueOf(msg.msgid));
+ break;
}
}
@@ -547,158 +638,250 @@ public class MAVLinkReceiver {
Log.d(TAG, "==============================");
boolean stopUpload = false;
+ boolean takeoff_received = false;
int errorCode = 0;
+ float base_alt = 0; // The DJI Drone got bad absolute Altitude, so we calibrate it with what the mission planner expects.
boolean triggerDistanceEnabled = false;
float triggerDistance = 0;
- waypoint_loop:
+ // If we got our position...
+// if(mModel.get_current_lat()+mModel.get_current_lon()+mModel.get_current_alt() > 0) {
+// currentWP = new Waypoint(mModel.get_current_lat(), mModel.get_current_lon(), mModel.get_current_alt());
+// }
- for (msg_mission_item_int m : this.mMissionItemList) {
- Log.d(TAG, "Command: " +String.valueOf(m));
+ if (AIstat == true)
+ {
+// aiWP = "";
+ int num = 0;
- switch (m.command) {
+ for (msg_mission_item_int m : this.mMissionItemList) {
+ Log.d(TAG, "AI Command: " + String.valueOf(m));
- case MAV_CMD.MAV_CMD_NAV_TAKEOFF:
- Log.d(TAG, "Takeoff...");
+ switch (m.command) {
+ case MAV_CMD.MAV_CMD_NAV_WAYPOINT:
- // if we got an item (Start item) already we got a position, now we just add altitude.
- if(currentWP != null){
- currentWP.altitude = m.z;
- }else{
- if(m.x == 0 || m.y == 0) {
- currentWP = new Waypoint(mModel.get_current_lat(), mModel.get_current_lon(), m.z);
- }else {
- currentWP = new Waypoint(m.x,m.z, m.z);
- }
- }
- dji_wps.add(currentWP);
- break;
+ if(num ==0)
+ mModel.mission_alt = m.z;
- case MAV_CMD.MAV_CMD_NAV_WAYPOINT:
- Log.d(TAG, "Waypoint: " + m.x/10000000.0 + ", " + m.y/10000000.0 + " at " + m.z + " m " + m.param2 + " Yaw " + m.param1 + " Delay ");
-
- // If this is a start item let's store the position ...
- if(m.frame != 3 || Float.isNaN(m.z) ) {
- currentWP = new Waypoint(mModel.get_current_lat(), mModel.get_current_lon(), 0);
+ aiWP[num++] = "WP," +
+ String.valueOf(num) + "," +
+ String.valueOf(m.x / 10000000.0) + "," +
+ String.valueOf(m.y / 10000000.0) + "," +
+ String.valueOf(m.z-mModel.mission_alt) + "";
break;
- }
-
- if ((m.z) > 500) { // TODO: Shuld reqest max altitude not assume 500...
- parent.runOnUiThread(() -> NotificationHandler.notifyAlert(parent, TYPE_WAYPOINT_MAX_ALTITUDE, null, null));
- stopUpload = true;
- mModel.send_command_ack(MAVLINK_MSG_ID_MISSION_ACK, MAV_RESULT.MAV_RESULT_DENIED);
- break waypoint_loop;
-
- } else if ((m.z) < -200) { // TODO: hmm so we can not take off from a mountain and fly down?? ...
- parent.runOnUiThread(() -> NotificationHandler.notifyAlert(parent, TYPE_WAYPOINT_MIN_ALTITUDE, null, null));
- stopUpload = true;
- mModel.send_command_ack(MAVLINK_MSG_ID_MISSION_ACK, MAV_RESULT.MAV_RESULT_DENIED);
- break waypoint_loop;
- }
-
- currentWP = new Waypoint(m.x/10000000.0, m.y/10000000.0, m.z); // TODO check altitude conversion
-
- // If delay at wp...
- if (m.param1 > 0)
- currentWP.addAction(new WaypointAction(WaypointActionType.STAY, (int) m.param1 * 1000)); // milliseconds...
-
- // If rotate at wp...
- if (m.param2 > 0)
- currentWP.addAction(new WaypointAction(WaypointActionType.ROTATE_AIRCRAFT, (int) (m.param2 * 180.0 / 3.141592))); // +-180 deg...
-
- // This is set in the RosettaDrone2 settings...
- if (curvedFlightPath) {
- currentWP.cornerRadiusInMeters = flightPathRadius;
- }
-
- dji_wps.add(currentWP);
- break;
-
- case MAV_CMD.MAV_CMD_DO_CHANGE_SPEED:
- Log.d(TAG, "Change Speed: " + m.x/10000000.0 + ", " + m.y/10000000.0 + " at " + m.z + " m " + m.param2 + " Yaw " + m.param1 + " Delay ");
-
- if (m.param2 < -15) {
- parent.runOnUiThread(() -> NotificationHandler.notifyAlert(parent, TYPE_WAYPOINT_MIN_SPEED,
- null, null));
- stopUpload = true;
- break waypoint_loop;
- }
- else if (m.param2 > 15) {
- parent.runOnUiThread(() -> NotificationHandler.notifyAlert(parent, TYPE_WAYPOINT_MAX_SPEED,
- null, null));
- stopUpload = true;
- break waypoint_loop;
- }
- else {
- mBuilder.autoFlightSpeed(m.param2);
- }
- break;
-
- case MAV_CMD.MAV_CMD_DO_MOUNT_CONTROL:
- Log.d(TAG, "Set gimbal pitch: " + m.param1);
- currentWP = new Waypoint(m.x/10000000.0, m.y/10000000.0, m.z); // TODO check altitude conversion
- currentWP.addAction(new WaypointAction(WaypointActionType.GIMBAL_PITCH, (int) m.param1));
- dji_wps.add(currentWP);
- break;
-
- case MAV_CMD.MAV_CMD_IMAGE_START_CAPTURE:
- Log.d(TAG, "MAV_CMD_IMAGE_START_CAPTURE");
- currentWP = new Waypoint(m.x/10000000.0, m.y/10000000.0, m.z); // TODO check altitude conversion
- currentWP.addAction(new WaypointAction(WaypointActionType.START_TAKE_PHOTO, 0));
- dji_wps.add(currentWP);
- break;
-
- case MAV_CMD.MAV_CMD_DO_SET_CAM_TRIGG_DIST:
- Log.d(TAG, "MAV_CMD_DO_SET_CAM_TRIGG_DIST");
-
- if (!triggerDistanceEnabled) {
- if (m.param1 != 0) {
- triggerDistanceEnabled = true;
- triggerDistance = m.param1;
- } else {
- triggerDistance = 0;
- }
- }
- break;
-
- case MAV_CMD.MAV_CMD_NAV_RETURN_TO_LAUNCH:
- Log.d(TAG, "MAV_CMD_NAV_RETURN_TO_LAUNCH");
- mBuilder.finishedAction(WaypointMissionFinishedAction.GO_HOME);
- break;
-
- case MAV_CMD.MAV_CMD_NAV_DELAY:
- Log.d(TAG, "MAV_CMD_NAV_DELAY");
- break;
-
- case MAV_CMD.MAV_CMD_VIDEO_START_CAPTURE:
- Log.d(TAG, "MAV_CMD_VIDEO_START_CAPTURE");
- break;
-
- case MAV_CMD.MAV_CMD_VIDEO_STOP_CAPTURE:
- Log.d(TAG, "MAV_CMD_VIDEO_STOP_CAPTURE");
- break;
-
- case MAV_CMD.MAV_CMD_CONDITION_YAW:
- Log.d(TAG, "MAV_CMD_CONDITION_YAW");
- break;
-
- case MAV_CMD.MAV_CMD_DO_DIGICAM_CONTROL:
- Log.d(TAG, "MAV_CMD_DO_DIGICAM_CONTROL");
- break;
-
- case MAV_CMD.MAV_CMD_SET_CAMERA_ZOOM:
- Log.d(TAG, "MAV_CMD_SET_CAMERA_ZOOM");
- break;
-
- case MAV_CMD.MAV_CMD_SET_CAMERA_FOCUS:
- Log.d(TAG, "MAV_CMD_SET_CAMERA_FOCUS");
- break;
}
+ }
+
+ // Send mission to the AIactivityModule
+// Log.d(TAG, aiWP);
+
+ if(num > 0){
+ aiWP[num++] = "WP,9999,0,0,0"; // End of mission...
+
+ try {
+ InetAddress address = InetAddress.getByName(AIactivityIP);
+
+ try {
+ DatagramSocket mSocketUDP = new DatagramSocket();
+
+ for(int x=0; x < num; x++){
+
+ byte[] byteArrray = aiWP[x].getBytes();
+ DatagramPacket p = new DatagramPacket(byteArrray, byteArrray.length, address, AIactivityPort);
+ try {
+ mSocketUDP.send(p);
+ // mSocketUDP.close(); // It this needed / wanted...
+ } catch (IOException e) {
+ Log.e(TAG, "Error sending AI datagram socket", e);
+ }
+ aiWP[x]="";
+ }
+ } catch (SocketException e) {
+ Log.e(TAG, "Error creating AI datagram socket", e);
+ }
+ } catch (UnknownHostException e) {
+ Log.e(TAG, "Error setting address to AI datagram socket", e);
+ }
+ }
+ stopUpload = true;
+
+ NotificationHandler.notifySnackbar(parent.findViewById(R.id.snack),R.string.ai_uploaded_active, LENGTH_LONG);
+ } else {
+ waypoint_loop:
+ for (msg_mission_item_int m : this.mMissionItemList) {
+ Log.d(TAG, "Command: " + String.valueOf(m));
+
+ switch (m.command) {
+
+ case MAV_CMD.MAV_CMD_NAV_TAKEOFF:
+ Log.d(TAG, "Takeoff...");
+
+ // if we got an item (Start item) already we got a position, now we just add altitude.
+ if (currentWP != null) {
+ currentWP.altitude = m.z;
+ } else {
+ if (m.x == 0 || m.y == 0) {
+ currentWP = new Waypoint(mModel.get_current_lat(), mModel.get_current_lon(), m.z);
+ } else {
+ currentWP = new Waypoint(m.x, m.z, m.z);
+ }
+ }
+
+ Log.d(TAG, "Takeoff at " + currentWP);
+ dji_wps.add(currentWP);
+ takeoff_received = true;
+ break;
+
+ case MAV_CMD.MAV_CMD_NAV_WAYPOINT:
+ Log.d(TAG, "Waypoint: " + m.x / 10000000.0 + ", " + m.y / 10000000.0 + " at " + m.z + " m " + m.param2 + " Yaw " + m.param1 + " Delay ");
+
+ // We only support some frma models... 0 = Global, 3 = Lat,Lon,HomeAlt relative
+ if (m.frame != 0 && m.frame != 3) {
+ Log.d(TAG, "Waypoint frame error! " + m.frame);
+ break;
+ }
+
+ // If this is a start item let's store the altitude and make a base as the drone's altitude is quite bad ...
+ if (takeoff_received == false && m.command == 16 && m.seq == 0) {
+ Log.d(TAG, "Waypoint error 1");
+ // if(mModel.get_current_alt() > 0.5 && m.z > 0.5) {
+ // base_alt = mModel.get_current_alt() - m.z;
+ // }
+ currentWP = new Waypoint(m.x / 10000000.0, m.y / 10000000.0, m.z);
+ base_alt = m.z;
+ mModel.mission_alt = m.z;
+ break;
+ }
+
+ // If this is a start item let's store the position ...
+ if (m.frame == 0) {
+ Log.d(TAG, "Waypoint rescaled..." + m.z + " " + base_alt); //mModel.home_position.altitude);
+ m.z = m.z - base_alt;
+ // m.z = (m.z - mModel.home_position.altitude)-base_alt;
+ Log.d(TAG, "= " + m.z);
+ }
+
+ if ((m.z) > 500) { // TODO: Shuld reqest max altitude not assume 500...
+ parent.runOnUiThread(() -> NotificationHandler.notifyAlert(parent, TYPE_WAYPOINT_MAX_ALTITUDE, null, null));
+ stopUpload = true;
+ mModel.send_command_ack(MAVLINK_MSG_ID_MISSION_ACK, MAV_RESULT.MAV_RESULT_DENIED);
+ Log.d(TAG, "Waypoint error 2");
+ break; // waypoint_loop;
+
+ } else if ((m.z) < -200) { // TODO: hmm so we can not take off from a mountain and fly down?? ...
+ parent.runOnUiThread(() -> NotificationHandler.notifyAlert(parent, TYPE_WAYPOINT_MIN_ALTITUDE, null, null));
+ stopUpload = true;
+ mModel.send_command_ack(MAVLINK_MSG_ID_MISSION_ACK, MAV_RESULT.MAV_RESULT_DENIED);
+ Log.d(TAG, "Waypoint error 3");
+ break; // waypoint_loop;
+ }
+
+ currentWP = new Waypoint(m.x / 10000000.0, m.y / 10000000.0, m.z); // TODO check altitude conversion
+
+ // If delay at wp...
+ if (m.param1 > 0)
+ currentWP.addAction(new WaypointAction(WaypointActionType.STAY, (int) m.param1 * 1000)); // milliseconds...
+
+ // If rotate at wp...
+ if (m.param2 > 0)
+ currentWP.addAction(new WaypointAction(WaypointActionType.ROTATE_AIRCRAFT, (int) (m.param2 * 180.0 / 3.141592))); // +-180 deg...
+
+ // This is set in the RosettaDrone2 settings...
+ if (curvedFlightPath) {
+ currentWP.cornerRadiusInMeters = flightPathRadius;
+ }
+ Log.d(TAG, "Waypoint OK");
+ dji_wps.add(currentWP);
+ break;
+
+ case MAV_CMD.MAV_CMD_DO_CHANGE_SPEED:
+ Log.d(TAG, "Change Speed: " + m.x / 10000000.0 + ", " + m.y / 10000000.0 + " at " + m.z + " m " + m.param2 + " Yaw " + m.param1 + " Delay ");
+
+ if (m.param2 < -15) {
+ parent.runOnUiThread(() -> NotificationHandler.notifyAlert(parent, TYPE_WAYPOINT_MIN_SPEED,
+ null, null));
+ stopUpload = true;
+ break; // waypoint_loop;
+ } else if (m.param2 > 15) {
+ parent.runOnUiThread(() -> NotificationHandler.notifyAlert(parent, TYPE_WAYPOINT_MAX_SPEED,
+ null, null));
+ stopUpload = true;
+ break; // waypoint_loop;
+ } else {
+ mBuilder.autoFlightSpeed(m.param2);
+ }
+ break;
+
+ case MAV_CMD.MAV_CMD_DO_MOUNT_CONTROL:
+ Log.d(TAG, "Set gimbal pitch: " + m.param1);
+ // currentWP = new Waypoint(mModel.get_current_lat(),mModel.get_current_lon(),mModel.get_current_alt()); // TODO check altitude conversion
+ currentWP = dji_wps.get(dji_wps.size() - 1);
+ dji_wps.remove(dji_wps.size() - 1);
+ currentWP.addAction(new WaypointAction(WaypointActionType.GIMBAL_PITCH, (int) m.param1));
+ dji_wps.add(currentWP);
+ break;
+
+ case MAV_CMD.MAV_CMD_IMAGE_START_CAPTURE:
+ Log.d(TAG, "MAV_CMD_IMAGE_START_CAPTURE");
+ currentWP = new Waypoint(m.x / 10000000.0, m.y / 10000000.0, m.z); // TODO check altitude conversion
+ currentWP.addAction(new WaypointAction(WaypointActionType.START_TAKE_PHOTO, 0));
+ dji_wps.add(currentWP);
+ break;
+
+ case MAV_CMD.MAV_CMD_DO_SET_CAM_TRIGG_DIST:
+ Log.d(TAG, "MAV_CMD_DO_SET_CAM_TRIGG_DIST");
+
+ if (!triggerDistanceEnabled) {
+ if (m.param1 != 0) {
+ triggerDistanceEnabled = true;
+ triggerDistance = m.param1;
+ } else {
+ triggerDistance = 0;
+ }
+ }
+ break;
+
+ case MAV_CMD.MAV_CMD_NAV_RETURN_TO_LAUNCH:
+ Log.d(TAG, "MAV_CMD_NAV_RETURN_TO_LAUNCH");
+ mBuilder.finishedAction(WaypointMissionFinishedAction.GO_HOME);
+ break;
+
+ case MAV_CMD.MAV_CMD_NAV_DELAY:
+ Log.d(TAG, "MAV_CMD_NAV_DELAY");
+ break;
+
+ case MAV_CMD.MAV_CMD_VIDEO_START_CAPTURE:
+ Log.d(TAG, "MAV_CMD_VIDEO_START_CAPTURE");
+ break;
+
+ case MAV_CMD.MAV_CMD_VIDEO_STOP_CAPTURE:
+ Log.d(TAG, "MAV_CMD_VIDEO_STOP_CAPTURE");
+ break;
+
+ case MAV_CMD.MAV_CMD_CONDITION_YAW:
+ Log.d(TAG, "MAV_CMD_CONDITION_YAW");
+ break;
+
+ case MAV_CMD.MAV_CMD_DO_DIGICAM_CONTROL:
+ Log.d(TAG, "MAV_CMD_DO_DIGICAM_CONTROL");
+ break;
+
+ case MAV_CMD.MAV_CMD_SET_CAMERA_ZOOM:
+ Log.d(TAG, "MAV_CMD_SET_CAMERA_ZOOM");
+ break;
+
+ case MAV_CMD.MAV_CMD_SET_CAMERA_FOCUS:
+ Log.d(TAG, "MAV_CMD_SET_CAMERA_FOCUS");
+ break;
+ }
+ }
}
if (stopUpload) {
- Log.d(TAG, "Waypoint upload aborted due to invalid parameters");
+ Log.d(TAG, "Waypoint upload aborted due to invalid parameters or AI mode");
+ mModel.send_mission_ack(MAV_MISSION_RESULT.MAV_MISSION_ACCEPTED);
+ mModel.send_text("eSmart AI Mission uploaded !");
} else {
Log.d(TAG, "Speed for mission will be " + mBuilder.getAutoFlightSpeed() + " m/s");
Log.d(TAG, "==============================");
@@ -721,13 +904,13 @@ public class MAVLinkReceiver {
logWaypointstoRD(correctedWps);
Log.d(TAG, "WP size " + correctedWps.size());
- safeSleep(200);
+// safeSleep(200);
mBuilder.waypointList(correctedWps).waypointCount(correctedWps.size());
- safeSleep(200);
+// safeSleep(200);
WaypointMission builtMission = mBuilder.build();
- safeSleep(200);
+ // safeSleep(200);
mModel.setWaypointMission(builtMission);
- safeSleep(200);
+// safeSleep(200);
}
mMissionItemList=null; // Flush the mission list...
diff --git a/app/src/main/java/sq/rogue/rosettadrone/MainActivity.java b/app/src/main/java/sq/rogue/rosettadrone/MainActivity.java
index 413b1ac..12ad607 100644
--- a/app/src/main/java/sq/rogue/rosettadrone/MainActivity.java
+++ b/app/src/main/java/sq/rogue/rosettadrone/MainActivity.java
@@ -5,8 +5,10 @@ package sq.rogue.rosettadrone;
// Hide keyboard: https://stackoverflow.com/questions/16495440/how-to-hide-keyboard-by-default-and-show-only-when-click-on-edittext
// MenuItemTetColor: RPP @ https://stackoverflow.com/questions/31713628/change-menuitem-text-color-programmatically
+import android.Manifest;
import android.content.ComponentName;
import android.content.Context;
+import android.content.DialogInterface;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
@@ -14,9 +16,18 @@ import android.content.pm.ActivityInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.res.AssetManager;
+import android.graphics.Bitmap;
+import android.graphics.Color;
import android.graphics.SurfaceTexture;
+import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
+import android.graphics.drawable.StateListDrawable;
import android.hardware.usb.UsbManager;
+import android.location.Address;
+import android.location.Geocoder;
+import android.location.Location;
+import android.location.LocationListener;
+import android.location.LocationManager;
import android.media.Ringtone;
import android.media.RingtoneManager;
import android.net.Uri;
@@ -49,10 +60,13 @@ import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.SupportMapFragment;
+import com.google.android.gms.maps.model.BitmapDescriptor;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;
+import com.google.android.gms.maps.model.Polyline;
+import com.google.android.gms.maps.model.PolylineOptions;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
@@ -72,6 +86,10 @@ import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Locale;
import java.util.Objects;
import java.util.Timer;
import java.util.TimerTask;
@@ -79,24 +97,38 @@ import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import androidx.annotation.NonNull;
+import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
+import androidx.core.app.ActivityCompat;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
+import androidx.preference.Preference;
import androidx.preference.PreferenceManager;
import dji.common.camera.SettingsDefinitions;
import dji.common.error.DJIError;
import dji.common.error.DJISDKError;
+import dji.common.flightcontroller.LocationCoordinate3D;
+import dji.common.mission.waypoint.Waypoint;
+import dji.common.mission.waypoint.WaypointMission;
import dji.common.model.LocationCoordinate2D;
import dji.common.product.Model;
+import dji.common.util.CommonCallbacks;
+import dji.log.DJILog;
import dji.sdk.base.BaseComponent;
import dji.sdk.base.BaseProduct;
import dji.sdk.camera.Camera;
import dji.sdk.camera.VideoFeeder;
import dji.sdk.codec.DJICodecManager;
+import dji.sdk.media.DownloadListener;
+import dji.sdk.media.FetchMediaTaskScheduler;
+import dji.sdk.media.MediaFile;
+import dji.sdk.media.MediaManager;
import dji.sdk.products.Aircraft;
import dji.sdk.sdkmanager.DJISDKInitEvent;
import dji.sdk.sdkmanager.DJISDKManager;
import sq.rogue.rosettadrone.logs.LogFragment;
+import sq.rogue.rosettadrone.settings.GMailSender;
+import sq.rogue.rosettadrone.settings.MailReport;
import sq.rogue.rosettadrone.settings.SettingsActivity;
import sq.rogue.rosettadrone.settings.Waypoint1Activity;
import sq.rogue.rosettadrone.settings.Waypoint2Activity;
@@ -106,6 +138,9 @@ import sq.rogue.rosettadrone.video.VideoService;
import static com.google.android.material.snackbar.Snackbar.LENGTH_LONG;
import static sq.rogue.rosettadrone.util.safeSleep;
+// TODO: Continue does not work, pause only allow flying in line with mission, report does not work, Button click should be changed, Mission not shown on rosettadrone.
+
+
//public class MainActivity extends AppCompatActivity implements OnMapReadyCallback, GoogleMap.OnMapClickListener {
public class MainActivity extends AppCompatActivity implements OnMapReadyCallback {
@@ -128,11 +163,17 @@ public class MainActivity extends AppCompatActivity implements OnMapReadyCallbac
public static boolean FLAG_DRONE_FLIGHT_PATH_MODE_CHANGED = false;
public static boolean FLAG_DRONE_MAX_HEIGHT_CHANGED = false;
public static boolean FLAG_APP_NAME_CHANGED = false;
+ public static boolean FLAG_APP_REPORT_EMAIL = false;
public static boolean FLAG_MAPS_CHANGED = false;
+ public static boolean FLAG_DRONE_AI_IP_CHANGED = false;
+ public static boolean FLAG_DRONE_AI_PORT_CHANGED = false;
+ public static boolean FLAG_DRONE_AI_ENABLED_CHANGED = false;
- private GoogleMap aMap;
+ private GoogleMap aMap;
private double droneLocationLat, droneLocationLng;
private Marker droneMarker = null;
+ private Marker gcsMarker = null;
+ private Marker gotoMarker = null;
private static BaseProduct mProduct;
private Model mProductModel;
@@ -144,22 +185,26 @@ public class MainActivity extends AppCompatActivity implements OnMapReadyCallbac
private LogFragment logOutbound;
private LogFragment logInbound;
private int navState = -1;
- private SharedPreferences prefs;
private String mNewOutbound = "";
private String mNewInbound = "";
private String mNewDJI = "";
private DatagramSocket socket;
- private DroneModel mModel;
+ public DroneModel mModel;
+ private Boolean mExtRunning = false;
+ private LocationManager locationManager;
+ private double m_host_lat = -500;
+ private double m_host_lon = -500;
- private MAVLinkReceiver mMavlinkReceiver;
+ public MAVLinkReceiver mMavlinkReceiver;
private Parser mMavlinkParser;
private GCSCommunicatorAsyncTask mGCSCommunicator;
private boolean connectivityHasChanged = false;
private boolean shouldConnect = false;
private boolean gui_enabled = true;
private Button mBtnSafety;
- private boolean stat = false; // Is it safe to takeoff....
+ private boolean stat = false; // Is it safe to takeoff...
+ private SharedPreferences prefs;
private boolean mExternalVideoOut = true;
private String mvideoIPString;
private int videoPort;
@@ -174,19 +219,33 @@ public class MainActivity extends AppCompatActivity implements OnMapReadyCallbac
protected VideoFeeder.VideoDataListener mReceivedVideoDataListener;
private TextureView videostreamPreviewTtView;
private TextureView videostreamPreviewTtViewSmall;
- private Camera mCamera;
private DJICodecManager mCodecManager;
private int videoViewWidth;
private int videoViewHeight;
protected SharedPreferences sharedPreferences;
private boolean mIsTranscodedVideoFeedNeeded = false;
+ public static WaypointMission.Builder waypointMissionBuilder;
+ private List waypointList = new ArrayList<>();
+ private List m_line = new ArrayList<>();
+ private LatLng m_lastPos;
+ public List mediaFileList = new ArrayList();
+ private MediaManager mMediaManager;
+ private MediaManager.FileListState currentFileListState = MediaManager.FileListState.UNKNOWN;
+ private FetchMediaTaskScheduler scheduler;
+ public String last_downloaded_file;
+ public boolean downloadError = false;
+ public int lastDownloadedIndex = -1;
+
+ private String AIaddress = "127.0.0.1";
+ private int AIport = 7001;
+ //-----------------------------------------------------------------------------//
private Runnable djiUpdateRunnable = () -> {
Intent intent = new Intent(DJISimulatorApplication.FLAG_CONNECTION_CHANGE);
-// Intent intent = new Intent(FLAG_CONNECTION_CHANGE);
sendBroadcast(intent);
};
+ //-----------------------------------------------------------------------------
private Runnable RunnableUpdateUI = new Runnable() {
// We have to update UI from here because we can't update the UI from the
// drone/GCS handling threads
@@ -196,17 +255,14 @@ public class MainActivity extends AppCompatActivity implements OnMapReadyCallbac
if (!gui_enabled) {
try {
if (!mNewDJI.equals("")) {
- // ((LogFragment) logPagerAdapter.getItem(0)).appendLogText(mNewDJI);
logDJI.appendLogText(mNewDJI);
mNewDJI = "";
}
if (!mNewOutbound.equals("")) {
- // ((LogFragment) logPagerAdapter.getItem(1)).appendLogText(mNewOutbound);
logOutbound.appendLogText(mNewOutbound);
mNewOutbound = "";
}
if (!mNewInbound.equals("")) {
- // ((LogFragment) logPagerAdapter.getItem(2)).appendLogText(mNewInbound);
logInbound.appendLogText(mNewInbound);
mNewInbound = "";
}
@@ -218,11 +274,24 @@ public class MainActivity extends AppCompatActivity implements OnMapReadyCallbac
}
};
-
+ //-----------------------------------------------------------------------------
private void initPacketizer() {
Log.e(TAG, "initPacketizer");
String address = "127.0.0.1";
+ locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
+ LocationListener locationListener = new MyLocationListener();
+
+ if (ActivityCompat.checkSelfPermission(this,
+ Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED &&
+ ActivityCompat.checkSelfPermission(this,
+ Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
+ Log.e(TAG, "Missing right to access GPS location!");
+ }else {
+ locationManager.requestLocationUpdates(
+ LocationManager.GPS_PROVIDER, 5000, 10, locationListener);
+ }
+
sharedPreferences = android.preference.PreferenceManager.getDefaultSharedPreferences(this);
if (sharedPreferences.getBoolean("pref_external_gcs", false)) {
if (!sharedPreferences.getBoolean("pref_separate_gcs", false)) {
@@ -243,8 +312,8 @@ public class MainActivity extends AppCompatActivity implements OnMapReadyCallbac
//------------------------------------------------------------
mMaptype = Integer.parseInt(Objects.requireNonNull(prefs.getString("pref_maptype_mode", "2")));
logMessageDJI("Mapmode: " + mMaptype);
- //------------------------------------------------------------
+ //------------------------------------------------------------
Intent intent = new Intent(this, VideoService.class);
this.startService(intent);
safeSleep(500);
@@ -261,7 +330,7 @@ public class MainActivity extends AppCompatActivity implements OnMapReadyCallbac
videostreamPreviewTtView = findViewById(R.id.livestream_preview_ttv);
videostreamPreviewTtViewSmall = findViewById(R.id.livestream_preview_ttv_small);
videostreamPreviewTtView.setVisibility(View.VISIBLE);
- //videostreamPreviewTtViewSmall.setVisibility(View.VISIBLE);
+
}
// After the service have started...
@@ -345,15 +414,8 @@ public class MainActivity extends AppCompatActivity implements OnMapReadyCallbac
}
}
- // if(compare_height == 0) {
videostreamPreviewTtView.setSurfaceTextureListener(mSurfaceTextureListener);
- // }
- // else {
- // videostreamPreviewTtViewSmall.setSurfaceTextureListener(mSurfaceTextureListener);
- // }
- // If we use a camera... Remove Listeners if needed...
-
- if (mCamera != null) {
+ if ( mModel.m_camera != null) {
if (!mExternalVideoOut) {
if (mIsTranscodedVideoFeedNeeded) {
if (standardVideoFeeder != null) {
@@ -381,7 +443,6 @@ public class MainActivity extends AppCompatActivity implements OnMapReadyCallbac
}
}
-
/**
* Called whenever RosettaDrone leaves the foreground, such as when the home button is pressed or
* we enter the preferences/settings screen. We unbind the AIDL service since we don't need it if we're not
@@ -391,26 +452,7 @@ public class MainActivity extends AppCompatActivity implements OnMapReadyCallbac
@Override
protected void onPause() {
Log.e(TAG, "onPause()");
-/*
- // Remove Listeners...
- if (mCamera != null) {
- if (mIsTranscodedVideoFeedNeeded) {
- if (standardVideoFeeder != null) {
- standardVideoFeeder.removeVideoDataListener(mReceivedVideoDataListener);
- }
- } else {
- if (VideoFeeder.getInstance().getPrimaryVideoFeed() != null) {
- VideoFeeder.getInstance().getPrimaryVideoFeed().removeVideoDataListener(mReceivedVideoDataListener);
- }
- }
- }
-
- */
super.onPause();
- // We have to save text when onPause is called or it will be erased
-// mNewOutbound = logToGCS.getLogText() + mNewOutbound;
-// mNewInbound = logFromGCS.getLogText() + mNewInbound;
-// mNewDJI = logDJI.getLogText() + mNewDJI;
}
@Override
@@ -438,6 +480,10 @@ public class MainActivity extends AppCompatActivity implements OnMapReadyCallbac
mCodecManager.cleanSurface();
mCodecManager.destroyCodec();
}
+ if (mediaFileList != null) {
+ mediaFileList.clear();
+ }
+
doUnbindService();
//Intent intent = getIntent();
@@ -455,7 +501,7 @@ public class MainActivity extends AppCompatActivity implements OnMapReadyCallbac
LinearLayout map_layout = findViewById(R.id.map_view);
map_layout.setClickable(true);
map_layout.setOnClickListener((map) -> {
- ViewGroup.LayoutParams map_para = map_layout.getLayoutParams();
+ LayoutParams map_para = map_layout.getLayoutParams();
map_layout.setZ(0.f);
map_para.height = LayoutParams.WRAP_CONTENT;
map_para.width = LayoutParams.WRAP_CONTENT;
@@ -464,40 +510,141 @@ public class MainActivity extends AppCompatActivity implements OnMapReadyCallbac
}
aMap.getUiSettings().setZoomControlsEnabled(false);
aMap.setMapType(mMaptype);
+ LatLng marker = new LatLng(m_host_lat, m_host_lon);
+ aMap.moveCamera(CameraUpdateFactory.newLatLngZoom(marker, 14F));
+
+ aMap.setOnMapClickListener((point)-> {
+
+ Log.d(TAG, "Goto: " + point.toString());
+ AlertDialog.Builder alertDialog2 = new AlertDialog.Builder(this);
+ alertDialog2.setIcon(R.mipmap.track_right);
+ alertDialog2.setTitle("AI Mavlink/Python function selector!");
+ alertDialog2.setMessage("Clikked");
+ alertDialog2.setNegativeButton("Cancel",
+ (dialog, which) -> {
+ dialog.cancel();
+ });
+ alertDialog2.setNeutralButton("Add to Waypoint list",
+ (dialog, which) -> {
+ markWaypoint(point, true);
+ Waypoint mWaypoint = new Waypoint(point.latitude, point.longitude, mModel.m_alt);
+ //Add Waypoints to Waypoint arraylist;
+ if (waypointMissionBuilder != null) {
+ waypointList.add(mWaypoint);
+ waypointMissionBuilder.waypointList(waypointList).waypointCount(waypointList.size());
+ } else {
+ waypointMissionBuilder = new WaypointMission.Builder();
+ waypointList.add(mWaypoint);
+ waypointMissionBuilder.waypointList(waypointList).waypointCount(waypointList.size());
+ }
+ dialog.cancel();
+ });
+ alertDialog2.setPositiveButton ("Accept",
+ (dialog, which) -> {
+ // If we are airborn...
+ if( mModel.m_alt > -5.0) {
+ markWaypoint(point, false);
+ mModel.goto_position(point.latitude, point.longitude, mModel.m_alt, 0);
+ dialog.cancel();
+ }else {
+ Log.d(TAG, "Cannot Add Waypoint");
+ Toast.makeText(getApplicationContext(), "Cannot goto position!!!", Toast.LENGTH_LONG).show();
+ }
+ });
+ ///
+ this.runOnUiThread(() -> {
+ alertDialog2.show();
+ });
+ });
updateDroneLocation();
}
+ private void markWaypoint(LatLng point, boolean keep) {
+ //Create MarkerOptions object
+ MarkerOptions markerOptions = new MarkerOptions();
+ markerOptions.position(point);
+ markerOptions.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_BLUE));
+
+ if(keep) {
+ if(m_line.size() == 0){ m_lastPos = new LatLng(mModel.get_current_lat(), mModel.get_current_lat()); }
+ m_line.add(aMap.addPolyline(new PolylineOptions()
+ .add(m_lastPos, point)
+ .width(5)
+ .color(Color.RED)));
+
+ m_lastPos = point;
+
+ }else{
+ for( Polyline line : m_line){
+ line.remove();
+ }
+ m_line.clear();
+ }
+
+ if (gotoMarker != null && keep == false) {
+ gotoMarker.remove();
+ }
+ gotoMarker = aMap.addMarker(markerOptions);
+ }
+
// Update the drone location based on states from MCU.
private void updateDroneLocation() {
+ // We initialize the default map location to the same as the default SIM location...
if (aMap != null) {
- // We initialize the default map location to the same as the default SIM location...
- LatLng pos;
+ // Add GCS Location to map...
+ if (checkGpsCoordination(m_host_lat, m_host_lon)) {
+ final MarkerOptions GCS_markerOptions = new MarkerOptions();
+ GCS_markerOptions.position(new LatLng(m_host_lat, m_host_lon));
+ BitmapDrawable bitmapdraw = (BitmapDrawable)getResources().getDrawable(R.drawable.pilot, null);
+ Bitmap smallMarker;
+
+ if (compare_height == 0) {
+ smallMarker = Bitmap.createScaledBitmap(bitmapdraw.getBitmap(), 32, 32, false);
+ }else{
+ smallMarker = Bitmap.createScaledBitmap(bitmapdraw.getBitmap(), 64, 64, false);
+ }
+ GCS_markerOptions.icon(BitmapDescriptorFactory.fromBitmap(smallMarker));
+
+ runOnUiThread(() -> {
+ if (gcsMarker != null) { gcsMarker.remove(); }
+ gcsMarker = aMap.addMarker(GCS_markerOptions);
+// aMap.moveCamera(CameraUpdateFactory.newLatLng(pos));
+ });
+
+ }
+
+ // Find/define GCS Host location...
+ LatLng pos;
if (checkGpsCoordination(droneLocationLat, droneLocationLng)) {
pos = new LatLng(droneLocationLat, droneLocationLng);
} else {
LocationCoordinate2D loc = mModel.getSimPos2D();
- if(checkGpsCoordination(loc.getLongitude(),loc.getLongitude())) {
+ if(checkGpsCoordination(loc.getLatitude(),loc.getLongitude())) {
pos = new LatLng(loc.getLatitude(), loc.getLongitude());
}
else{
pos = new LatLng(62,12);
}
}
-
- //Create MarkerOptions object
+ // Add Drone location to map...
final MarkerOptions markerOptions = new MarkerOptions();
markerOptions.position(pos);
- markerOptions.icon(BitmapDescriptorFactory.fromResource(R.drawable.aircraft));
+ BitmapDrawable bitmapdraw = (BitmapDrawable)getResources().getDrawable(R.drawable.drone_img, null);
+ Bitmap smallMarker;
+
+ if (compare_height == 0) {
+ smallMarker = Bitmap.createScaledBitmap(bitmapdraw.getBitmap(), 32, 32, false);
+ }else{
+ smallMarker = Bitmap.createScaledBitmap(bitmapdraw.getBitmap(), 64, 64, false);
+ }
+ markerOptions.icon(BitmapDescriptorFactory.fromBitmap(smallMarker));
runOnUiThread(() -> {
- if (droneMarker != null) {
- droneMarker.remove();
- }
-
+ if (droneMarker != null) { droneMarker.remove(); }
if (checkGpsCoordination(pos.latitude, pos.longitude)) {
droneMarker = aMap.addMarker(markerOptions);
aMap.moveCamera(CameraUpdateFactory.newLatLng(pos));
@@ -506,16 +653,62 @@ public class MainActivity extends AppCompatActivity implements OnMapReadyCallbac
}
}
- private void initFlightController() {
-// setResultToToast(droneLocationLat+"----"+droneLocationLng);
+ /*---------- GCS Location changed... ------------- */
+ private class MyLocationListener implements LocationListener {
+ @Override
+ public void onLocationChanged(Location loc) {
+
+ m_host_lat = loc.getLatitude();
+ m_host_lon = loc.getLongitude();
+ updateDroneLocation();
+
+ /*------- To get city name from coordinates -------- */
+/*
+ String cityName = null;
+ Geocoder gcd = new Geocoder(getBaseContext(), Locale.getDefault());
+ List addresses;
+ try {
+ addresses = gcd.getFromLocation(loc.getLatitude(),
+ loc.getLongitude(), 1);
+ if (addresses.size() > 0) {
+ System.out.println(addresses.get(0).getLocality());
+ cityName = addresses.get(0).getLocality();
+ }
+ }
+ catch (IOException e) {
+ e.printStackTrace();
+ }
+ Log.d(TAG, m_host_lat + "\n" + m_host_lon + "\n\nMy Current City is: "+ cityName);
+ */
+ }
+
+ @Override
+ public void onProviderDisabled(String provider) {}
+
+ @Override
+ public void onProviderEnabled(String provider) {}
+
+ @Override
+ public void onStatusChanged(String provider, int status, Bundle extras) {}
+ }
+
+ private void initFlightController() {
if (mModel.mFlightController != null) {
mModel.mFlightController.setStateCallback(
- djiFlightControllerCurrentState -> {
- droneLocationLat = djiFlightControllerCurrentState.getAircraftLocation().getLatitude();
- droneLocationLng = djiFlightControllerCurrentState.getAircraftLocation().getLongitude();
+ djiFlightControllerCurrentState -> {
+ droneLocationLat = djiFlightControllerCurrentState.getAircraftLocation().getLatitude();
+ droneLocationLng = djiFlightControllerCurrentState.getAircraftLocation().getLongitude();
+
+ if (checkGpsCoordination(droneLocationLat, droneLocationLng)) {
+ // If we got no data from GCS then use the initial Drone location
+ if (m_host_lat == -500 && m_host_lon == -500) {
+ m_host_lat = droneLocationLat;
+ m_host_lon = droneLocationLng+0.00005;
+ }
updateDroneLocation();
- });
+ }
+ });
}
}
@@ -540,6 +733,8 @@ public class MainActivity extends AppCompatActivity implements OnMapReadyCallbac
//----------------
setContentView(R.layout.activity_gui);
+ PreferenceManager.setDefaultValues(this, R.xml.preferences, false);
+ prefs = PreferenceManager.getDefaultSharedPreferences(MainActivity.this);
mProduct = RDApplication.getProductInstance(); // Should be set by Connection ...
if(mProduct != null){
@@ -549,26 +744,41 @@ public class MainActivity extends AppCompatActivity implements OnMapReadyCallbac
Log.d(TAG, "exception", e);
mProductModel = Model.MAVIC_PRO; // Just a dummy value should we be in test mode... (No Target)
}
+ }else{
+ // We need to make this; if no device or if sin mode...
+ // Will support alternative video source...
+ Log.d(TAG, "Starting external video input...");
+ int port = Integer.parseInt(Objects.requireNonNull(prefs.getString("pref_external_video", "0")));
+ if(port > 1000) {
+ startVideoLoop(port);
+ }
}
/*
FrameLayout wv = findViewById(R.id.compass_container);
wv.getLayoutParams().height = FrameLayout.LayoutParams.MATCH_PARENT; // LayoutParams: android.view.ViewGroup.LayoutParams
// wv.getLayoutParams().height = LayoutParams.WRAP_CONTENT;
wv.requestLayout();//It is necesary to refresh the screen
-
*/
-
if (savedInstanceState != null) {
navState = savedInstanceState.getInt("navigation_state");
}
- PreferenceManager.setDefaultValues(this, R.xml.preferences, false);
- prefs = PreferenceManager.getDefaultSharedPreferences(MainActivity.this);
-
+ //------------------------------------------------------------
mModel = new DroneModel(this, null, RDApplication.getSim());
mModel.setSystemId(Integer.parseInt(Objects.requireNonNull(prefs.getString("pref_drone_id", "1"))));
+ //--------------------------------------------------------------
mMavlinkReceiver = new MAVLinkReceiver(this, mModel);
+ // Set some user values for the AI assist functionallity.
+ AIaddress = prefs.getString("pref_ai_ip", "127.0.0.1");
+ AIport = Integer.parseInt(Objects.requireNonNull(prefs.getString("pref_ai_port", "2000")));
+
+ mMavlinkReceiver.AIenabled = prefs.getBoolean("pref_ai_telemetry_enabled", true);
+
+ // Store IP to mission controll...
+ mMavlinkReceiver.AIactivityIP = AIaddress; //prefs.getString("pref_gcs_ip", "127.0.0.1");
+ mMavlinkReceiver.AIactivityPort = AIport;
+
loadMockParamFile();
mDJIHandler = new Handler(Looper.getMainLooper());
@@ -607,60 +817,107 @@ public class MainActivity extends AppCompatActivity implements OnMapReadyCallbac
if (stat) {
connectedDrawable = getResources().getDrawable(R.drawable.ic_lock_outline_secondary_24dp, null);
mBtnSafety.setBackground(connectedDrawable);
- findViewById(R.id.Takeoff).setVisibility(View.INVISIBLE);
+ findViewById(R.id.btn_takeoff).setVisibility(View.INVISIBLE);
} else {
connectedDrawable = getResources().getDrawable(R.drawable.ic_lock_open_black_24dp, null);
mBtnSafety.setBackground(connectedDrawable);
- findViewById(R.id.Takeoff).setVisibility(View.VISIBLE);
+ findViewById(R.id.btn_takeoff).setVisibility(View.VISIBLE);
}
mModel.setSafetyEnabled(stat);
NotificationHandler.notifySnackbar(findViewById(R.id.snack),
(mModel.isSafetyEnabled()) ? R.string.safety_on : R.string.safety_off, LENGTH_LONG);
});
-
+ //--------------------------------------------------------------
+ // Make the Takeoff button....
+ Button mBtnTakeoff = findViewById(R.id.btn_takeoff);
+ mBtnTakeoff.setOnClickListener(v -> mModel.do_takeoff(5000));
+ //--------------------------------------------------------------
+ // Make the Return Home button....
+ Button mBtnRTH = findViewById(R.id.btn_rth);
+ mBtnRTH.setOnClickListener(v -> mModel.do_go_home());
+ //--------------------------------------------------------------
+ // Make the Report button....
+ Button mBtnRepport = findViewById(R.id.btn_Report);
+ mBtnRepport.setOnClickListener(v -> SetMesasageBox("com.example.sendmail",1));
+/*
+ StateListDrawable listDrawable = new StateListDrawable();
+ listDrawable.addState(new int[] {android.R.attr.state_pressed}, mBtnRepport.getBackground());
+ listDrawable.addState(new int[] {android.R.attr.defaultValue}, mBtnRepport.setBackground("@mipmap/track_report");
+ mBtnRepport.setBackground(listDrawable);
+*/
//--------------------------------------------------------------
// Make the AI button....
Button mBtnAI = findViewById(R.id.btn_AI_start);
- mBtnAI.setOnClickListener(v -> SetMesasageBox("Hit A to go Left and B to go right..."));
+ mBtnAI.setOnClickListener(v -> Set_ai_mode());
//--------------------------------------------------------------
// Disable takeoff by default... This however it not how DJI does it, so we must delay this action...
Handler mTimerHandler = new Handler(Looper.getMainLooper());
mTimerHandler.postDelayed(enablesafety, 3000);
- //--------------------------------------------------------------
}
- // Start the AI Pluggin (Developed by the customers...)
- protected boolean startActivity(String pluggin) {
+ // Toggle AI mission mode...
+ public void Set_ai_mode()
+ {
+ Button mBtnAI = findViewById(R.id.btn_AI_start);
+ Drawable connectedDrawable;
- Intent intent = getPackageManager().getLaunchIntentForPackage(pluggin);
- if (intent == null) {
- // Bring user to the market or let them choose an app?
- intent = new Intent(Intent.ACTION_VIEW);
- intent.setData(Uri.parse("market://details?id=" + "com.example.remoteconfig3"));
+ if (mMavlinkReceiver.AIenabled == true) {
+ mMavlinkReceiver.AIstat = !mMavlinkReceiver.AIstat;
+ if (mMavlinkReceiver.AIstat) {
+ connectedDrawable = getResources().getDrawable(R.drawable.drone_img, null);
+ } else {
+ connectedDrawable = getResources().getDrawable(R.mipmap.track_right, null);
+ }
+
+ mBtnAI.setBackground(connectedDrawable);
}
- if (intent != null) {
- intent.putExtra("password", "thisisrosettadrone246546101");
- intent.putExtra("ip", "127.0.0.1");
- intent.putExtra("port", 4000);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- startActivity(intent);
+ else{
+ logMessageDJI("AI not activated in setup menu !!!!!");
+ NotificationHandler.notifySnackbar(findViewById(R.id.snack),R.string.ai_not_active, LENGTH_LONG);
+ mMavlinkReceiver.AIstat = false;
+ connectedDrawable = getResources().getDrawable(R.mipmap.track_right, null);
+ mBtnAI.setBackground(connectedDrawable);
}
- return true;
}
// If AI button is pressed then start the AI Pluggin ...
- protected void SetMesasageBox(String msg) {
- Uri notification = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM);
- Ringtone r = RingtoneManager.getRingtone(getApplicationContext(), notification);
- startActivity("com.example.remoteconfig3");
+ protected void SetMesasageBox(String msg, int type) {
+ switch (type){
+ case 1:
+ //--------------------------------------------------------------
+ logMessageDJI("Init media manager and fetch image...");
+ NotificationHandler.notifySnackbar(findViewById(R.id.snack),R.string.hold, LENGTH_LONG);
+
+ List address = new ArrayList();
+ String Eemail = sharedPreferences.getString("pref_email_name1", " ");
+ address.add(Eemail);
+ Eemail = sharedPreferences.getString("pref_email_name2", " ");
+ address.add(Eemail);
+ Eemail = sharedPreferences.getString("pref_email_name3", " ");
+ address.add(Eemail);
+ Eemail = sharedPreferences.getString("pref_email_name4", " ");
+ address.add(Eemail);
+
+ Runnable runnable = () -> mModel.initMediaManager(address);
+ new Thread(runnable).start();
+ break;
+ case 2:
+ Uri notification = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM);
+ Ringtone r = RingtoneManager.getRingtone(getApplicationContext(), notification);
+ startActivity(msg);
+ break;
+ case 3:
+
+ break;
+ }
}
// By default disable takeoff...
private Runnable enablesafety = new Runnable() {
@Override
public void run() {
- stat = false;
+ //stat = false;
mBtnSafety.callOnClick();
}
};
@@ -681,10 +938,11 @@ public class MainActivity extends AppCompatActivity implements OnMapReadyCallbac
private void notifyStatusChange() {
mDJIHandler.removeCallbacks(djiUpdateRunnable);
mDJIHandler.postDelayed(djiUpdateRunnable, 500);
- final BaseProduct product = RDApplication.getProductInstance();
+ mProduct = RDApplication.getProductInstance();
+// final BaseProduct product = RDApplication.getProductInstance();
- if (product != null && product.isConnected() && product.getModel() != null) {
- logMessageDJI(product.getModel().name() + " Connected ");
+ if (mProduct != null && mProduct.isConnected() && mProduct.getModel() != null) {
+ logMessageDJI(mProduct.getModel().name() + " Connected ");
} else {
logMessageDJI("Disconnected");
Log.e(TAG, "Video out 2: ");
@@ -700,7 +958,6 @@ public class MainActivity extends AppCompatActivity implements OnMapReadyCallbac
}
// Send raw H264 to the FFMPEG parser...
if (mExternalVideoOut == true) {
-// Log.e(TAG, "Video size:: "+size);
NativeHelper.getInstance().parse(videoBuffer, size, 0);
}
} else {
@@ -711,32 +968,46 @@ public class MainActivity extends AppCompatActivity implements OnMapReadyCallbac
}
};
- if (null == product || !product.isConnected()) {
- mCamera = null;
+ if (null == mProduct || !mProduct.isConnected()) {
+ mModel.m_camera = null; // Hmm to be investigated...
} else {
// List all models that needs alternative decoding...
- if (validateTranscodingMethod(product.getModel()) == true) {
+ if (validateTranscodingMethod(mProduct.getModel()) == true) {
m_videoMode = 2;
}
- if (!product.getModel().equals(Model.UNKNOWN_AIRCRAFT)) {
- mCamera = product.getCamera();
- if (mCamera != null) {
- mCamera.setMode(SettingsDefinitions.CameraMode.SHOOT_PHOTO, djiError -> {
+ if (!mProduct.getModel().equals(Model.UNKNOWN_AIRCRAFT)) {
+
+ if (mModel.m_camera != null) {
+
+ mModel.m_camera.setMode(SettingsDefinitions.CameraMode.SHOOT_PHOTO, djiError -> {
if (djiError != null) {
Log.e(TAG, "can't change mode of camera, error: " + djiError.getDescription());
logMessageDJI("can't change mode of camera, error: " + djiError.getDescription());
}
});
- }
-/*
- mCamera.setVideoResolutionAndFrameRate(new ResolutionAndFrameRate(SettingsDefinitions.VideoResolution.RESOLUTION_1280x720,SettingsDefinitions.VideoFrameRate.FRAME_RATE_25_FPS) , djiError -> {
- if (djiError != null) {
- Log.e(TAG, "can't change mode of camera, error: "+djiError);
- logMessageDJI("can't change mode of camera, error: "+djiError);
+ if (mProduct.getModel().equals(Model.MAVIC_AIR_2)){
+ mProduct.getCamera()
+ .setFlatMode(SettingsDefinitions.FlatCameraMode.PHOTO_SINGLE, djiError -> {
+ if (djiError != null) {
+ Log.e(TAG, "can't change mode of camera, error: " + djiError.getDescription());
+ logMessageDJI("can't change mode of camera, error: " + djiError.getDescription());
+ }
+ });
}
- });
-*/
+ else {
+ mModel.m_camera.setMode(SettingsDefinitions.CameraMode.SHOOT_PHOTO, djiError -> {
+ if (djiError != null) {
+ Log.e(TAG, "can't change mode of camera, error: " + djiError.getDescription());
+ logMessageDJI("can't change mode of camera, error: " + djiError.getDescription());
+ }
+ else{
+ logMessageDJI("Camera Mode set OK...");
+ }
+ });
+ }
+ }
+
//When calibration is needed or the fetch key frame is required by SDK, should use the provideTranscodedVideoFeed
//to receive the transcoded video feed from main camera.
if (mIsTranscodedVideoFeedNeeded) {
@@ -763,6 +1034,33 @@ public class MainActivity extends AppCompatActivity implements OnMapReadyCallbac
}
}
+
+ // Start the AI Pluggin (Developed by the customers...)
+ public boolean startActivity(String pluggin)
+ {
+ Intent intent = getPackageManager().getLaunchIntentForPackage(pluggin);
+ if (intent == null) {
+ // Bring user to the market or let them choose an app?
+ intent = new Intent(Intent.ACTION_VIEW);
+ intent.setData(Uri.parse("market://details?id=" + pluggin));
+ }
+ if (intent != null) {
+
+ if( mMavlinkReceiver.AIenabled == true){
+ intent.putExtra("password", "thisisrosettadrone246546101");
+ intent.putExtra("ip", AIaddress);
+ intent.putExtra("port", AIport);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity(intent);
+ }else{
+ logMessageDJI("AI not enabled!");
+ NotificationHandler.notifySnackbar(findViewById(R.id.snack),R.string.ai_not_active, LENGTH_LONG);
+
+ }
+ }
+ return true;
+ }
+
private boolean validateTranscodingMethod(Model model) {
// If the drone requires the old handling...
switch (model) {
@@ -776,6 +1074,7 @@ public class MainActivity extends AppCompatActivity implements OnMapReadyCallbac
case MAVIC_PRO:
case INSPIRE_1:
case Spark:
+ case MAVIC_AIR_2:
case INSPIRE_1_PRO:
case INSPIRE_1_RAW: // Verified...
case MAVIC_AIR: // Verified...
@@ -802,32 +1101,22 @@ public class MainActivity extends AppCompatActivity implements OnMapReadyCallbac
width = ((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 164, getResources().getDisplayMetrics()));
}
- /*else if(compare_height==0){
- width = LayoutParams.WRAP_CONTENT;
- height = LayoutParams.WRAP_CONTENT;
- }*/
videoViewWidth = width;
videoViewHeight = height;
Log.d(TAG, "real onSurfaceTextureAvailable: width " + videoViewWidth + " height " + videoViewHeight + " Mode: " + compare_height);
if (mCodecManager == null) {
mCodecManager = new DJICodecManager(getApplicationContext(), surface, width, height);
+ } else {
+ mCodecManager.changeOutputSurface(surface);
+ mCodecManager.onSurfaceSizeChanged(width, height, 0);
}
- /*
- else{
- mCodecManager.cleanSurface();
- mCodecManager.destroyCodec();
- mCodecManager = new DJICodecManager(getApplicationContext(), surface, width, height);
- }
- */
-
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
videoViewWidth = width;
videoViewHeight = height;
-
Log.d(TAG, "real onSurfaceTextureAvailable2: width " + videoViewWidth + " height " + videoViewHeight);
}
@@ -845,19 +1134,16 @@ public class MainActivity extends AppCompatActivity implements OnMapReadyCallbac
}
};
- //---------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------
private DJISDKManager.SDKManagerCallback mDJISDKManagerCallback = new DJISDKManager.SDKManagerCallback() {
@Override
- public void onDatabaseDownloadProgress(long x, long y) {
- }
+ public void onDatabaseDownloadProgress(long x, long y) {}
@Override
- public void onInitProcess(DJISDKInitEvent a, int x) {
- }
+ public void onInitProcess(DJISDKInitEvent a, int x) {}
@Override
public void onProductDisconnect() {
@@ -924,28 +1210,21 @@ public class MainActivity extends AppCompatActivity implements OnMapReadyCallbac
Log.e(TAG, error.toString());
}
}
-
};
//---------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------
//endregion
- /**
- *
- */
private void initLogs() {
-
FragmentManager fragmentManager = getSupportFragmentManager();
//Adapters in order: DJI, Outbound to GCS, Inbound to GCS
-
logDJI = new LogFragment();
logOutbound = new LogFragment();
logInbound = new LogFragment();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
-
fragmentTransaction.add(R.id.fragment_container, logDJI);
fragmentTransaction.add(R.id.fragment_container, logOutbound);
fragmentTransaction.add(R.id.fragment_container, logInbound);
@@ -976,7 +1255,6 @@ public class MainActivity extends AppCompatActivity implements OnMapReadyCallbac
byte[] buffer = new byte[BUF_LEN];
String zipName = "RD_LOG_" + android.text.format.DateFormat.format("yyyy-MM-dd-hh:mm:ss", new java.util.Date());
-
String[] fileNames = {"DJI_LOG", "OUTBOUND_LOG", "INBOUND_LOG"};
File directory = new File(Environment.getExternalStorageDirectory().getPath()
@@ -999,7 +1277,6 @@ public class MainActivity extends AppCompatActivity implements OnMapReadyCallbac
bufferedWriter.write(logDJI.getLogText());
} else if (file.getName().equals("OUTBOUND_LOG")) {
bufferedWriter.write(logOutbound.getLogText());
-
} else {
bufferedWriter.write(logInbound.getLogText());
}
@@ -1015,10 +1292,8 @@ public class MainActivity extends AppCompatActivity implements OnMapReadyCallbac
}
}
-
BufferedInputStream origin;
FileOutputStream dest = new FileOutputStream(dataFile);
-
ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(dest));
for (int i = 0; i < files.size(); i++) {
@@ -1044,7 +1319,6 @@ public class MainActivity extends AppCompatActivity implements OnMapReadyCallbac
out.finish();
out.close();
-
} catch (IOException e) {
Log.e(TAG, "ERROR ZIPPING LOGS", e);
}
@@ -1053,10 +1327,6 @@ public class MainActivity extends AppCompatActivity implements OnMapReadyCallbac
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
-// navState = mBottomNavigation.getSelectedItemId();
-// outState.putInt("navigation_state", navState);
-// Log.d(TAG, "SAVED NAVSTATE: " + navState);
-
}
private void deleteApplicationDirectory() {
@@ -1077,7 +1347,6 @@ public class MainActivity extends AppCompatActivity implements OnMapReadyCallbac
Log.d(TAG, "exception", e);
}
//File dir = new File(Environment.getExternalStorageDirectory()+"DJI/sq.rogue.rosettadrone");
-
}
/**
@@ -1093,7 +1362,6 @@ public class MainActivity extends AppCompatActivity implements OnMapReadyCallbac
// Log.d(TAG, "onActivityResult");
super.onActivityResult(reqCode, resCode, data);
if (reqCode == RESULT_SETTINGS && mGCSCommunicator != null && FLAG_PREFS_CHANGED) {
-
if (FLAG_TELEMETRY_ADDRESS_CHANGED) {
mGCSCommunicator.renewDatalinks();
if (mExternalVideoOut == false) {
@@ -1113,7 +1381,6 @@ public class MainActivity extends AppCompatActivity implements OnMapReadyCallbac
FLAG_VIDEO_ADDRESS_CHANGED = false;
}
setDroneParameters();
-
FLAG_PREFS_CHANGED = false;
}
}
@@ -1146,6 +1413,11 @@ public class MainActivity extends AppCompatActivity implements OnMapReadyCallbac
case R.id.action_gui:
onClickGUI();
break;
+ case R.id.action_AI:
+ Uri notification = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM);
+ Ringtone r = RingtoneManager.getRingtone(getApplicationContext(), notification);
+ startActivity("com.example.remoteconfig4");
+ break;
default:
return false;
}
@@ -1162,87 +1434,56 @@ public class MainActivity extends AppCompatActivity implements OnMapReadyCallbac
logMessageDJI("Set Small screen...");
videostreamPreviewTtView.clearFocus();
videostreamPreviewTtView.setVisibility(View.GONE);
-/*
- if ( mCodecManager != null) {
- mCodecManager.cleanSurface();
- mCodecManager.destroyCodec();
- mCodecManager=null;
- }
-*/
+
// safeSleep(200);
videostreamPreviewTtViewSmall.setSurfaceTextureListener(mSurfaceTextureListener);
videostreamPreviewTtViewSmall.setVisibility(View.VISIBLE);
-// videostreamPreviewTtViewSmall.setAlpha((float)0.4);
- // videostreamPreviewTtViewSmall.requestFocus(); // .buildLayer();
-
- // videostreamPreviewTtView.clearFocus(); //
-/*
- videoViewHeight = ((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 86, getResources().getDisplayMetrics()));
- videoViewWidth = ((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 164, getResources().getDisplayMetrics()));
- mCodecManager = new DJICodecManager(getApplicationContext(), surfaceT, videoViewWidth, videoViewHeight);
- videostreamPreviewTtViewSmall.setSurfaceTexture(surfaceT);
- videostreamPreviewTtViewSmall.setSurfaceTextureListener(mSurfaceTextureListener);
- videostreamPreviewTtViewSmall.setVisibility(View.VISIBLE);
-*/
video_layout_small.setZ(100.f);
- map_layout.setZ(0.f);
+ // MAP ok...
+ map_layout.setZ(0.f);
map_para.height = LayoutParams.WRAP_CONTENT;
map_para.width = LayoutParams.WRAP_CONTENT;
map_layout.setLayoutParams(map_para);
+ if (mCodecManager != null) {
+ mCodecManager.changeOutputSurface(videostreamPreviewTtViewSmall.getSurfaceTexture());
+ mCodecManager.onSurfaceSizeChanged(videostreamPreviewTtViewSmall.getWidth(), videostreamPreviewTtViewSmall.getHeight(), 0);
+ }
+
compare_height = 1;
} else {
logMessageDJI("Set Main screen...");
videostreamPreviewTtViewSmall.clearFocus();
videostreamPreviewTtViewSmall.setVisibility(View.GONE);
-/*
- if ( mCodecManager != null) {
- mCodecManager.cleanSurface();
- mCodecManager.destroyCodec();
- mCodecManager = null;
- }
-*/
+
// safeSleep(200);
videostreamPreviewTtView.setSurfaceTextureListener(mSurfaceTextureListener);
- // videostreamPreviewTtView.buildLayer();
videostreamPreviewTtView.setVisibility(View.VISIBLE);
- // videostreamPreviewTtView.requestFocus();
- // videostreamPreviewTtViewSmall.setAlpha((float)0.4);
-
-
- // videostreamPreviewTtViewSmall.setSurfaceTextureListener(mSurfaceTextureListener);
- // mSurfaceTextureListener.onSurfaceTextureAvailable(surfaceT, videoViewWidth, videoViewHeight);
- // videostreamPreviewTtView.setVisibility(View.VISIBLE);
-
- // videostreamPreviewTtView.setSurfaceTextureListener(mSurfaceTextureListener);
- // videostreamPreviewTtViewSmall.clearFocus(); //
-
-
video_layout_small.setZ(0.f);
- map_layout.setZ(100.f);
+ //MAP OK...
+ map_layout.setZ(100.f);
map_para.height = ((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 86, getResources().getDisplayMetrics()));
map_para.width = ((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 164, getResources().getDisplayMetrics()));
map_layout.setLayoutParams(map_para);
map_layout.setBottom(((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10, getResources().getDisplayMetrics())));
map_layout.setLeft(((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 5, getResources().getDisplayMetrics())));
-// com.amap.api.maps.MapView map = findViewById(R.id.map);
-// map.setAlpha((float)0.99);
+ if (mCodecManager != null) {
+ mCodecManager.changeOutputSurface(videostreamPreviewTtView.getSurfaceTexture());
+ mCodecManager.onSurfaceSizeChanged(videostreamPreviewTtView.getWidth(), videostreamPreviewTtView.getHeight(), 0);
+ }
+
compare_height = 0;
}
-
-// v.bringToFront();
v.setZ(101.f);
-
-
+ // updateDroneLocation();
}
// Hmm is this ever called...
@Override
public boolean onContextItemSelected(MenuItem item) {
- // AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
logMessageDJI("Config ..." + item);
switch (item.getItemId()) {
case R.id.action_clear_logs:
@@ -1341,17 +1582,14 @@ public class MainActivity extends AppCompatActivity implements OnMapReadyCallbac
while (!mModel.loadParamsFromDJI()) {
safeSleep(100);
}
-
sendDroneConnected();
final Drawable connectedDrawable = getResources().getDrawable(R.drawable.ic_baseline_connected_24px, null);
-
runOnUiThread(() -> {
ImageView djiImageView = findViewById(R.id.dji_conn);
djiImageView.setBackground(connectedDrawable);
djiImageView.invalidate();
});
-
}
private void onDroneDisconnected() {
@@ -1363,13 +1601,10 @@ public class MainActivity extends AppCompatActivity implements OnMapReadyCallbac
imageView.invalidate();
});
-
logMessageDJI("Drone disconnected");
mModel.setDjiAircraft(null);
closeGCSCommunicator();
-
sendDroneDisconnected();
-
}
private void closeGCSCommunicator() {
@@ -1382,7 +1617,6 @@ public class MainActivity extends AppCompatActivity implements OnMapReadyCallbac
private void loadMockParamFile() {
mModel.getParams().clear();
try {
-
AssetManager am = getAssets();
InputStream is = am.open("DJIMock.txt");
InputStreamReader inputStreamReader = new InputStreamReader(is);
@@ -1411,7 +1645,6 @@ public class MainActivity extends AppCompatActivity implements OnMapReadyCallbac
//region Interface to VideoService
//---------------------------------------------------------------------------------------
-
public void logMessageFromGCS(String msg) {
if (prefs.getBoolean("pref_log_mavlink", false))
mNewInbound += "\n" + msg;
@@ -1438,7 +1671,6 @@ public class MainActivity extends AppCompatActivity implements OnMapReadyCallbac
// Going to reset all the flags so everything is set.
// Assume its a new drone.
-
FLAG_DRONE_RTL_ALTITUDE_CHANGED = true;
FLAG_DRONE_SMART_RTL_CHANGED = true;
FLAG_DRONE_MULTI_MODE_CHANGED = true;
@@ -1453,11 +1685,9 @@ public class MainActivity extends AppCompatActivity implements OnMapReadyCallbac
setDroneParameters();
logMessageDJI("Starting Video link to " + videoIP + ":" + videoPort);
-
}
private void sendDroneDisconnected() {
-
}
private String getVideoIP() {
@@ -1498,6 +1728,19 @@ public class MainActivity extends AppCompatActivity implements OnMapReadyCallbac
mModel.setMaxHeight(Integer.parseInt(Objects.requireNonNull(prefs.getString("pref_drone_max_height", "500"))));
FLAG_DRONE_MAX_HEIGHT_CHANGED = false;
}
+ if(FLAG_DRONE_AI_IP_CHANGED) {
+ mModel.setAiIP(prefs.getString("pref_ai_ip", "127.0.0.1"));
+ FLAG_DRONE_AI_IP_CHANGED = false;
+ }
+ if(FLAG_DRONE_AI_PORT_CHANGED) {
+ mModel.setAiPort(Integer.parseInt(Objects.requireNonNull(prefs.getString("pref_ai_port", "2000"))));
+ FLAG_DRONE_AI_PORT_CHANGED = false;
+
+ }
+ if(FLAG_DRONE_AI_ENABLED_CHANGED) {
+ mModel.setAIenable(prefs.getBoolean("pref_ai_telemetry_enabled", true));
+ FLAG_DRONE_AI_ENABLED_CHANGED = false;
+ }
if (FLAG_DRONE_SMART_RTL_CHANGED) {
mModel.setSmartRTLEnabled(prefs.getBoolean("pref_drone_smart_rtl", true));
@@ -1535,18 +1778,11 @@ public class MainActivity extends AppCompatActivity implements OnMapReadyCallbac
aMap.setMapType(mMaptype);
FLAG_MAPS_CHANGED = false;
}
-
}
//---------------------------------------------------------------------------------------
//endregion
- //region GCS Timer Task
- //---------------------------------------------------------------------------------------
-
- //---------------------------------------------------------------------------------------
- //endregion
-
@Override
protected void onStart() {
super.onStart();
@@ -1557,9 +1793,9 @@ public class MainActivity extends AppCompatActivity implements OnMapReadyCallbac
Log.e(TAG, "onPointerCaptureChanged");
}
-
//region GCS Timer Task
//---------------------------------------------------------------------------------------
+ //---------------------------------------------------------------------------------------
private static class GCSSenderTimerTask extends TimerTask {
@@ -1567,7 +1803,6 @@ public class MainActivity extends AppCompatActivity implements OnMapReadyCallbac
GCSSenderTimerTask(WeakReference mainActivityWeakReference) {
this.mainActivityWeakReference = mainActivityWeakReference;
-
}
@Override
@@ -1601,7 +1836,7 @@ public class MainActivity extends AppCompatActivity implements OnMapReadyCallbac
@Override
protected Integer doInBackground(Integer... ints2) {
-// Log.d("RDTHREADS", "doInBackground()");
+ Log.d("TERJE", "doInBackground()");
try {
createTelemetrySocket();
@@ -1670,6 +1905,8 @@ public class MainActivity extends AppCompatActivity implements OnMapReadyCallbac
MAVLinkMessage msg = packet.unpack();
if (mainActivityWeakReference.get().prefs.getBoolean("pref_log_mavlink", false))
mainActivityWeakReference.get().logMessageFromGCS(msg.toString());
+
+ // Log.d("TERJE", "process...()");
mainActivityWeakReference.get().mMavlinkReceiver.process(msg);
}
}
@@ -1792,20 +2029,39 @@ public class MainActivity extends AppCompatActivity implements OnMapReadyCallbac
}
//---------------------------------------------------------------------------------------
+ // Receive alternative H.264 video data, mostly for debugging or simulation...
+ private void startVideoLoop(int port){
+ if(!mExtRunning) {
+ mExtRunning = true;
+ runOnUiThread(() -> {
+ try {
+ DatagramSocket clientsocket = new DatagramSocket(port);
+ byte[] receivedata = new byte[1024 * 64];
+ while (true) {
+ DatagramPacket recv_packet = new DatagramPacket(receivedata, receivedata.length);
+ clientsocket.receive(recv_packet);
+ if (mCodecManager != null) {
+ mCodecManager.sendDataToDecoder(recv_packet.getData(), recv_packet.getData().length);
+ }
+ // Send raw H264 to the FFMPEG parser...
+ NativeHelper.getInstance().parse(recv_packet.getData(), recv_packet.getData().length, 0);
+ }
+ } catch (Exception e) {
+ Log.e("UDP", "S: Error", e);
+ }
+ });
+ mExtRunning = false;
+ }
+ }
// --------------------------------------------------------------------------------------------
// To be moved later for better structure....
-
private boolean isTranscodedVideoFeedNeeded() {
-
if (VideoFeeder.getInstance() == null) {
return false;
}
-
return VideoFeeder.getInstance().isFetchKeyFrameNeeded() || VideoFeeder.getInstance()
.isLensDistortionCalibrationNeeded();
}
- //---------------------------------------------------------------------------------------
- //endregion
}
diff --git a/app/src/main/java/sq/rogue/rosettadrone/RDApplication.java b/app/src/main/java/sq/rogue/rosettadrone/RDApplication.java
index 99b8134..4c0148f 100644
--- a/app/src/main/java/sq/rogue/rosettadrone/RDApplication.java
+++ b/app/src/main/java/sq/rogue/rosettadrone/RDApplication.java
@@ -7,6 +7,9 @@ import com.secneo.sdk.Helper;
import dji.sdk.base.BaseProduct;
import dji.sdk.sdkmanager.DJISDKManager;
+import dji.sdk.camera.Camera;
+import dji.sdk.products.Aircraft;
+import dji.sdk.products.HandHeld;
public class RDApplication extends Application {
@@ -59,6 +62,17 @@ public class RDApplication extends Application {
simulatorApplication.setContext(this);
}
}
+
+ public static synchronized Camera getCameraInstance() {
+ if (getProductInstance() == null) return null;
+ Camera camera = null;
+ if (getProductInstance() instanceof Aircraft){
+ camera = ((Aircraft) getProductInstance()).getCamera();
+ } else if (getProductInstance() instanceof HandHeld) {
+ camera = ((HandHeld) getProductInstance()).getCamera();
+ }
+ return camera;
+ }
}
diff --git a/app/src/main/java/sq/rogue/rosettadrone/settings/GMailSender.java b/app/src/main/java/sq/rogue/rosettadrone/settings/GMailSender.java
new file mode 100644
index 0000000..8d1cffa
--- /dev/null
+++ b/app/src/main/java/sq/rogue/rosettadrone/settings/GMailSender.java
@@ -0,0 +1,113 @@
+// Does not work...
+
+package sq.rogue.rosettadrone.settings;
+
+//import android.se.omapi.Session;
+
+import android.util.Log;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.PasswordAuthentication;
+import java.security.Security;
+import java.util.Properties;
+
+import javax.activation.DataHandler;
+import javax.activation.DataSource;
+import javax.mail.Message;
+import javax.mail.Session;
+import javax.mail.Transport;
+import javax.mail.internet.InternetAddress;
+import javax.mail.internet.MimeMessage;
+
+public class GMailSender extends javax.mail.Authenticator {
+ private String mailhost = "smtp.gmail.com";
+ private String user;
+ private String password;
+ private Session session;
+
+ static {
+ java.security.Security.addProvider(new JSSEProvider());
+ }
+
+ public GMailSender(String user, String password) {
+ this.user = user;
+ this.password = password;
+
+ Properties props = new Properties();
+ props.setProperty("mail.transport.protocol", "smtp");
+ props.setProperty("mail.host", mailhost);
+ props.put("mail.smtp.auth", "true");
+ props.put("mail.smtp.port", "465");
+ props.put("mail.smtp.socketFactory.port", "465");
+ props.put("mail.smtp.socketFactory.class",
+ "javax.net.ssl.SSLSocketFactory");
+ props.put("mail.smtp.socketFactory.fallback", "false");
+ props.setProperty("mail.smtp.quitwait", "false");
+
+ session = Session.getDefaultInstance(props, this);
+ }
+
+ protected javax.mail.PasswordAuthentication getPasswordAuthentication() {
+ return new javax.mail.PasswordAuthentication(user, password);
+ }
+
+ public synchronized void sendMail(String subject, String body, String sender, String recipients) throws Exception {
+ try{
+ MimeMessage message = new MimeMessage(session);
+ DataHandler handler = new DataHandler(new ByteArrayDataSource(body.getBytes(), "text/plain"));
+ message.setSender(new InternetAddress(sender));
+ message.setSubject(subject);
+ message.setDataHandler(handler);
+ if (recipients.indexOf(',') > 0)
+ message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(recipients));
+ else
+ message.setRecipient(Message.RecipientType.TO, new InternetAddress(recipients));
+ Transport.send(message);
+ } catch (Exception e) {
+ e.printStackTrace();
+ Log.d("TEST","Error: "+ e.toString());
+ }
+ }
+
+ public class ByteArrayDataSource implements DataSource {
+ private byte[] data;
+ private String type;
+
+ public ByteArrayDataSource(byte[] data, String type) {
+ super();
+ this.data = data;
+ this.type = type;
+ }
+
+ public ByteArrayDataSource(byte[] data) {
+ super();
+ this.data = data;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public String getContentType() {
+ if (type == null)
+ return "application/octet-stream";
+ else
+ return type;
+ }
+
+ public InputStream getInputStream() throws IOException {
+ return new ByteArrayInputStream(data);
+ }
+
+ public String getName() {
+ return "ByteArrayDataSource";
+ }
+
+ public OutputStream getOutputStream() throws IOException {
+ throw new IOException("Not Supported");
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/sq/rogue/rosettadrone/settings/JSSEProvider.java b/app/src/main/java/sq/rogue/rosettadrone/settings/JSSEProvider.java
new file mode 100644
index 0000000..ae74a1b
--- /dev/null
+++ b/app/src/main/java/sq/rogue/rosettadrone/settings/JSSEProvider.java
@@ -0,0 +1,46 @@
+package sq.rogue.rosettadrone.settings;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @author Alexander Y. Kleymenov
+ * @version $Revision$
+ */
+
+
+import java.security.AccessController;
+ import java.security.Provider;
+
+public final class JSSEProvider extends Provider {
+
+ public JSSEProvider() {
+ super("HarmonyJSSE", 1.0, "Harmony JSSE Provider");
+ AccessController.doPrivileged(new java.security.PrivilegedAction() {
+ public Void run() {
+ put("SSLContext.TLS",
+ "org.apache.harmony.xnet.provider.jsse.SSLContextImpl");
+ put("Alg.Alias.SSLContext.TLSv1", "TLS");
+ put("KeyManagerFactory.X509",
+ "org.apache.harmony.xnet.provider.jsse.KeyManagerFactoryImpl");
+ put("TrustManagerFactory.X509",
+ "org.apache.harmony.xnet.provider.jsse.TrustManagerFactoryImpl");
+ return null;
+ }
+ });
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/sq/rogue/rosettadrone/settings/MailReport.java b/app/src/main/java/sq/rogue/rosettadrone/settings/MailReport.java
new file mode 100644
index 0000000..6223c0b
--- /dev/null
+++ b/app/src/main/java/sq/rogue/rosettadrone/settings/MailReport.java
@@ -0,0 +1,115 @@
+package sq.rogue.rosettadrone.settings;
+
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Intent;
+import android.net.Uri;
+import android.provider.MediaStore;
+import android.util.Log;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import javax.mail.Session;
+
+import dji.sdk.media.MediaFile;
+import sq.rogue.rosettadrone.MainActivity;
+
+
+//public class MailReport extends AsyncTask { // extends AppCompatActivity {
+public class MailReport extends javax.mail.Authenticator{
+
+ private final String TAG = MailReport.class.getSimpleName();
+ private MainActivity parent;
+
+ private ContentResolver contentResolver;
+ private File imageDirectory;
+
+ //---------------------------------------------------------------------------------------
+ public MailReport(MainActivity parent, ContentResolver resolver)
+ {
+ this.parent = parent;
+ contentResolver = resolver;
+ }
+
+ //---------------------------------------------------------------------------------------
+ private ArrayList getUriListForImages(String filename) throws Exception
+ {
+ ArrayList myList = new ArrayList();
+ String[] fileList = imageDirectory.list();
+
+ if(fileList.length != 0) {
+ Log.d(TAG,"FileLength: "+fileList.length);
+ for(int i=0; i "+fileList[i]+" err: "+err);
+
+ if(err==0) {
+ Log.d(TAG,"Found... ");
+ try {
+ ContentValues values = new ContentValues(7);
+ values.put(MediaStore.Images.Media.TITLE, fileList[i]);
+ values.put(MediaStore.Images.Media.DISPLAY_NAME, fileList[i]);
+ values.put(MediaStore.Images.Media.DATE_TAKEN, new Date().getTime());
+ values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
+ values.put(MediaStore.Images.ImageColumns.BUCKET_ID, imageDirectory.getAbsolutePath().hashCode());
+ values.put(MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME, fileList[i]);
+ values.put("_data", imageDirectory.getAbsolutePath() + "/" + fileList[i]);
+ Uri uri = contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
+ myList.add(uri);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+ Log.d(TAG,"Uri: "+myList);
+ return myList;
+ }
+
+ //---------------------------------------------------------------------------------------
+ public Intent createEmail(List to, String subject, String message, double lat, double lon, double alt, double head, String filename, String dir)
+ {
+
+ imageDirectory = new File(dir);
+
+ message+="\nAt position Lat: "+lat+" Lon: "+lon+" Alt: "+alt + " Heading: "+head;
+ //message+="\nURL: https://www.google.com/maps/search/?api=1&query="+lat+","+lon+"\n";
+ message+="\nURL: http://maps.google.com/maps?z=1&t=h&q=loc:"+lat+","+lon+"\n";
+
+ Intent email = new Intent(Intent.ACTION_SEND_MULTIPLE);
+ email.setData(Uri.parse("mailto:")); // only email apps should handle this
+
+ String to_address = "";
+ for(String address:to){
+ if(address.length() > 3) {
+ to_address=to_address+", "+address;
+ }
+ }
+ Log.d(TAG, "Mail to use: " + to_address);
+ email.putExtra(Intent.EXTRA_EMAIL, new String[]{to_address});
+
+ email.putExtra(Intent.EXTRA_SUBJECT, subject);
+ email.putExtra(Intent.EXTRA_TEXT, message);
+
+ try {
+ Log.d(TAG,"File: "+ filename);
+ ArrayList uriList = getUriListForImages(filename);
+ email.putExtra(Intent.EXTRA_STREAM, uriList);
+ } catch (Exception e){
+ Log.d(TAG,"Missing file: "+ e.toString());
+ }
+
+ //need this to prompts email client only
+ email.setType("message/rfc822");
+ // email.setType("text/html");
+
+ Log.d(TAG,"Email: "+ email);
+ return email;
+ }
+//---------------------------------------------------------------------------------------------
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/sq/rogue/rosettadrone/settings/MapActivity.java b/app/src/main/java/sq/rogue/rosettadrone/settings/MapActivity.java
index a0428fb..e389fee 100644
--- a/app/src/main/java/sq/rogue/rosettadrone/settings/MapActivity.java
+++ b/app/src/main/java/sq/rogue/rosettadrone/settings/MapActivity.java
@@ -56,27 +56,19 @@ import sq.rogue.rosettadrone.DJISimulatorApplication;
import sq.rogue.rosettadrone.R;
import sq.rogue.rosettadrone.RDApplication;
-//public class HelpActivity extends AppCompatActivity implements OnMapReadyCallback{
//public class HelpActivity extends AppCompatActivity implements PreferenceFragmentCompat.OnPreferenceStartScreenCallback, OnMapReadyCallback{
public class MapActivity extends FragmentActivity implements View.OnClickListener, GoogleMap.OnMapClickListener, OnMapReadyCallback {
private static final String TAG = MapActivity.class.getSimpleName();
-
private GoogleMap gMap;
-
private Button locate, add, clear, exit;
private Button config, upload, start, stop;
-
private boolean isAdd = false;
-
private double droneLocationLat = 30, droneLocationLng = 60;
private final Map mMarkers = new ConcurrentHashMap();
private Marker droneMarker = null;
-
private float altitude = 100.0f;
private float mSpeed = 10.0f;
-
private List waypointList = new ArrayList<>();
-
public static WaypointMission.Builder waypointMissionBuilder;
private FlightController mFlightController;
private WaypointMissionOperator instance;
@@ -96,8 +88,8 @@ public class MapActivity extends FragmentActivity implements View.OnClickListene
@Override
protected void onDestroy() {
- if (mReceiver.getResultCode() < 0)
- unregisterReceiver(mReceiver);
+ // if (mReceiver.getResultCode() < 0)
+ // unregisterReceiver(mReceiver);
removeListener();
super.onDestroy();
}
diff --git a/app/src/main/java/sq/rogue/rosettadrone/settings/SettingsFragment.java b/app/src/main/java/sq/rogue/rosettadrone/settings/SettingsFragment.java
index 3d7f08b..33c2da9 100644
--- a/app/src/main/java/sq/rogue/rosettadrone/settings/SettingsFragment.java
+++ b/app/src/main/java/sq/rogue/rosettadrone/settings/SettingsFragment.java
@@ -196,7 +196,43 @@ public class SettingsFragment extends PreferenceFragmentCompat implements Shared
}
});
- findPreference("pref_sim_pos_lat").setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
+ findPreference("pref_email_name1").setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ MainActivity.FLAG_PREFS_CHANGED = true;
+ MainActivity.FLAG_APP_REPORT_EMAIL = true;
+ return true;
+ }
+ });
+ findPreference("pref_email_name2").setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ MainActivity.FLAG_PREFS_CHANGED = true;
+ MainActivity.FLAG_APP_REPORT_EMAIL = true;
+ return true;
+ }
+ });
+
+ findPreference("pref_email_name3").setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ MainActivity.FLAG_PREFS_CHANGED = true;
+ MainActivity.FLAG_APP_REPORT_EMAIL = true;
+ return true;
+ }
+ });
+
+ findPreference("pref_email_name4").setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ MainActivity.FLAG_PREFS_CHANGED = true;
+ MainActivity.FLAG_APP_REPORT_EMAIL = true;
+ return true;
+ }
+ });
+
+
+ findPreference("pref_external_video").setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
try {
@@ -212,11 +248,28 @@ public class SettingsFragment extends PreferenceFragmentCompat implements Shared
return false;
}
});
+
+ findPreference("pref_sim_pos_lat").setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ try {
+ if (Double.parseDouble((String) newValue) >= -90 && Double.parseDouble((String) newValue) <= 90) {
+ MainActivity.FLAG_PREFS_CHANGED = true;
+ MainActivity.FLAG_VIDEO_ADDRESS_CHANGED = true;
+ return true;
+ }
+ } catch (NumberFormatException ignored) {
+ }
+ NotificationHandler.notifyAlert(SettingsFragment.this.getActivity(), TYPE_APP_NAME,
+ null, null);
+ return false;
+ }
+ });
findPreference("pref_sim_pos_lon").setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
try {
- if (Integer.parseInt((String) newValue) >= 1 && Integer.parseInt((String) newValue) <= 65535) {
+ if (Double.parseDouble((String) newValue) >= 0 && Double.parseDouble((String) newValue) <= 180) {
MainActivity.FLAG_PREFS_CHANGED = true;
MainActivity.FLAG_VIDEO_ADDRESS_CHANGED = true;
return true;
diff --git a/app/src/main/java/sq/rogue/rosettadrone/settings/Waypoint1Activity.java b/app/src/main/java/sq/rogue/rosettadrone/settings/Waypoint1Activity.java
index 477ebaf..2ccf8c0 100644
--- a/app/src/main/java/sq/rogue/rosettadrone/settings/Waypoint1Activity.java
+++ b/app/src/main/java/sq/rogue/rosettadrone/settings/Waypoint1Activity.java
@@ -97,7 +97,7 @@ public class Waypoint1Activity extends FragmentActivity implements View.OnClickL
@Override
protected void onDestroy() {
- unregisterReceiver(mReceiver);
+ // unregisterReceiver(mReceiver);
removeListener();
super.onDestroy();
}
@@ -161,9 +161,9 @@ public class Waypoint1Activity extends FragmentActivity implements View.OnClickL
setContentView(R.layout.activity_waypoint1);
- IntentFilter filter = new IntentFilter();
- filter.addAction(DJISimulatorApplication.FLAG_CONNECTION_CHANGE);
- registerReceiver(mReceiver, filter);
+ // IntentFilter filter = new IntentFilter();
+ // filter.addAction(DJISimulatorApplication.FLAG_CONNECTION_CHANGE);
+ // registerReceiver(mReceiver, filter);
initUI();
@@ -311,7 +311,7 @@ public class Waypoint1Activity extends FragmentActivity implements View.OnClickL
//Create MarkerOptions object
final MarkerOptions markerOptions = new MarkerOptions();
markerOptions.position(pos);
- markerOptions.icon(BitmapDescriptorFactory.fromResource(R.drawable.aircraft));
+ markerOptions.icon(BitmapDescriptorFactory.fromResource(R.drawable.ic_drone));
runOnUiThread(new Runnable() {
@Override
diff --git a/app/src/main/java/sq/rogue/rosettadrone/settings/Waypoint2Activity.java b/app/src/main/java/sq/rogue/rosettadrone/settings/Waypoint2Activity.java
index d01abe2..77190ae 100644
--- a/app/src/main/java/sq/rogue/rosettadrone/settings/Waypoint2Activity.java
+++ b/app/src/main/java/sq/rogue/rosettadrone/settings/Waypoint2Activity.java
@@ -116,7 +116,7 @@ public class Waypoint2Activity extends FragmentActivity implements View.OnClickL
@Override
protected void onDestroy() {
- unregisterReceiver(mReceiver);
+ // unregisterReceiver(mReceiver);
removeListener();
super.onDestroy();
}
@@ -198,9 +198,9 @@ public class Waypoint2Activity extends FragmentActivity implements View.OnClickL
setContentView(R.layout.activity_waypoint2);
- IntentFilter filter = new IntentFilter();
- filter.addAction(DJISimulatorApplication.FLAG_CONNECTION_CHANGE);
- registerReceiver(mReceiver, filter);
+ // IntentFilter filter = new IntentFilter();
+ // filter.addAction(DJISimulatorApplication.FLAG_CONNECTION_CHANGE);
+ // registerReceiver(mReceiver, filter);
if (getWaypointMissionOperator() == null) {
setResultToToast("Not support Waypoint2.0");
@@ -426,7 +426,7 @@ public class Waypoint2Activity extends FragmentActivity implements View.OnClickL
//Create MarkerOptions object
final MarkerOptions markerOptions = new MarkerOptions();
markerOptions.position(pos);
- markerOptions.icon(BitmapDescriptorFactory.fromResource(R.drawable.aircraft));
+ markerOptions.icon(BitmapDescriptorFactory.fromResource(R.drawable.ic_drone));
runOnUiThread(new Runnable() {
@Override
@@ -718,7 +718,7 @@ public class Waypoint2Activity extends FragmentActivity implements View.OnClickL
.setMissionID(new Random().nextInt(65535))
.setFinishedAction(mFinishedAction)
.setGotoFirstWaypointMode(firstMode)
- .setMaxFlightSpeed(mSpeed)
+ .setMaxFlightSpeed((float)12.0) //mSpeed)
.setAutoFlightSpeed(mSpeed);
getWaypointMissionOperator().loadMission(waypointMissionBuilder.build(), new CommonCallbacks.CompletionCallback() {
diff --git a/app/src/main/java/sq/rogue/rosettadrone/settings/drone/DroneSettingsFragment.java b/app/src/main/java/sq/rogue/rosettadrone/settings/drone/DroneSettingsFragment.java
index 0bb29bd..55b2991 100644
--- a/app/src/main/java/sq/rogue/rosettadrone/settings/drone/DroneSettingsFragment.java
+++ b/app/src/main/java/sq/rogue/rosettadrone/settings/drone/DroneSettingsFragment.java
@@ -82,6 +82,7 @@ public class DroneSettingsFragment extends PreferenceFragmentCompat implements S
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
setPreferencesFromResource(R.xml.preferences, rootKey);
+
setListeners();
}
@@ -152,10 +153,44 @@ public class DroneSettingsFragment extends PreferenceFragmentCompat implements S
return false;
}
});
- findPreference("pref_drone_multi_mode").setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
+
+ findPreference("pref_ai_port").setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ try {
+ if (Integer.parseInt((String) newValue) >= 1000 && Integer.parseInt((String) newValue) <= 32000) {
+ MainActivity.FLAG_PREFS_CHANGED = true;
+ MainActivity.FLAG_DRONE_AI_PORT_CHANGED = true;
+ return true;
+ }
+ } catch (NumberFormatException ignored) {
+ }
+ return false;
+ }
+ });
+ findPreference("pref_ai_telemetry_enabled").setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
+ MainActivity.FLAG_PREFS_CHANGED = true;
+ MainActivity.FLAG_DRONE_AI_ENABLED_CHANGED = true;
+ return true;
+ }
+ });
+ findPreference("pref_ai_ip").setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+
+ MainActivity.FLAG_PREFS_CHANGED = true;
+ MainActivity.FLAG_DRONE_AI_IP_CHANGED = true;
+ return true;
+ }
+ });
+
+ findPreference("pref_drone_multi_mode").setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue)
+ {
MainActivity.FLAG_PREFS_CHANGED = true;
MainActivity.FLAG_DRONE_MULTI_MODE_CHANGED = true;
return true;
@@ -163,8 +198,8 @@ public class DroneSettingsFragment extends PreferenceFragmentCompat implements S
});
findPreference("pref_drone_leds").setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
- public boolean onPreferenceChange(Preference preference, Object newValue) {
-
+ public boolean onPreferenceChange(Preference preference, Object newValue)
+ {
MainActivity.FLAG_PREFS_CHANGED = true;
MainActivity.FLAG_DRONE_LEDS_CHANGED = true;
return true;
@@ -183,7 +218,6 @@ public class DroneSettingsFragment extends PreferenceFragmentCompat implements S
findPreference("pref_drone_flight_path_radius").setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
-
try {
if (Float.parseFloat((String) newValue) >= .2f && Float.parseFloat((String) newValue) <= 1000) {
MainActivity.FLAG_PREFS_CHANGED = true;
diff --git a/app/src/main/java/sq/rogue/rosettadrone/video/RtpSocket.java b/app/src/main/java/sq/rogue/rosettadrone/video/RtpSocket.java
index 0efceaa..d39c5c6 100755
--- a/app/src/main/java/sq/rogue/rosettadrone/video/RtpSocket.java
+++ b/app/src/main/java/sq/rogue/rosettadrone/video/RtpSocket.java
@@ -204,8 +204,10 @@ public class RtpSocket implements Runnable {
if (dport != 0 && rtcpPort != 0) {
if(dest.isMulticastAddress()) {
mTransport = TRANSPORT_MULTICAST;
+ Log.d(TAG, "Use Multicast...");
}else {
mTransport = TRANSPORT_UDP;
+ Log.d(TAG, "Use Unicast...");
}
mPort = dport;
Log.d(TAG, "setDestination: " + dest + ":" + dport);
diff --git a/app/src/main/res/drawable/drone_img.png b/app/src/main/res/drawable/drone_img.png
new file mode 100644
index 0000000..ffda339
Binary files /dev/null and b/app/src/main/res/drawable/drone_img.png differ
diff --git a/app/src/main/res/drawable/pilot.png b/app/src/main/res/drawable/pilot.png
new file mode 100644
index 0000000..92209cb
Binary files /dev/null and b/app/src/main/res/drawable/pilot.png differ
diff --git a/app/src/main/res/drawable/quadcopter_start.png b/app/src/main/res/drawable/quadcopter_start.png
new file mode 100644
index 0000000..fb310be
Binary files /dev/null and b/app/src/main/res/drawable/quadcopter_start.png differ
diff --git a/app/src/main/res/drawable/return_home.png b/app/src/main/res/drawable/return_home.png
new file mode 100644
index 0000000..76afac3
Binary files /dev/null and b/app/src/main/res/drawable/return_home.png differ
diff --git a/app/src/main/res/layout/activity_connection.xml b/app/src/main/res/layout/activity_connection.xml
index ad4a284..890c087 100755
--- a/app/src/main/res/layout/activity_connection.xml
+++ b/app/src/main/res/layout/activity_connection.xml
@@ -1,5 +1,6 @@
-
-
-
+
diff --git a/app/src/main/res/menu/toolbar_menu.xml b/app/src/main/res/menu/toolbar_menu.xml
index 7c36fba..17b1010 100644
--- a/app/src/main/res/menu/toolbar_menu.xml
+++ b/app/src/main/res/menu/toolbar_menu.xml
@@ -17,6 +17,13 @@
android:onClick="onMenuItemClick"
android:title="@string/settings" />
+
+
-
ID
+ Email Address 1
+ Email Address 2
+ Email Address 3
+ Email Address 4
RTL/RTH
@@ -83,7 +87,10 @@
GCS IP address
+ AI Assist IP address
Telemetry port
+ AI Assist port
+ Enable AI Assist
Enable secondary telemetry output
Secondary telemetry IP
Secondary telemetry port
@@ -134,14 +141,20 @@
SAFETY ENABLED
SAFETY DISABLED - READY TO FLY
Disable safety to takeoff.
+ Please wait while image is grabbed!
+ AI not activated in setup menu!
+ AI mission sent to AI module!
+ Report Sent!
Settings
+ AI Settings
Drone Configuration
Telemetry
+ AI Assist
Video
Logs
diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml
index 04e28d3..83d19bb 100644
--- a/app/src/main/res/xml/preferences.xml
+++ b/app/src/main/res/xml/preferences.xml
@@ -13,6 +13,38 @@
android:key="pref_drone_id"
android:title="@string/pref_drone_id" />
+
+
+
+
+
+
+
+
+ android:title="Report Email 3:">
+
+ android:title="Report Email 4:">
+
+
+
@@ -42,16 +74,15 @@
android:defaultValue="2" />
+
-
+ android:title="Use External H.264 input">
-
+ android:defaultValue="0"
+ android:key="pref_external_video"
+ android:title="Port Number" />
+
@@ -64,7 +95,7 @@
android:defaultValue="0"
android:enabled="false" />
-v
+
@@ -137,6 +168,34 @@ v
android:defaultValue="true" />
+
+
+
+
+
+
+
+
+
+
+
+