commit ccafb0a3095c56503dc7097a60ee5193bb896fee Author: gunther82 Date: Thu Mar 16 16:01:08 2023 +0100 first commit diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..d67f175 --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2018-2020 DJI + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply plugin: 'kotlin-android-extensions' + +android { + lintOptions { + abortOnError = false + } + compileSdkVersion 31 + buildToolsVersion "30.0.3" + useLibrary 'org.apache.http.legacy' + defaultConfig { + applicationId "com.dji.ux.beta.sample" + minSdkVersion 23 + targetSdkVersion 31 + multiDexEnabled true + vectorDrawables.useSupportLibrary = true + ndk { + // On x86 devices that run Android API 23 or above, if the application is targeted with API 23 or + // above, FFmpeg lib might lead to runtime crashes or warnings. + abiFilters 'armeabi-v7a', 'arm64-v8a' + } + } + buildTypes { + release { + minifyEnabled true + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + /* + dexOptions { + javaMaxHeapSize "3g" + } + */ + lintOptions{ + abortOnError false + } + packagingOptions{ + doNotStrip "*/*/libdjivideo.so" + doNotStrip "*/*/libSDKRelativeJNI.so" + doNotStrip "*/*/libFlyForbid.so" + doNotStrip "*/*/libduml_vision_bokeh.so" + doNotStrip "*/*/libyuv2.so" + doNotStrip "*/*/libGroudStation.so" + doNotStrip "*/*/libFRCorkscrew.so" + doNotStrip "*/*/libUpgradeVerify.so" + doNotStrip "*/*/libFR.so" + doNotStrip "*/*/libDJIFlySafeCore.so" + doNotStrip "*/*/libdjifs_jni.so" + doNotStrip "*/*/libsfjni.so" + doNotStrip "*/*/libDJICommonJNI.so" + doNotStrip "*/*/libDJICSDKCommon.so" + doNotStrip "*/*/libDJIUpgradeCore.so" + doNotStrip "*/*/libDJIUpgradeJNI.so" + doNotStrip "*/*/libDJIWaypointV2Core.so" + doNotStrip "*/*/libDJIMOP.so" + doNotStrip "*/*/libDJISDKLOGJNI.so" + + pickFirst 'lib/*/libstlport_shared.so' + pickFirst 'lib/*/libRoadLineRebuildAPI.so' + pickFirst 'lib/*/libGNaviUtils.so' + pickFirst 'lib/*/libGNaviMapex.so' + pickFirst 'lib/*/libGNaviData.so' + pickFirst 'lib/*/libGNaviMap.so' + pickFirst 'lib/*/libGNaviSearch.so' + exclude 'META-INF/proguard/okhttp3.pro' + exclude 'META-INF/rxjava.properties' + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } +} + +dependencies { + + implementation ('com.dji:dji-sdk:4.16.1', { + /** + * Comment the "library-anti-distortion" if your app needs Anti Distortion for Mavic 2 Pro + * com.dji:dji-sdk:4.16 + * and Mavic 2 Zoom. + * Comment the "fly-safe-database" if you don't need a smaller apk for release. When + * uncommented, we will download it when DJISDKManager.getInstance().registerApp + * is called, and it won't register success without fly-safe-database. + * Both will greatly reduce the size of the APK. + */ + exclude module: 'library-anti-distortion' + exclude module: 'fly-safe-database' + + /** + * Uncomment the following line to exclude Amap from the app. + * Note that Google Play Store does not allow APKs that include this library. + */ + // exclude group: 'com.amap.api' + }) + implementation 'androidx.test.ext:junit:1.1.3' + compileOnly ('com.dji:dji-sdk-provided:4.16.1') + + //UXSDK MOBILE HERE + implementation 'com.github.dji-sdk:Mobile-UXSDK-Beta-Android:v0.5.1' + + // UXSDK dependencies + implementation 'androidx.annotation:annotation:1.3.0' + implementation 'androidx.appcompat:appcompat:1.4.1' + implementation 'androidx.core:core:1.7.0' + implementation "androidx.core:core-ktx:1.7.0" + + // UXSDK map dependencies: Only include dependencies for map providers that are used in your app + // Amap: Do not include if publishing to Google Play Store + //implementation 'com.amap.api:3dmap:6.9.2' + //implementation 'com.amap.api:search:6.9.2' + //implementation 'com.amap.api:location:4.7.0' + // HERE maps + //implementation files('libs/HERE-sdk-3.15.0.aar') + // Mapbox + implementation 'com.mapbox.mapboxsdk:mapbox-android-sdk:9.2.0' + // Google maps + implementation 'com.google.android.gms:play-services-base:18.0.1' + implementation 'com.google.android.gms:play-services-location:19.0.1' + implementation 'com.google.android.gms:play-services-maps:18.0.2' + implementation 'com.google.android.gms:play-services-places:17.0.0' + implementation 'com.google.maps.android:android-maps-utils:1.2.1' + + // SDK dependencies + implementation 'androidx.constraintlayout:constraintlayout:2.1.3' + implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0' + implementation 'androidx.multidex:multidex:2.0.1' + implementation 'androidx.recyclerview:recyclerview:1.2.1' + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" + + // required to avoid crash on Android 12 API 31 + implementation 'androidx.work:work-runtime-ktx:2.7.1' + + // Sample app dependencies + implementation 'com.jakewharton:butterknife:10.0.0' + annotationProcessor 'com.jakewharton:butterknife-compiler:10.0.0' + + implementation "com.ncorti:slidetoact:0.9.0" + implementation "com.github.emrekose26:RecordButton:1.2.4" + implementation 'com.google.android.material:material:1.5.0' + + api 'io.reactivex.rxjava3:rxandroid:3.0.0' + api 'io.reactivex.rxjava3:rxjava:3.0.0' + + +} +repositories { + mavenLocal() +} diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/app/src/androidTest/java/com/dji/ux/beta/sample/ExampleInstrumentedTest.java b/app/src/androidTest/java/com/dji/ux/beta/sample/ExampleInstrumentedTest.java new file mode 100644 index 0000000..477764a --- /dev/null +++ b/app/src/androidTest/java/com/dji/ux/beta/sample/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package com.dji.ux.beta.sample; + +import android.content.Context; + +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Instrumented test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); + assertEquals("com.dji.ux.beta.sample", appContext.getPackageName()); + } +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..3614cf3 --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,106 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/java/com/dji/ux/beta/sample/DJIConnectionControlActivity.java b/app/src/main/java/com/dji/ux/beta/sample/DJIConnectionControlActivity.java new file mode 100644 index 0000000..38f9e2c --- /dev/null +++ b/app/src/main/java/com/dji/ux/beta/sample/DJIConnectionControlActivity.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2018-2020 DJI + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +package com.dji.ux.beta.sample; + +import android.app.Activity; +import android.content.Intent; +import android.hardware.usb.UsbManager; +import android.os.Bundle; +import android.view.View; + +/** + * Controls the connection to the USB accessory. This activity listens for the USB attached action + * and sends a broadcast with an internal code which is listened to by the + * {@link OnDJIUSBAttachedReceiver}. + */ +public class DJIConnectionControlActivity extends Activity { + + public static final String ACCESSORY_ATTACHED = "dji.ux.beta.sample.ACCESSORY_ATTACHED"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(new View(this)); + + Intent usbIntent = getIntent(); + if (usbIntent != null) { + String action = usbIntent.getAction(); + if (UsbManager.ACTION_USB_ACCESSORY_ATTACHED.equals(action)) { + Intent attachedIntent = new Intent(); + attachedIntent.setAction(ACCESSORY_ATTACHED); + sendBroadcast(attachedIntent); + } + } + + finish(); + } +} diff --git a/app/src/main/java/com/dji/ux/beta/sample/MainActivity.java b/app/src/main/java/com/dji/ux/beta/sample/MainActivity.java new file mode 100644 index 0000000..8a10d15 --- /dev/null +++ b/app/src/main/java/com/dji/ux/beta/sample/MainActivity.java @@ -0,0 +1,358 @@ +/* + * Copyright (c) 2018-2020 DJI + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +package com.dji.ux.beta.sample; + +import android.Manifest; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.SharedPreferences; +import android.content.pm.PackageManager; +import android.os.AsyncTask; +import android.os.Build; +import android.os.Bundle; +import android.util.Log; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.annotation.NonNull; +import androidx.annotation.RequiresApi; +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.app.ActivityCompat; +import androidx.core.content.ContextCompat; +import androidx.localbroadcastmanager.content.LocalBroadcastManager; + +import com.dji.ux.beta.sample.cameraview.CameraActivity; +import com.dji.ux.beta.sample.connection.TcpClientService; +import com.dji.ux.beta.sample.utils.ToastUtils; +import com.ncorti.slidetoact.SlideToActView; + +import org.jetbrains.annotations.NotNull; + +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.List; +import java.util.Locale; +import java.util.concurrent.atomic.AtomicBoolean; + +import butterknife.BindView; +import butterknife.ButterKnife; +import dji.common.error.DJIError; +import dji.common.error.DJISDKError; +import dji.sdk.base.BaseComponent; +import dji.sdk.base.BaseProduct; +import dji.sdk.products.Aircraft; +import dji.sdk.sdkmanager.DJISDKInitEvent; +import dji.sdk.sdkmanager.DJISDKManager; + +/** + * Handles the connection to the product and provides links to the different test activities. Also + * shows the current connection state and displays logs for the different steps of the SDK + * registration process. + */ +//@RequiresApi(api = Build.VERSION_CODES.S) +public class MainActivity extends AppCompatActivity { + +// SharedPreferences sharedPreferences; + + //region Constants + private static final int REQUEST_PERMISSION_CODE = 12345; + private static final String[] REQUIRED_PERMISSION_LIST = new String[]{ + Manifest.permission.VIBRATE, // Gimbal rotation + Manifest.permission.INTERNET, // API requests + Manifest.permission.ACCESS_WIFI_STATE, // WIFI connected products + Manifest.permission.ACCESS_COARSE_LOCATION, // Maps + Manifest.permission.ACCESS_NETWORK_STATE, // WIFI connected products + Manifest.permission.ACCESS_FINE_LOCATION, // Maps + Manifest.permission.CHANGE_WIFI_STATE, // Changing between WIFI and USB connection + Manifest.permission.WRITE_EXTERNAL_STORAGE, // Log files + Manifest.permission.BLUETOOTH, // Bluetooth connected products + Manifest.permission.BLUETOOTH_ADMIN, // Bluetooth connected products + Manifest.permission.READ_EXTERNAL_STORAGE, // Log files + Manifest.permission.READ_PHONE_STATE, // Device UUID accessed upon registration + Manifest.permission.RECORD_AUDIO, // Speaker accessory + //Manifest.permission.BLUETOOTH_SCAN, + //Manifest.permission.BLUETOOTH_CONNECT, + Manifest.permission.ACCESS_FINE_LOCATION + }; + private static final String TIME_FORMAT = "MMM dd, yyyy 'at' h:mm:ss a"; + private static final String TAG = "MainActivity"; + private static final String ACTION = "FROM_MAIN"; + + //TODO: eliminare broadcast receiver e prendere indirizzo ip server dallo shared preference + //TODO: inviare al server stato iniziale connessione del drone + //TODO: applicazione crasha se Bluetooth acceso (Android 12 bug) + //TODO: eliminare il toast del log + + //endregion + private static boolean isAppStarted = false; + @BindView(R.id.text_view_version) + protected TextView versionTextView; + @BindView(R.id.text_view_registered) + protected TextView registeredTextView; + @BindView(R.id.text_view_product_name) + protected TextView productNameTextView; + @BindView(R.id.text_view_server_ip) + protected TextView ipServerTextView; + @BindView(R.id.camera_button) + protected SlideToActView cameraButton; + //region Fields + private AtomicBoolean isRegistrationInProgress = new AtomicBoolean(false); + private int lastProgress = -1; + private DJISDKManager.SDKManagerCallback registrationCallback = new DJISDKManager.SDKManagerCallback() { + + @Override + public void onRegister(DJIError error) { + isRegistrationInProgress.set(false); + if (error == DJISDKError.REGISTRATION_SUCCESS) { + DJISDKManager.getInstance().startConnectionToProduct(); + runOnUiThread(() -> { + registeredTextView.setText(R.string.registered); + }); + Log.i(TAG, "Registration success"); + } else { + showToast( "Register sdk fails, check network is available"); + Log.i(TAG, "Registration failed"); + } + } + + @Override + public void onProductDisconnect() { + runOnUiThread(() -> { + //addLog("Disconnected from product"); + productNameTextView.setText(R.string.no_product); + fromActivityToService(ACTION, "drone_connection", getString(R.string.no_product)); + }); + Log.i(TAG, "Drone disconnected"); + } + + @Override + public void onProductConnect(BaseProduct product) { + if (product != null) { + runOnUiThread(() -> { + //addLog("Connected to product"); + if (product.getModel() != null) { + productNameTextView.setText(getString(R.string.product_name, product.getModel().getDisplayName())); + fromActivityToService(ACTION, "drone_connection", getString(R.string.product_name, product.getModel().getDisplayName())); + } else if (product instanceof Aircraft) { + Aircraft aircraft = (Aircraft) product; + if (aircraft.getRemoteController() != null) { + productNameTextView.setText(getString(R.string.remote_controller)); + fromActivityToService(ACTION, "drone_connection", getString((R.string.remote_controller))); + } + } + }); + Log.i(TAG, String.format("onProductConnect newProduct:%s", product)); + } + } + + @Override + public void onProductChanged(BaseProduct product) { + if (product != null) { + runOnUiThread(() -> { + //addLog("Product changed"); + if (product.getModel() != null) { + productNameTextView.setText(getString(R.string.product_name, product.getModel().getDisplayName())); + } else if (product instanceof Aircraft) { + Aircraft aircraft = (Aircraft) product; + if (aircraft.getRemoteController() != null) { + productNameTextView.setText(getString(R.string.remote_controller)); + } + } + }); + } + } + + @Override + public void onComponentChange(BaseProduct.ComponentKey key, + BaseComponent oldComponent, + BaseComponent newComponent) { + Log.i(TAG, key.toString() + " changed"); + } + + @Override + public void onInitProcess(DJISDKInitEvent djisdkInitEvent, int totalProcess) { + Log.i(TAG, djisdkInitEvent.getInitializationState().toString()); + } + + @Override + public void onDatabaseDownloadProgress(long current, long total) { + runOnUiThread(() -> { + int progress = (int) (100 * current / total); + if (progress == lastProgress) { + return; + } + lastProgress = progress; + //addLog("Fly safe database download progress: " + progress); + }); + } + }; + private List missingPermission = new ArrayList<>(); + //endregion + + /** + * Whether the app has started. + * + * @return `true` if the app has been started. + */ + public static boolean isStarted() { + return isAppStarted; + } + + //region Lifecycle + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + ButterKnife.bind(this); + isAppStarted = true; + checkAndRequestPermissions(); + versionTextView.setText(getResources().getString(R.string.sdk_version, + DJISDKManager.getInstance().getSDKVersion())); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + startForegroundService(new Intent(this, TcpClientService.class)); + } else { + startService(new Intent(this, TcpClientService.class)); + } + + cameraButton.setOnSlideCompleteListener(new SlideToActView.OnSlideCompleteListener() { + @Override + public void onSlideComplete(@NotNull SlideToActView slideToActView) { + Intent intent = new Intent(MainActivity.this, CameraActivity.class); + startActivity(intent); + cameraButton.resetSlider(); + } + }); + } + //endregion + + private final BroadcastReceiver broadcastReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + /* + if("FROM_SERVER".equals(intent.getAction())){ + String message = intent.getStringExtra("speed_wp"); + addLog(message); + }*/ + if ("IP".equals(intent.getAction())){ + String ip = intent.getStringExtra("server_ip"); + ipServerTextView.setText("Status: " + ip); + } + } + }; + + private void fromActivityToService(String action, String key, String msg){ + Intent intent = new Intent(action); + intent.putExtra(key, msg); + LocalBroadcastManager.getInstance(MainActivity.this).sendBroadcast(intent); + } + + @Override + protected void onResume() { + super.onResume(); + IntentFilter filter = new IntentFilter("IP"); + filter.addAction("FROM_SERVER"); + LocalBroadcastManager.getInstance(MainActivity.this).registerReceiver(broadcastReceiver, filter); + } + + @Override + protected void onDestroy() { + // Prevent memory leak by releasing DJISDKManager's references to this activity + if (DJISDKManager.getInstance() != null) { + DJISDKManager.getInstance().destroy(); + } + isAppStarted = false; + super.onDestroy(); + stopService(new Intent(this, TcpClientService.class)); + LocalBroadcastManager.getInstance(MainActivity.this).unregisterReceiver(broadcastReceiver); + } + + /** + * Checks if there is any missing permissions, and + * requests runtime permission if needed. + */ + private void checkAndRequestPermissions() { + // Check for permissions + for (String eachPermission : REQUIRED_PERMISSION_LIST) { + if (ContextCompat.checkSelfPermission(this, eachPermission) != PackageManager.PERMISSION_GRANTED) { + missingPermission.add(eachPermission); + } + } + // Request for missing permissions + if (missingPermission.isEmpty()) { + startSDKRegistration(); + } else { + ActivityCompat.requestPermissions(this, + missingPermission.toArray(new String[missingPermission.size()]), + REQUEST_PERMISSION_CODE); + } + } + + /** + * Result of runtime permission request + */ + @Override + public void onRequestPermissionsResult(int requestCode, + @NonNull String[] permissions, + @NonNull int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + // Check for granted permission and remove from missing list + if (requestCode == REQUEST_PERMISSION_CODE) { + for (int i = grantResults.length - 1; i >= 0; i--) { + if (grantResults[i] == PackageManager.PERMISSION_GRANTED) { + missingPermission.remove(permissions[i]); + } + } + } + // If there is enough permission, we will start the registration + if (missingPermission.isEmpty()) { + startSDKRegistration(); + } else { + ToastUtils.setResultToToast("Missing permissions! Will not register SDK to connect to aircraft."); + } + } + + /** + * Start the SDK registration + */ + private void startSDKRegistration() { + if (isRegistrationInProgress.compareAndSet(false, true)) { + Log.i(TAG, "registering product"); + //addLog("Registering product"); + AsyncTask.execute(() -> DJISDKManager.getInstance().registerApp(getApplicationContext(), registrationCallback)); + } + } + + private void showToast(final String toastMsg) { + runOnUiThread(new Runnable() { + @Override + public void run() { + Toast.makeText(getApplicationContext(), toastMsg, Toast.LENGTH_LONG).show(); + } + }); + } +} diff --git a/app/src/main/java/com/dji/ux/beta/sample/OnDJIUSBAttachedReceiver.java b/app/src/main/java/com/dji/ux/beta/sample/OnDJIUSBAttachedReceiver.java new file mode 100644 index 0000000..42c68cf --- /dev/null +++ b/app/src/main/java/com/dji/ux/beta/sample/OnDJIUSBAttachedReceiver.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2018-2020 DJI + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +package com.dji.ux.beta.sample; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; + +import dji.sdk.sdkmanager.DJISDKManager; + +/** + * This receiver will detect the USB attached event. + * It will check if the app has been previously started. + * If the app is already running, it will prevent a new activity from being started and bring the + * existing activity to the top of the stack. + */ + +public class OnDJIUSBAttachedReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + if (!MainActivity.isStarted()) { + Intent startIntent = context.getPackageManager() + .getLaunchIntentForPackage(context.getPackageName()); + startIntent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT + | Intent.FLAG_ACTIVITY_NEW_TASK + | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); + startIntent.addCategory(Intent.CATEGORY_LAUNCHER); + context.startActivity(startIntent); + } else { + Intent attachedIntent = new Intent(); + attachedIntent.setAction(DJISDKManager.USB_ACCESSORY_ATTACHED); + context.sendBroadcast(attachedIntent); + } + } +} diff --git a/app/src/main/java/com/dji/ux/beta/sample/SampleApplication.java b/app/src/main/java/com/dji/ux/beta/sample/SampleApplication.java new file mode 100644 index 0000000..f242d4f --- /dev/null +++ b/app/src/main/java/com/dji/ux/beta/sample/SampleApplication.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2018-2020 DJI + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +package com.dji.ux.beta.sample; + +import android.app.Application; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.IntentFilter; +import android.content.SharedPreferences; +import android.util.Log; + +import androidx.multidex.MultiDex; + +import com.dji.frame.util.V_JsonUtil; +import com.secneo.sdk.Helper; + +import dji.sdk.base.BaseProduct; +import dji.sdk.sdkmanager.DJISDKManager; +import dji.ux.beta.core.communication.DefaultGlobalPreferences; +import dji.ux.beta.core.communication.GlobalPreferencesManager; + +import static com.dji.ux.beta.sample.DJIConnectionControlActivity.ACCESSORY_ATTACHED; + +/** + * An application that loads the SDK classes. + */ +public class SampleApplication extends Application { + + private static Application app = null; + private static BaseProduct product; + private static final String TAG = SampleApplication.class.getName(); + + public static synchronized BaseProduct getProductInstance(){ + product = DJISDKManager.getInstance().getProduct(); + return product; + } + + @Override + public void onCreate() { + super.onCreate(); + //For the global preferences to take effect, this must be done before the widgets are initialized + //If this is not done, no global preferences will take effect or persist across app restarts + //TODO: prima era commentato e non c'era njson + GlobalPreferencesManager.initialize(new DefaultGlobalPreferences(this)); + + BroadcastReceiver br = new OnDJIUSBAttachedReceiver(); + IntentFilter filter = new IntentFilter(); + filter.addAction(ACCESSORY_ATTACHED); + registerReceiver(br, filter); + V_JsonUtil.DjiLog(); + + SharedPreferences sharedPreferences = getSharedPreferences(getString(R.string.my_pref), Context.MODE_PRIVATE); + SharedPreferences.Editor editor = sharedPreferences.edit(); + editor.putBoolean(getString(R.string.disableConsole), true); + editor.apply(); + } + + @Override + protected void attachBaseContext(Context paramContext) { + super.attachBaseContext(paramContext); + Helper.install(SampleApplication.this); + MultiDex.install(this); + app = this; + } + + public static Application getInstance() { + return SampleApplication.app; + } + +} diff --git a/app/src/main/java/com/dji/ux/beta/sample/cameraview/CameraActivity.java b/app/src/main/java/com/dji/ux/beta/sample/cameraview/CameraActivity.java new file mode 100644 index 0000000..ce3fb43 --- /dev/null +++ b/app/src/main/java/com/dji/ux/beta/sample/cameraview/CameraActivity.java @@ -0,0 +1,1064 @@ +package com.dji.ux.beta.sample.cameraview; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.SharedPreferences; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.os.PersistableBundle; +import android.util.DisplayMetrics; +import android.util.Log; +import android.view.View; +import android.view.WindowManager; +import android.view.animation.Animation; +import android.view.animation.Transformation; +import android.widget.Toast; + +import androidx.annotation.NonNull; +import androidx.appcompat.app.AlertDialog; +import androidx.appcompat.app.AppCompatActivity; +import androidx.constraintlayout.widget.ConstraintLayout; +import androidx.core.content.ContextCompat; +import androidx.localbroadcastmanager.content.LocalBroadcastManager; + +import com.dji.mapkit.core.maps.DJIMap; +import com.dji.mapkit.core.models.DJIBitmapDescriptor; +import com.dji.mapkit.core.models.DJIBitmapDescriptorFactory; +import com.dji.mapkit.core.models.DJILatLng; +import com.dji.mapkit.core.models.annotations.DJICircle; +import com.dji.mapkit.core.models.annotations.DJICircleOptions; +import com.dji.mapkit.core.models.annotations.DJIMarker; +import com.dji.mapkit.core.models.annotations.DJIMarkerOptions; +import com.dji.ux.beta.sample.R; +import com.dji.ux.beta.sample.SampleApplication; +import com.dji.ux.beta.sample.mission.FollowMission; +import com.dji.ux.beta.sample.mission.GPSPlancia; +import com.dji.ux.beta.sample.mission.PointMission; +import com.dji.ux.beta.sample.mission.PosMission; +import com.dji.ux.beta.sample.utils.DroneState; +import com.google.android.gms.maps.GoogleMap; +import com.google.android.gms.maps.model.LatLng; +import com.google.android.material.floatingactionbutton.FloatingActionButton; +import com.google.maps.android.SphericalUtil; + +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; + +import butterknife.BindView; +import butterknife.ButterKnife; +import butterknife.OnClick; +import dji.common.airlink.PhysicalSource; +import dji.common.flightcontroller.FlightControllerState; +import dji.common.flightcontroller.flyzone.FlyZoneCategory; +import dji.common.mission.followme.FollowMeMissionState; +import dji.common.mission.hotpoint.HotpointMissionState; +import dji.common.mission.waypoint.WaypointMissionState; +import dji.sdk.base.BaseProduct; +import dji.sdk.flightcontroller.FlightController; +import dji.sdk.products.Aircraft; +import dji.sdk.sdkmanager.DJISDKManager; +import dji.sdk.sdkmanager.LiveStreamManager; +import dji.sdk.sdkmanager.LiveVideoBitRateMode; +import dji.sdk.sdkmanager.LiveVideoResolution; +import dji.thirdparty.io.reactivex.android.schedulers.AndroidSchedulers; +import dji.thirdparty.io.reactivex.disposables.CompositeDisposable; +import dji.ux.beta.cameracore.widget.fpvinteraction.FPVInteractionWidget; +import dji.ux.beta.core.extension.ViewExtensions; +import dji.ux.beta.core.panel.systemstatus.SystemStatusListPanelWidget; +import dji.ux.beta.core.panel.topbar.TopBarPanelWidget; +import dji.ux.beta.core.util.SettingDefinitions; +import dji.ux.beta.core.widget.fpv.FPVWidget; +import dji.ux.beta.core.widget.systemstatus.SystemStatusWidget; +import dji.ux.beta.core.widget.useraccount.UserAccountLoginWidget; +import dji.ux.beta.map.widget.map.MapWidget; + +public class CameraActivity extends AppCompatActivity { + + SharedPreferences sharedPreferences; + + private final int WAYPOINT_MISSION = 1; + private final int HOTPOINT_MISSION = 2; + private final int FOLLOW_MISSION = 3; + private final int NO_MISSION = 0; + +// private static final String TAG = CameraActivity.class.getSimpleName(); + private static final String TAG = "CameraActivity"; + + private final String liveShowUrl = "rtmp://192.168.1.100/live/drone"; //stream TP-link +// private final String liveShowUrl = "rtmp://192.168.2.8/live/drone"; //RUBICON +// private final String liveShowUrl = "rtmp://192.168.200.22/live/drone"; //stream livorno +// private final String liveShowUrl = "rtmp://146.48.39.44/live/drone"; //stream remoto + + private final String ACTION = "FROM CAMERA"; + + @BindView(R.id.widget_fpv) + protected FPVWidget fpvWidget; + @BindView(R.id.widget_fpv_interaction) + protected FPVInteractionWidget fpvInteractionWidget; + @BindView(R.id.widget_map) + protected MapWidget mapWidget; + @BindView(R.id.widget_secondary_fpv) + protected FPVWidget secondaryFPVWidget; + @BindView(R.id.root_view) + protected ConstraintLayout parentView; + @BindView(R.id.widget_panel_system_status_list) + protected SystemStatusListPanelWidget systemStatusListPanelWidget; + //@BindView(R.id.live_button) + private FloatingActionButton streamButton; + private FloatingActionButton stopSearch; + private FloatingActionButton wayPointSearch; + private FloatingActionButton disableConsoleButton; + + private boolean isMapMini = true; + private int widgetHeight; + private int widgetWidth; + private int widgetMargin; + private int deviceWidth; + private int deviceHeight; + private CompositeDisposable compositeDisposable; + private UserAccountLoginWidget userAccountLoginWidget; + private int dialogCount = 0; + private final float INTERDICTION_RADIUS = 20.0f; + private DJICircle mCircle; + private LatLng centerRadius; + private List centerRadiusList; + private LatLng currentPersonPos; + + private LiveStreamManager.OnLiveChangeListener listener; + private final LiveStreamManager.LiveStreamVideoSource currentVideoSource = LiveStreamManager.LiveStreamVideoSource.Primary; + + private float waypointSpeed = 5.0f; //range [2,15] m/s. + + private final PointMission mPointMission = new PointMission(this); + private final FollowMission mFollowMission = new FollowMission(this); + private final PosMission mPosMission = new PosMission(this); + + private List listMarker = new ArrayList<>(); + + private boolean personNotificationEnabled = false; + + //TODO: settare button a non clickable quando drone non connesso + //TODO: sistemare le icone nella top bar (troppo piccole) + //TODO: setExitMissionOnRCSignalLostEnabled(boolean enabled) + //TODO: far scegliere angolo gimbalpitch + + + private void setResultToToast(final String string){ + CameraActivity.this.runOnUiThread(new Runnable() { + @Override + public void run() { + Toast.makeText(CameraActivity.this, string, Toast.LENGTH_SHORT).show(); + } + }); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + setContentView(R.layout.activity_camera); + sharedPreferences = getSharedPreferences(getString(R.string.my_pref), Context.MODE_PRIVATE); + SharedPreferences.Editor editor = sharedPreferences.edit(); + editor.putBoolean(getString(R.string.disableConsole), true); + editor.apply(); + centerRadiusList = new ArrayList(); + + double interdictionRadius = sharedPreferences.getFloat(getString(R.string.interdiction_area), INTERDICTION_RADIUS); +// Toast.makeText(getApplicationContext(), "Interdiction radius: " + interdictionRadius, Toast.LENGTH_SHORT).show(); + + widgetHeight = (int) getResources().getDimension(R.dimen.mini_map_height); + widgetWidth = (int) getResources().getDimension(R.dimen.mini_map_width); + widgetMargin = (int) getResources().getDimension(R.dimen.mini_map_margin); + + if (isLiveStreamManagerOn()){ + Log.d(TAG, "live streaming manager on"); + DJISDKManager.getInstance().getLiveStreamManager().registerListener(listener); + } + + //add listener for waypoint + mPointMission.addListenerWaypoint(); + mFollowMission.addListenerFollow(); + mPosMission.addListenerHotpoint(); + //Log.i(TAG, "on create wp state " + mPointMission.getWaypointMissionState()); + + disableConsoleButton = findViewById(R.id.disableConsoleBtn); + disableConsoleButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + boolean disableConsole = sharedPreferences.getBoolean(getString(R.string.disableConsole), true); + Log.i(TAG, "disableConsole " + !disableConsole); + if(!disableConsole) { + stopAllMissions(); + editor.putBoolean(getString(R.string.disableConsole), true); + disableConsoleButton.setImageDrawable(ContextCompat.getDrawable(getBaseContext(), R.drawable.uxsdk_ic_flyzone_locked)); + } + else { + editor.putBoolean(getString(R.string.disableConsole), false); + disableConsoleButton.setImageDrawable(ContextCompat.getDrawable(getBaseContext(), R.drawable.uxsdk_ic_flyzone_unlocked)); + } + editor.apply(); + } + }); + + streamButton = findViewById(R.id.streamBtn); + streamButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if(!DJISDKManager.getInstance().getLiveStreamManager().isStreaming()){ + initSettings(); + startLiveStream(); +// streamButton.setImageDrawable(ContextCompat.getDrawable(getBaseContext(), R.drawable.baseline_cast_connected_black_48)); + } else { + stopLiveShow(); +// streamButton.setImageDrawable(ContextCompat.getDrawable(getBaseContext(), R.drawable.baseline_cast_black_48)); + } + } + }); + + stopSearch = findViewById(R.id.stopSearch); + //stopSearch.setVisibility(View.GONE); + stopSearch.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + nextTarget(); + } + }); + + wayPointSearch = findViewById(R.id.searchBtn); + wayPointSearch.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { +// if(mPointMission.getWaypointMissionState().equals(WaypointMissionState.EXECUTING.toString()) || mPointMission.getWaypointMissionState().equals(WaypointMissionState.EXECUTION_PAUSED.toString())){ +// mPointMission.stopWaypointMission(); +// Toast.makeText(getApplicationContext(), "Waypoint search stopped", Toast.LENGTH_SHORT).show(); +// wayPointSearch.setImageDrawable(ContextCompat.getDrawable(getBaseContext(), R.drawable.ic_start_search)); +// } else { +// deletePositions(); +// fromActivityToService("mob_mission"); +// wayPointSearch.setImageDrawable(ContextCompat.getDrawable(getBaseContext(), R.drawable.ic_stop_search)); +// } + } + }); + + initListener(); + initSettings(); + + DisplayMetrics displayMetrics = getResources().getDisplayMetrics(); + deviceHeight = displayMetrics.heightPixels; + deviceWidth = displayMetrics.widthPixels; + + ButterKnife.bind(this); + mapWidget.initGoogleMap(map -> { + map.setMapType(GoogleMap.MAP_TYPE_HYBRID); + //define home location as center radius + LatLng centerRadius = DroneState.getHomeLocation(); + centerRadiusList.add(centerRadius); + Log.i(TAG, "homeLocation " + centerRadius.toString()); + DJILatLng latLng = new DJILatLng(centerRadius.latitude, centerRadius.longitude); + DJICircleOptions circleOptions = new DJICircleOptions().center(latLng) + .radius(interdictionRadius).fillColor(getResources().getColor(R.color.background_blue)) + .strokeColor(getResources().getColor(R.color.background_blue)).strokeWidth(8); + mCircle = mapWidget.getMap().addSingleCircle(circleOptions); + map.setOnMapClickListener(new DJIMap.OnMapClickListener() { + @Override + public void onMapClick(DJILatLng djiLatLng) { + onViewClick(mapWidget); + //stopLiveShow(); + //startLiveStream(); + } + }); + }); + mapWidget.getFlyZoneHelper().setFlyZoneVisible(FlyZoneCategory.AUTHORIZATION, true); + mapWidget.getFlyZoneHelper().setFlyZoneVisible(FlyZoneCategory.WARNING, true); + mapWidget.getFlyZoneHelper().setFlyZoneVisible(FlyZoneCategory.ENHANCED_WARNING, true); + mapWidget.getFlyZoneHelper().setFlyZoneVisible(FlyZoneCategory.RESTRICTED, true); + mapWidget.getUserAccountLoginWidget().setVisibility(View.GONE); + mapWidget.onCreate(savedInstanceState); + + // Setup top bar state callbacks + TopBarPanelWidget topBarPanel = findViewById(R.id.panel_top_bar); + SystemStatusWidget systemStatusWidget = topBarPanel.getSystemStatusWidget(); + if (systemStatusWidget != null){ + systemStatusWidget.setStateChangeCallback(findViewById(R.id.widget_panel_system_status_list)); + } + + if(!DJISDKManager.getInstance().getLiveStreamManager().isStreaming()){ + startLiveStream(); + } + } + + + // -------------- STREAMING REGION ---------------------- + + private void initSettings(){ + if (!isLiveStreamManagerOn()) { + return; + } + //DJISDKManager.getInstance().allowStreamWhenAppInBackground(true); + DJISDKManager.getInstance().getLiveStreamManager().setAudioMuted(true); + DJISDKManager.getInstance().getLiveStreamManager().setLiveVideoBitRateMode(LiveVideoBitRateMode.AUTO); + DJISDKManager.getInstance().getLiveStreamManager().setLiveVideoResolution(LiveVideoResolution.VIDEO_RESOLUTION_1280_720); //per il momento quella con latenza/qualità migliore + DJISDKManager.getInstance().getLiveStreamManager().setLiveUrl(liveShowUrl); + DJISDKManager.getInstance().getLiveStreamManager().setVideoEncodingEnabled(false); + DJISDKManager.getInstance().getLiveStreamManager().setAudioStreamingEnabled(false); + } + + private void initListener() { + listener = new LiveStreamManager.OnLiveChangeListener() { + @Override + public void onStatusChanged(int i) { + setResultToToast("status changed : " + i); + } + }; + } + + private boolean isLiveStreamManagerOn() { + if(DJISDKManager.getInstance().getLiveStreamManager()==null){ + setResultToToast("No live stream manager!"); + return false; + } + return true; + } + + void startLiveStream(){ + Toast.makeText(getApplicationContext(), "start live show: " + isLiveStreamManagerOn(), Toast.LENGTH_SHORT).show(); + + if (!isLiveStreamManagerOn()) { + return; + } + if(DJISDKManager.getInstance().getLiveStreamManager().isStreaming()){ + Toast.makeText(getApplicationContext(), "already started", Toast.LENGTH_SHORT).show(); + return; + } + + new Thread() { + @Override + public void run() { + final int result = DJISDKManager.getInstance().getLiveStreamManager().startStream(); + DJISDKManager.getInstance().getLiveStreamManager().setStartTime(); + runOnUiThread(new Runnable() { + public void run() { + Toast.makeText(getApplication(), "RESULT: " + result, Toast.LENGTH_SHORT).show(); + if(result == 0){ + fromActivityToService("isStreaming"); + streamButton.setImageDrawable(ContextCompat.getDrawable(getBaseContext(), R.drawable.baseline_cast_connected_black_48)); + } + } + }); + } + }.start(); + } + + private void stopLiveShow() { + if (!isLiveStreamManagerOn()) { + return; + } + DJISDKManager.getInstance().getLiveStreamManager().stopStream(); + setResultToToast("Stop Live Show"); + streamButton.setImageDrawable(ContextCompat.getDrawable(getBaseContext(), R.drawable.baseline_cast_black_48)); + } + // ------- END STREAMING REGION --------------- + + + // ------- MISSIONS REGION --------------- + + private void drawWayPoint(List listWP){ + int vector = R.drawable.mapbox_marker_icon_default; + if (mapWidget.getMap() != null){ + for (GPSPlancia gps : listWP) { + DJILatLng latLng = new DJILatLng(gps.getLatitude(), gps.getLongitude()); + DJIMarker markerName = mapWidget.getMap().addMarker(new DJIMarkerOptions() + .position(latLng) + .icon(bitmapDescriptorFromVector(this, vector))); + + if(!listMarker.contains(markerName)) + listMarker.add(markerName); + } + } + } + + private void drawHotpoint(){ + int strokeColor = 0xffff0000; //red outline + int shadeColor = 0x44ff0000; //opaque red fill + double latitude = Double.parseDouble(sharedPreferences.getString(getString(R.string.latitude_pos), "0.0f")); + double longitude = Double.parseDouble(sharedPreferences.getString(getString(R.string.longitude_pos), "0.0f")); + double interdictionRadius = sharedPreferences.getFloat(getString(R.string.interdiction_area), INTERDICTION_RADIUS); + int vector = R.drawable.ic_noun_drrowning; + if(mapWidget.getMap()!=null){ + DJILatLng latLng = new DJILatLng(latitude, longitude); + DJICircleOptions circleOptions = new DJICircleOptions().center(latLng) + .radius(interdictionRadius).fillColor(shadeColor).strokeColor(strokeColor).strokeWidth(8); + mapWidget.getMap().addMarker(new DJIMarkerOptions() + .position(latLng) + .icon(bitmapDescriptorFromVector(this, vector))); + mCircle = mapWidget.getMap().addSingleCircle(circleOptions); + } + } + + private void deleteHotpoint() { + mCircle.remove(); + } + + private void handleWPMissionButton(boolean personNotification){ + if(mPointMission.getWaypointMissionState().equals(WaypointMissionState.EXECUTING.toString())){ + Toast.makeText(this, "WaypointMission already running!", Toast.LENGTH_SHORT).show(); + } + else { + double interdictionRadius = sharedPreferences.getFloat(getString(R.string.interdiction_area), INTERDICTION_RADIUS); + List cleanedList = GPSPlancia.getCleanedGPSList(interdictionRadius); + + if(cleanedList.size() == 0) { + Toast.makeText(this, "Waypoint list is empty, please use UploadAndStartWPMission()!", Toast.LENGTH_SHORT).show(); + return; + } + else if(cleanedList.size() < 2) { + Toast.makeText(this, "Not enough waypoints in the list, please add at least 2 waypoints in order to start a WaypointMission!", Toast.LENGTH_SHORT).show(); + return; + } + + drawWayPoint(cleanedList); + mPointMission.createWaypointFromList(cleanedList); + mPointMission.configWaypointMission(waypointSpeed); + mPointMission.uploadAndStartWayPointMission(); + personNotificationEnabled = personNotification; +// while(!mPointMission.getWaypointMissionState().equals(WaypointMissionState.READY_TO_EXECUTE.toString())) {} +// mPointMission.startWaypointMission(); + } + } + + private void handleSearchPerson() { + if(sharedPreferences.getString(getString(R.string.latitude_pos), "") == null || sharedPreferences.getString(getString(R.string.longitude_pos), "") == null || dialogCount != 0) { + return; + } + double latNewPos = Double.parseDouble(sharedPreferences.getString(getString(R.string.latitude_pos), "")); + double longNewPos = Double.parseDouble(sharedPreferences.getString(getString(R.string.longitude_pos), "")); + currentPersonPos = new LatLng(latNewPos, longNewPos); + + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setMessage("A person has been detected in open sea."); + builder.setCancelable(false); + builder.setPositiveButton("Confirm", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + Log.i(TAG, "Starting HotpointMission..."); + dialog.dismiss(); + dialogCount = 0; + + drawHotpoint(); + Log.i(TAG, mPosMission.getHPState()); + mPosMission.startHotPoint(sharedPreferences); + //TODO capire a che serve questo sotto + if(mPosMission.getHPState().equals(HotpointMissionState.EXECUTING.toString())){ + stopSearch.setVisibility(View.VISIBLE); + } + } + }); + builder.setNegativeButton("Skip", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + Log.i(TAG, "Skipping detected person and resuming WaypointMission"); + dialog.dismiss(); + dialogCount = 0; + + //reload and restart WaypointMission + String wpState = mPointMission.getWaypointMissionState(); + if(wpState.equals(WaypointMissionState.READY_TO_UPLOAD.toString())) { + fromActivityToService("Re-starting interrupted WP Mission, current state: " + wpState); + handleWPMissionButton(true); + Log.i(TAG, "Restarted WaypointMission"); + } + else { + Log.i(TAG, "ERROR: Couldn't restart WaypointMission because its state is : " + wpState); + setResultToToast("ERROR: Couldn't restart WaypointMission because its state is : " + wpState); + } + } + }); + + builder.setIcon(R.drawable.ic_noun_drrowning); + builder.setTitle("Person Detected!"); + AlertDialog alert = builder.create(); + +// if(ckeckPersonInRadius(centerRadius, currentPersonPos)){ + if(ckeckPersonInRadiusList(currentPersonPos)){ + if (dialogCount == 0) { + alert.show(); + dialogCount = 1; + } + + //create interdiction zone and add it to interdiction list, continue wpmission +// centerRadius = currentPersonPos; + centerRadiusList.add(currentPersonPos); + + //stop WaypointMission and delete visited waypoints + if(mPointMission.getWaypointMissionState().equals(WaypointMissionState.EXECUTING.toString())){ + mPointMission.stopWaypointMission(); + deletePositionsCount(); + } + } + else { + Toast.makeText(this, "Found person in no-go zone", Toast.LENGTH_SHORT).show(); + } + } + + private boolean ckeckPersonInRadius(LatLng oldPos, LatLng newPos){ + double distanceMeters = SphericalUtil.computeDistanceBetween(oldPos, newPos); + double interdictionRadius = sharedPreferences.getFloat(getString(R.string.interdiction_area), INTERDICTION_RADIUS); + + if(distanceMeters < interdictionRadius){ + return false; + } else { + mCircle.remove(); + } + return true; + } + + private boolean ckeckPersonInRadiusList(LatLng newPos){ + double distanceMeters; + double interdictionRadius = sharedPreferences.getFloat(getString(R.string.interdiction_area), INTERDICTION_RADIUS); + + for (LatLng pos : centerRadiusList) { + distanceMeters = SphericalUtil.computeDistanceBetween(pos, newPos); + if(distanceMeters < interdictionRadius) { + mCircle.remove(); + return false; + } + } + return true; + } + + public void setPersonNotification(boolean status) { + this.personNotificationEnabled = status; + } + + //remove all waypoints from waypointMissionBuilder (and set it tu null), from GPSPlancia and all markers on GUI + private void deletePositions(){ + mPointMission.deleteWaypoint(); //remove visited waypoints from waypointMissionBuilder and set it tu null + deleteMarkers(); + centerRadiusList.clear(); + } + + //remove all waypoints from waypointMissionBuilder and only visited waypoints from GPSPlancia and visited markers on GUI + private void deletePositionsCount(){ + mPointMission.deleteWPCount(); //remove all waypoints from waypointMissionBuilder and visited waypoints from GPSPlancia + deleteMarkers(); + personNotificationEnabled = false; + } + + public void deleteMarkers() { + runOnUiThread(new Runnable() { + @Override + public void run() { + if(mapWidget.getMap()!=null && listMarker!= null) { + for(DJIMarker marker : listMarker){ //remove all markers on GUI + marker.remove(); + } + } + } + }); + } + + private void stopAllMissions() { + Log.i(TAG, "Stopping all missions... "); + String wpState = mPointMission.getWaypointMissionState(); + String hpState = mPosMission.getHPState(); + String followState = mFollowMission.getFollowState(); + + Log.i(TAG, "Current states are: \nWPMission: " + wpState + "\nHPMission: " + hpState + "\nFollowMission: " + followState); + + if(wpState.equals(WaypointMissionState.EXECUTING.toString()) || wpState.equals(WaypointMissionState.EXECUTION_PAUSED.toString())) { + mPointMission.stopWaypointMission(); + deletePositionsCount(); + } + + if(hpState.equals(HotpointMissionState.INITIAL_PHASE.toString()) || hpState.equals(HotpointMissionState.EXECUTING.toString()) || hpState.equals(HotpointMissionState.EXECUTION_PAUSED.toString())) { + mPosMission.stopHotPoint(); + deleteHotpoint(); + } + + if(followState.equals(FollowMeMissionState.EXECUTING.toString())) { + mFollowMission.stopFollowShip(); + } + } + + private int getPausedMission() { + String wpState = mPointMission.getWaypointMissionState(); + String hpState = mPosMission.getHPState(); + String followState = mFollowMission.getFollowState(); + if(wpState.equals(WaypointMissionState.EXECUTION_PAUSED.toString())) { + return WAYPOINT_MISSION; + } + if(hpState.equals(HotpointMissionState.EXECUTION_PAUSED.toString())) { + return HOTPOINT_MISSION; + } + if(followState.equals(FollowMeMissionState.READY_TO_EXECUTE.toString())) { + return FOLLOW_MISSION; + } + return NO_MISSION; + } + + private int getExecutingMission() { + String wpState = mPointMission.getWaypointMissionState(); + String hpState = mPosMission.getHPState(); + String followState = mFollowMission.getFollowState(); + if(wpState.equals(WaypointMissionState.EXECUTING.toString())) { + return WAYPOINT_MISSION; + } + if(hpState.equals(HotpointMissionState.INITIAL_PHASE.toString()) || hpState.equals(HotpointMissionState.EXECUTING.toString())) { + return HOTPOINT_MISSION; + } + if(followState.equals(FollowMeMissionState.EXECUTING.toString())) { + return FOLLOW_MISSION; + } + return NO_MISSION; + } + + private int getExecutingOrPausedMission() { + String wpState = mPointMission.getWaypointMissionState(); + String hpState = mPosMission.getHPState(); + String followState = mFollowMission.getFollowState(); + if(wpState.equals(WaypointMissionState.EXECUTING.toString()) || wpState.equals(WaypointMissionState.EXECUTION_PAUSED.toString())) { + return WAYPOINT_MISSION; + } + if(hpState.equals(HotpointMissionState.INITIAL_PHASE.toString()) || hpState.equals(HotpointMissionState.EXECUTING.toString()) || hpState.equals(HotpointMissionState.EXECUTION_PAUSED.toString())) { + return HOTPOINT_MISSION; + } + if(followState.equals(FollowMeMissionState.EXECUTING.toString())) { + return FOLLOW_MISSION; + } + return NO_MISSION; + } + + private void nextTarget() { + if(mPosMission.getHPState().equals(HotpointMissionState.INITIAL_PHASE.toString()) || mPosMission.getHPState().equals(HotpointMissionState.EXECUTING.toString()) || mPosMission.getHPState().equals(HotpointMissionState.EXECUTION_PAUSED.toString())){ + Log.i(TAG, "Stopping HotpointMission and resuming WaypointMission..."); + mPosMission.stopHotPoint(); + deleteHotpoint(); + handleWPMissionButton(true); + } + } + + private String droneLat; + private String droneLon; + private String droneAlt; + + private void getDroneLocation() { + BaseProduct product = SampleApplication.getProductInstance(); + FlightController flightController = null; + if (product != null && product.isConnected()) { + + if (product instanceof Aircraft) { flightController = ((Aircraft) product).getFlightController(); } + + if (flightController !=null){ + flightController.setStateCallback(new FlightControllerState.Callback() { + @Override + public void onUpdate(@NonNull @NotNull FlightControllerState flightControllerState) { + + if (flightControllerState.getAircraftLocation() != null) { + if(!Double.isNaN(flightControllerState.getAircraftLocation().getLatitude()) && !Double.isNaN(flightControllerState.getAircraftLocation().getLongitude())) { + droneLat = String.valueOf(flightControllerState.getAircraftLocation().getLatitude()); + droneLon = String.valueOf(flightControllerState.getAircraftLocation().getLongitude()); + droneAlt = String.valueOf(flightControllerState.getAircraftLocation().getAltitude()); + } else { + droneLat = "0.0"; + droneLon = "0.0"; + droneAlt = "0.0"; + } + } + } + }); + } + } + } + + private void goToShip() { + String latitude = sharedPreferences.getString(getString(R.string.latitude_ship), ""); + String longitude = sharedPreferences.getString(getString(R.string.longitude_ship), ""); + String altitude = sharedPreferences.getString(getString(R.string.altitude_ship), "20.0f"); + + int mission = getExecutingOrPausedMission(); + switch (mission) { + case WAYPOINT_MISSION: + Log.i(TAG, "Can't go to specified ship position because WaypointMission is running."); + setResultToToast("Can't go to specified ship position because WaypointMission is running."); + break; + case HOTPOINT_MISSION: + Log.i(TAG, "Can't go to specified ship position because HotpointMission is running."); + setResultToToast("Can't go to specified ship position because HotpointMission is running."); + break; + case FOLLOW_MISSION: + Log.i(TAG, "Can't go to specified ship position because FollowMission is running."); + setResultToToast("Can't go to specified ship position because FollowMission is running."); + break; + case NO_MISSION: + Log.i(TAG, "Going to ship at the given coordinates: " + latitude + ", " + longitude + ", altitude: " + altitude); + setResultToToast("Going to ship's location..."); + + while(droneAlt == null || droneLat == null || droneLon == null) { + getDroneLocation(); + } + Log.i(TAG, "droneLat: " + droneLat + ", droneLon: " + droneLon + ", droneAlt: " + droneAlt); + GPSPlancia.populateGPSPlancia(droneLat, droneLon, droneAlt); + GPSPlancia.populateGPSPlancia(latitude, longitude, altitude); + handleWPMissionButton(false); + break; + } + } + + private void handleMission(String action){ + int mission; + switch (action) { + case "speed_wp": + waypointSpeed = sharedPreferences.getFloat(getString(R.string.speed_waypoint), 0.0f); +// Log.i(TAG, "speed value in camera act: " + waypointSpeed); + Toast.makeText(this, "WP speed: " + waypointSpeed, Toast.LENGTH_SHORT).show(); + mPointMission.setWaypointMissionSpeed(waypointSpeed); + break; + case "start_waypoint_list": + fromActivityToService("Uploading and starting WP Mission, current state: " + mPointMission.getWaypointMissionState()); + handleWPMissionButton(true); +// Log.i(TAG, "After handleWPMissionButton, current state: " + mPointMission.getWaypointMissionState()); + break; + case "start_waypoint": + String wpState = mPointMission.getWaypointMissionState(); + if(wpState.equals(WaypointMissionState.EXECUTION_PAUSED.toString())) { + fromActivityToService("Resuming WP Mission, current state: " + wpState); + mPointMission.resumeWaypointMission(); + } + if(wpState.equals(WaypointMissionState.READY_TO_EXECUTE.toString())) { //TODO check if this can happen + fromActivityToService("Starting WP Mission, current state: " + wpState); + mPointMission.startWaypointMission(); + } + if(wpState.equals(WaypointMissionState.READY_TO_UPLOAD.toString())) { + fromActivityToService("Re-starting interrupted WP Mission, current state: " + wpState); + handleWPMissionButton(true); + Log.i(TAG, "Restarted WaypointMission"); + } + break; + case "pause_mission": + mission = getExecutingMission(); + switch (mission) { + case WAYPOINT_MISSION: + fromActivityToService("Pausing WP mission, current state: " + mPointMission.getWaypointMissionState()); + mPointMission.pauseWaypointMission(); + break; + case HOTPOINT_MISSION: + fromActivityToService("Pausing HP mission, current state: " + mPosMission.getHPState()); + mPosMission.pauseHotPoint(); + break; + case FOLLOW_MISSION: + fromActivityToService("Pausing Follow mission, current state: " + mFollowMission.getFollowState()); + mFollowMission.stopFollowShip(); + break; + } + break; + case "stop_mission": + mission = getExecutingOrPausedMission(); + switch (mission) { + case WAYPOINT_MISSION: + fromActivityToService("Stopping WP Mission, current state: " + mPointMission.getWaypointMissionState()); + mPointMission.stopWaypointMission(); + deletePositionsCount(); + break; + case HOTPOINT_MISSION: + fromActivityToService("Stopping HP Mission, current state: " + mPosMission.getHPState()); + mPosMission.stopHotPoint(); + deleteHotpoint(); + break; + case FOLLOW_MISSION: + fromActivityToService("Stopping Follow Mission, current state: " + mFollowMission.getFollowState()); + mFollowMission.stopFollowShip(); + break; + } + break; + case "resume_mission": + mission = getPausedMission(); + switch (mission) { + case WAYPOINT_MISSION: + fromActivityToService("Resuming WP Mission, current state: " + mPointMission.getWaypointMissionState()); + mPointMission.resumeWaypointMission(); + break; + case HOTPOINT_MISSION: + fromActivityToService("Resuming HP Mission, current state: " + mPosMission.getHPState()); + mPosMission.resumeHotPoint(); + break; + case FOLLOW_MISSION: + fromActivityToService("Resuming Follow Mission, current state: " + mFollowMission.getFollowState()); + mFollowMission.startFollowShip(sharedPreferences); + break; + } + break; + case "del_pos": + deletePositions(); + break; + case "start_follow": + fromActivityToService("Starting Following Mission, current state: " + mFollowMission.getFollowState()); + mFollowMission.startFollowShip(sharedPreferences); + break; + case "start_hotpoint": + fromActivityToService("Starting Hotpoint Mission, current state: " + mPosMission.getHPState()); + drawHotpoint(); + mPosMission.startHotPoint(sharedPreferences); + break; + case "next_target": + nextTarget(); + break; + case "go_ship": + getDroneLocation(); + goToShip(); + break; + case "person": //from jetson when detected a person + if(personNotificationEnabled) + handleSearchPerson(); + break; + case "warning": + String warning = sharedPreferences.getString(getString(R.string.warning), ""); + Toast.makeText(getApplicationContext(), warning, Toast.LENGTH_SHORT).show(); + break; + case "interdiction_radius": + float radius = sharedPreferences.getFloat(getString(R.string.interdiction_area), 15.0f); + Toast.makeText(getApplicationContext(), "Interdiction radius: " + radius, Toast.LENGTH_SHORT).show(); + } + } + //------- END MISSIONS REGION --------------- + + private void fromActivityToService(String msg){ + Intent intent = new Intent("FROM CAMERA"); + intent.putExtra("state", msg); + LocalBroadcastManager.getInstance(CameraActivity.this).sendBroadcast(intent); + } + + private final BroadcastReceiver missionReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if("FROM_SERVER".equals(intent.getAction())){ + String msg = intent.getStringExtra("mission"); + handleMission(msg); + } + } + }; + + @Override + protected void onDestroy() { + mapWidget.onDestroy(); + super.onDestroy(); + if (isLiveStreamManagerOn()){ + DJISDKManager.getInstance().getLiveStreamManager().unregisterListener(listener); + } + mPointMission.removeListenerWaypoint(); + mPosMission.removeListenerHotpoint(); + mFollowMission.removeListenerFollow(); + LocalBroadcastManager.getInstance(CameraActivity.this).unregisterReceiver(missionReceiver); + } + + @Override + public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) { + super.onSaveInstanceState(outState, outPersistentState); + mapWidget.onSaveInstanceState(outState); + } + + @Override + public void onLowMemory() { + super.onLowMemory(); + mapWidget.onLowMemory(); + } + + @Override + protected void onResume() { + super.onResume(); + IntentFilter filter = new IntentFilter("FROM_SERVER"); + LocalBroadcastManager.getInstance(CameraActivity.this).registerReceiver(missionReceiver, filter); + mapWidget.onResume(); + compositeDisposable = new CompositeDisposable(); + compositeDisposable.add(secondaryFPVWidget.getCameraName() + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(this::updateSecondaryVideoVisibility)); + + compositeDisposable.add(systemStatusListPanelWidget.closeButtonPressed() + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(pressed -> { + if (pressed) { + ViewExtensions.hide(systemStatusListPanelWidget); + } + })); + } + + @Override + protected void onPause() { + if (compositeDisposable != null) { + compositeDisposable.dispose(); + compositeDisposable = null; + } + mapWidget.onPause(); + IntentFilter filter = new IntentFilter("FROM_SERVER"); + LocalBroadcastManager.getInstance(CameraActivity.this).registerReceiver(missionReceiver, filter); + super.onPause(); + } + //endregion + + //region Utils + + /** + * Handles a click event on the FPV widget + */ + @OnClick(R.id.widget_fpv) + public void onFPVClick() { + onViewClick(fpvWidget); + } + + /** + * Handles a click event on the secondary FPV widget + */ + @OnClick(R.id.widget_secondary_fpv) + public void onSecondaryFPVClick() { + swapVideoSource(); + } + + + /** + * Swaps the FPV and Map Widgets. + * + * @param view The thumbnail view that was clicked. + */ + + private void onViewClick(View view) { + if (view == fpvWidget && !isMapMini){ + //reorder widgets + parentView.removeView(fpvWidget); + parentView.addView(fpvWidget, 0); + //resize widgets + resizeViews(fpvWidget, mapWidget); + //enable interaction on FPV + fpvInteractionWidget.setInteractionEnabled(true); + //disable user login widget on map + //userAccountLoginWidget.setVisibility(View.GONE); + isMapMini = true; + //stopLiveShow(); + //startLiveStream(); + } else if (view == mapWidget && isMapMini) { + //reorder widgets + parentView.removeView(fpvWidget); + parentView.addView(fpvWidget, parentView.indexOfChild(mapWidget) + 1); + //resize widgets + resizeViews(mapWidget, fpvWidget); + //disable interaction on FPV + fpvInteractionWidget.setInteractionEnabled(false); + //enable user login widget on map + //userAccountLoginWidget.setVisibility(View.VISIBLE); + isMapMini = false; + } + } + + /** + * Helper method to resize the FPV and Map Widgets. + * + * @param viewToEnlarge The view that needs to be enlarged to full screen. + * @param viewToShrink The view that needs to be shrunk to a thumbnail. + */ + private void resizeViews(View viewToEnlarge, View viewToShrink) { + //enlarge first widget + ResizeAnimation enlargeAnimation = new ResizeAnimation(viewToEnlarge, widgetWidth, widgetHeight, deviceWidth, deviceHeight, 0); + viewToEnlarge.startAnimation(enlargeAnimation); + + //shrink second widget + ResizeAnimation shrinkAnimation = new ResizeAnimation(viewToShrink, deviceWidth, deviceHeight, widgetWidth, widgetHeight, widgetMargin); + viewToShrink.startAnimation(shrinkAnimation); + } + + /** + * Swap the video sources of the FPV and secondary FPV widgets. + */ + private void swapVideoSource() { + if (secondaryFPVWidget.getVideoSource() == SettingDefinitions.VideoSource.SECONDARY) { + fpvWidget.setVideoSource(SettingDefinitions.VideoSource.SECONDARY); + secondaryFPVWidget.setVideoSource(SettingDefinitions.VideoSource.PRIMARY); + } else { + fpvWidget.setVideoSource(SettingDefinitions.VideoSource.PRIMARY); + secondaryFPVWidget.setVideoSource(SettingDefinitions.VideoSource.SECONDARY); + } + } + + /** + * Hide the secondary FPV widget when there is no secondary camera. + * + * @param cameraName The name of the secondary camera. + */ + private void updateSecondaryVideoVisibility(String cameraName) { + if (cameraName.equals(PhysicalSource.UNKNOWN.name())) { + secondaryFPVWidget.setVisibility(View.GONE); + } else { + secondaryFPVWidget.setVisibility(View.VISIBLE); + } + } + + + //function to use custom icon for maps + /* + private DJIBitmapDescriptor bitmapDescriptorFromVector(Context context, @DrawableRes int vectorDrawableResourceId) { + Drawable background = ContextCompat.getDrawable(context, R.drawable.outline_pin_drop_black_24); + background.setBounds(0, 0, background.getIntrinsicWidth(), background.getIntrinsicHeight()); + Drawable vectorDrawable = ContextCompat.getDrawable(context, vectorDrawableResourceId); + vectorDrawable.setBounds(40, 20, vectorDrawable.getIntrinsicWidth() + 40, vectorDrawable.getIntrinsicHeight() + 20); + Bitmap bitmap = Bitmap.createBitmap(background.getIntrinsicWidth(), background.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(bitmap); + background.draw(canvas); + vectorDrawable.draw(canvas); + return DJIBitmapDescriptorFactory.fromBitmap(bitmap); + } + + */ + + private DJIBitmapDescriptor bitmapDescriptorFromVector(Context context, int vectorResId) { + Drawable vectorDrawable = ContextCompat.getDrawable(context, vectorResId); + assert vectorDrawable != null; + vectorDrawable.setBounds(0, 0, vectorDrawable.getIntrinsicWidth(), vectorDrawable.getIntrinsicHeight()); + Bitmap bitmap = Bitmap.createBitmap(vectorDrawable.getIntrinsicWidth(), vectorDrawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(bitmap); + vectorDrawable.draw(canvas); + return DJIBitmapDescriptorFactory.fromBitmap(bitmap); + } + + /** + * Animation to change the size of a view. + */ + + private static class ResizeAnimation extends Animation{ + + private static final int DURATION = 300; + + private final View view; + private final int toHeight; + private final int fromHeight; + private final int toWidth; + private final int fromWidth; + private final int margin; + + private ResizeAnimation(View v, int fromWidth, int fromHeight, int toWidth, int toHeight, int margin) { + this.toHeight = toHeight; + this.toWidth = toWidth; + this.fromHeight = fromHeight; + this.fromWidth = fromWidth; + view = v; + this.margin = margin; + setDuration(DURATION); + } + + @Override + protected void applyTransformation(float interpolatedTime, Transformation t) { + float height = (toHeight - fromHeight) * interpolatedTime + fromHeight; + float width = (toWidth - fromWidth) * interpolatedTime + fromWidth; + ConstraintLayout.LayoutParams p = (ConstraintLayout.LayoutParams) view.getLayoutParams(); + p.height = (int) height; + p.width = (int) width; + p.rightMargin = margin; + p.bottomMargin = margin; + view.requestLayout(); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/dji/ux/beta/sample/connection/TcpClientService.java b/app/src/main/java/com/dji/ux/beta/sample/connection/TcpClientService.java new file mode 100644 index 0000000..616ccbb --- /dev/null +++ b/app/src/main/java/com/dji/ux/beta/sample/connection/TcpClientService.java @@ -0,0 +1,498 @@ +package com.dji.ux.beta.sample.connection; + +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.app.Service; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.SharedPreferences; +import android.os.Build; +import android.os.Handler; +import android.os.IBinder; +import android.util.Log; + +import androidx.annotation.Nullable; +import androidx.annotation.RequiresApi; +import androidx.core.app.NotificationCompat; +import androidx.core.content.res.ResourcesCompat; +import androidx.localbroadcastmanager.content.LocalBroadcastManager; + +import com.dji.ux.beta.sample.R; +import com.dji.ux.beta.sample.cameraview.CameraActivity; +import com.dji.ux.beta.sample.mission.GPSPlancia; +import com.dji.ux.beta.sample.utils.DroneState; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.net.Socket; +import java.nio.charset.StandardCharsets; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class TcpClientService extends Service { + private final AtomicBoolean working = new AtomicBoolean(true); + private final AtomicBoolean workingJetson = new AtomicBoolean(true); + private Socket socket; + private Thread connectThread; +// private Handler handler = new Handler(); + BufferedReader bufferedReader;//Declare the input stream object + OutputStream outputStream;//Declare the output stream object + public final String _ip = "192.168.1.104"; //console TP-link +// public final String _ip = "192.168.2.5"; //console RUBICON +// public final String _ip = "192.168.200.185"; //console livorno +// public final String _ip = "213.82.97.234"; //console livorno remoto + //public final String _ip = "192.168.1.105"; //localhost plancia + private final String port = "11000"; +// private final String port = "8089"; //porta console livorno +// private final String TAG = TcpClientService.class.getSimpleName(); + private final String TAG = "TcpClientService"; + Boolean isconnectBoolean = false; + + Boolean isconnectBooleanJetson = false; + private Socket socketJetson; + private Thread connectThreadJetson; + BufferedReader bufferedReaderJetson; //Declare the input stream object + OutputStream outputStreamJetson; //Declare the output stream object + public final String _ipJetson = "192.168.1.100"; //python jetson TP-link +// public final String _ipJetson = "192.168.2.8"; //python jetson RUBICON +// public final String _ipJetson = "192.168.200.22"; //python jetson livorno +// public final String _ipJetson = "146.48.39.44"; //python jetson remoto + private final String portJetson = "65432"; //port python + + private String[] request; + private String[] requestJetson; + private String message = "Hello Server"; + private String messageJetson = "Hello Jetson"; + private static final String ACTION = "FROM_SERVER"; + private static final String KEY = "mission"; + SharedPreferences sharedPreferences; + + private final Runnable runnable = new Runnable() { + //TODO: connettere tramite pulsante + + @RequiresApi(api = Build.VERSION_CODES.N) + @Override + public void run() { + try { + sharedPreferences = getSharedPreferences(getString(R.string.my_pref), Context.MODE_PRIVATE); + socket = new Socket(_ip, Integer.parseInt(port)); + + bufferedReader=new BufferedReader(new InputStreamReader(socket.getInputStream())); + outputStream=socket.getOutputStream(); + + //char[] buffer=new char[256];//Define the array to receive the input stream data + StringBuilder bufferString= new StringBuilder();//Define a character to receive array data + //int tag=0;//First know where to write to the array + + while (working.get()){ + Log.i(TAG, "Connected to " + _ip); + fromServiceToActivity("IP", "server_ip", "Connected to " + _ip); + +// outputStream.write((message +"\n").getBytes(StandardCharsets.UTF_8)); +// //The output stream is sent to the server +// outputStream.flush(); + + //while (bufferedReader.read(buffer) >0){ + String line; + + while ((line = bufferedReader.readLine()) != null){ + bufferString.append(line); + + handleRequest(bufferString.toString()); + + //Log.i(TAG, message); + + if(bufferString.toString().equals("status")){ + boolean disableConsole = sharedPreferences.getBoolean(getString(R.string.disableConsole), true); + StringBuilder bufferStatus = DroneState.getDroneStatePlancia(disableConsole); + //StringBuilder bufferStatus = DroneState.getDroneState(); + //bufferStatus.append("\n").append(DroneState.getStreamInfo()); + //TODO status string +// Log.i(TAG, "State: " + bufferStatus.toString()); + outputStream.write((bufferStatus.toString()).getBytes(StandardCharsets.UTF_8)); + outputStream.flush(); + } + + if(bufferString.toString().equals("closing connection")){ + //Log.i(TAG, "Status connection --> " + bufferStatus.toString()); + fromServiceToActivity("IP", "server_ip", getString(R.string.server_disc)); + break; + } + + //fromServiceToActivity("FROM_SERVER", "server_msg", bufferString.toString()); + bufferString.setLength(0); + } + break; + } + socket.close(); + bufferedReader.close(); + outputStream.close(); + + } catch (IOException e) { + e.printStackTrace(); + } + } + }; + + private final Runnable runnableJetson = new Runnable() { + @RequiresApi(api = Build.VERSION_CODES.N) + @Override + public void run() { + + try { + socketJetson = new Socket(_ipJetson, Integer.parseInt(portJetson)); + + bufferedReaderJetson=new BufferedReader(new InputStreamReader(socketJetson.getInputStream())); + outputStreamJetson=socketJetson.getOutputStream(); + + StringBuilder bufferStringJetson= new StringBuilder();//Define a character to receive array data + + while (workingJetson.get()){ + + Log.i(TAG, "Connected to " + _ipJetson); + fromServiceToActivity("IP", "jetson_ip", "Jetson connected"); +// outputStreamJetson.write((messageJetson +"\n").getBytes(StandardCharsets.UTF_8)); +// outputStreamJetson.flush(); + + String line; + while ((line=bufferedReaderJetson.readLine()) != null){ + bufferStringJetson.append(line); + handleRequest(bufferStringJetson.toString()); + + if(bufferStringJetson.toString().equals("gps")){ + StringBuilder bufferStatus = DroneState.getGPSJetson(); +// Log.i(TAG, "Jetson gps: " + bufferStatus.toString()); + //outputStreamJetson.write((bufferStatus.toString()).getBytes(StandardCharsets.UTF_8)); + //outputStreamJetson.flush(); + sendMessageJetson(bufferStatus.toString()); + } + bufferStringJetson.setLength(0); + } + break; + } + socketJetson.close(); + bufferedReaderJetson.close(); + outputStreamJetson.close(); + + } catch (IOException e) { + e.printStackTrace(); + } + } + }; + + + @Nullable + @Override + public IBinder onBind(Intent intent) { + return null; + } + + @Override + public void onCreate() { + + IntentFilter filter = new IntentFilter("FROM CAMERA"); + LocalBroadcastManager.getInstance(this).registerReceiver(broadcastReceiver, filter); + + if(!isconnectBoolean){ + connectThread = new Thread(runnable); + connectThread.start(); + Log.d(TAG, "Connected to the server successfully"); + isconnectBoolean=true; + } + + if(!isconnectBooleanJetson){ + connectThreadJetson = new Thread(runnableJetson); + connectThreadJetson.start(); + Log.d(TAG, "Connected to the jetson successfully"); + isconnectBooleanJetson=true; + } + startMeForeground(); + } + + @Override + public void onDestroy() { + if (isconnectBoolean){ + working.set(false); + //connectThread.interrupt(); + if(socket!=null && !socket.isClosed()){ + try{ + if(outputStream!=null){ + outputStream.close(); + } + if(bufferedReader!=null){ + bufferedReader.close(); + } + socket.close(); + socket=null; + + } catch (Exception e) { + e.printStackTrace(); + Logger.getLogger(TcpClientService.class.getName()).log(Level.SEVERE, "Can't close a System.in based BufferedReader", e); + } + } + connectThread.interrupt(); + Log.i(TAG, "Closing connection!"); + isconnectBoolean=false; + } + LocalBroadcastManager.getInstance(this).unregisterReceiver(broadcastReceiver); + } + + private void startMeForeground() { + if (Build.VERSION.SDK_INT >= 26) { + String NOTIFICATION_CHANNEL_ID = this.getPackageName(); + String channelName = "Tcp Client Background Service"; + NotificationChannel notificationChannel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, channelName, NotificationManager.IMPORTANCE_DEFAULT); + notificationChannel.setLightColor(ResourcesCompat.getColor(getResources(), R.color.dark_gray, null)); + notificationChannel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC); + NotificationManager notificationManager = getSystemService(NotificationManager.class); + notificationManager.createNotificationChannel(notificationChannel); + + Notification builder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID) + .setOngoing(true) + .setSmallIcon(R.drawable.icon_slider) //TODO:cambiare icona notifica (mettere mini-drone) + .setContentTitle("Tcp Client is running in background") + .setPriority(NotificationManager.IMPORTANCE_MAX) + .setCategory(Notification.CATEGORY_SERVICE) + .build(); + + startForeground(2, builder); + } else {startForeground(1, new Notification());} + } + + private void fromServiceToActivity(String action, String key, String msg){ + Intent intent = new Intent(action); + intent.putExtra(key, msg); + LocalBroadcastManager.getInstance(TcpClientService.this).sendBroadcast(intent); + } + + private void handleRequest(String req){ + sharedPreferences = this.getSharedPreferences(getString(R.string.my_pref), Context.MODE_PRIVATE); + SharedPreferences.Editor editor = sharedPreferences.edit(); + boolean disableConsole = sharedPreferences.getBoolean(getString(R.string.disableConsole), true); + + if(!req.equals("gps") && !req.equals("") && !req.equals("status")) + Log.i(TAG, "Received " + req); + request = req.split("-"); + + if(disableConsole && !request[0].startsWith("hotpoint_jetson") && !request[0].startsWith("warning") && !req.equals("gps") && !req.equals("") && !req.equals("status")) { + Log.i(TAG, "Ignoring command because the console is disabled"); + return; + } + + switch (request[0]){ + case "waypoint_speed": + setSpeed(editor); + break; + case "interdiction_area": + setInterdictionRadius(editor); + break; + case "warning": + setWarning(editor); + break; + case "waypoint_coordinates": + //setCoordinate(editor); + request[1] = request[1].replace(",", "."); + request[2] = request[2].replace(",", "."); + GPSPlancia.populateGPSPlancia(request[1], request[2], request[3]); + //fromServiceToActivity(ACTION, KEY, "coordinates_wp"); + break; + case "follow_coordinates": + request[1] = request[1].replace(",", "."); + request[2] = request[2].replace(",", "."); + setFMCoordinate(editor); + fromServiceToActivity(ACTION, KEY, "start_follow"); + break; + case "go_to_ship": + request[1] = request[1].replace(",", "."); + request[2] = request[2].replace(",", "."); + setShipCoordinate(editor); + fromServiceToActivity(ACTION, KEY, "go_ship"); + break; + case "update_coordinates": + request[1] = request[1].replace(",", "."); + request[2] = request[2].replace(",", "."); +// Log.i(TAG, "Converted update coordinates: " + request[1] + ", " + request[2]); + updateFMCoordinate(editor); + //fromServiceToActivity(ACTION, KEY, "start_follow"); //TODO: cambiare MSG, serve un msg? + break; + case "hotpoint_coordinates": //from command NADSearchAtPos + request[1] = request[1].replace(",", "."); + request[2] = request[2].replace(",", "."); + setHotCoordinate(editor); + fromServiceToActivity(ACTION, KEY, "start_hotpoint"); + break; + case "hotpoint_jetson": + setHotCoordinate(editor); + //TODO: check if it is needed fromServiceToActivity() here too + break; + default: + fromServiceToActivity(ACTION, KEY, request[0]); + } + } + + private void setSpeed(SharedPreferences.Editor editor){ + Log.i(TAG, "Setting speed to: " + request[1]); + float speed = Float.parseFloat(request[1]); + if(checkSpeed(speed, request[1])){ + editor.putFloat(getString(R.string.speed_waypoint), speed); + editor.apply(); + fromServiceToActivity(ACTION, KEY, "speed_wp"); + } + } + + private void setInterdictionRadius(SharedPreferences.Editor editor){ + Log.i(TAG, "New interdiction radius: " + request[1]); + request[1] = request[1].replace(",", "."); + if(isNumeric(request[1])){ +// CameraActivity.setInterdictionRadius(Float.parseFloat(request[1])); + + float interdictionArea = Float.parseFloat(request[1]); + editor.putFloat(getString(R.string.interdiction_area), interdictionArea); + editor.apply(); + fromServiceToActivity(ACTION, KEY, "interdiction_radius"); + } + else { + try { + outputStream.write(("Incorrect or null interdiction_area value! Please send a numeric value\n").getBytes(StandardCharsets.UTF_8)); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + private void setWarning(SharedPreferences.Editor editor){ + Log.i(TAG, "warning " + request[1]); + editor.putString(getString(R.string.warning), request[1]); + editor.apply(); + fromServiceToActivity(ACTION, KEY, "warning"); + } + + private void setCoordinate(SharedPreferences.Editor editor){ + editor.putString(getString(R.string.latitude), request[1]); + editor.putString(getString(R.string.longitude), request[2]); + editor.putString(getString(R.string.altitude), request[3]); + editor.apply(); + } + + private void setHotCoordinate(SharedPreferences.Editor editor){ + editor.putString(getString(R.string.latitude_pos), request[1]); + editor.putString(getString(R.string.longitude_pos), request[2]); + editor.putString(getString(R.string.altitude_pos), request[3]); + editor.putString(getString(R.string.radius_pos), request[4]); + editor.apply(); + } + + private void setFMCoordinate(SharedPreferences.Editor editor){ + editor.putString(getString(R.string.latitude_fm), request[1]); + editor.putString(getString(R.string.longitude_fm), request[2]); + editor.putString(getString(R.string.altitude_fm), request[3]); + editor.apply(); + } + + private void setShipCoordinate(SharedPreferences.Editor editor){ + editor.putString(getString(R.string.latitude_ship), request[1]); + editor.putString(getString(R.string.longitude_ship), request[2]); + editor.putString(getString(R.string.altitude_ship), request[3]); + editor.apply(); + } + + private void updateFMCoordinate(SharedPreferences.Editor editor){ +// //TODO check if the replace is needed or not +// request[1] = request[1].replace(",", "."); +// request[2] = request[2].replace(",", "."); + editor.putString(getString(R.string.latitude_fm), request[1]); + editor.putString(getString(R.string.longitude_fm), request[2]); + editor.apply(); + } + + private boolean checkSpeed(float speed, String msg){ + if (speed <= 0.0f || speed > 15.0f || !isNumeric(msg)){ + try { + outputStream.write(("Incorrect or null speed value! Please send a value in the range [0.0f-15.0f] " + "\n").getBytes(StandardCharsets.UTF_8)); + } catch (IOException e) { + e.printStackTrace(); + } + return false; + } + return true; + } + + private static boolean isNumeric(String strNum) { + if (strNum == null) { + return false; + } + try { + Float.parseFloat(strNum); + } catch (NumberFormatException nfe) { + return false; + } + return true; + } + + private static boolean checkGpsCoordinates(double latitude, double longitude) { + return (latitude > -90 && latitude < 90 && longitude > -180 && longitude < 180) && (latitude != 0f && longitude != 0f); + } + + private final BroadcastReceiver broadcastReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + + Log.i(TAG, "Broadcaster: " + intent.getStringExtra("state")); + String msg= intent.getStringExtra("state"); + if(msg.equals("isStreaming")){ + sendMessageJetson(msg); + } + if(msg.equals("mob_mission")){ + sendMessagePlancia(msg); + } + } + }; + + private void sendMessageJetson(String msg) { + Thread thread = new Thread(new Runnable() { + + @Override + public void run() { + try { + if(outputStreamJetson!=null){ +// Log.i(TAG, "sending to jetson" + msg); + outputStreamJetson.write((msg + "\n").getBytes(StandardCharsets.UTF_8)); + outputStreamJetson.flush(); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + + + thread.start(); + } + + private void sendMessagePlancia(String msg) { + Thread thread = new Thread(new Runnable() { + + @Override + public void run() { + try { + if(outputStream!=null){ + Log.i(TAG, "sending to ship" + msg); + outputStream.write((msg).getBytes(StandardCharsets.UTF_8)); + outputStream.flush(); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + thread.start(); + } + + //TODO: cambiare thread (ogni nuovo messaggio apre un thread-no buono) +} \ No newline at end of file diff --git a/app/src/main/java/com/dji/ux/beta/sample/mission/FollowMission.java b/app/src/main/java/com/dji/ux/beta/sample/mission/FollowMission.java new file mode 100644 index 0000000..8e336e3 --- /dev/null +++ b/app/src/main/java/com/dji/ux/beta/sample/mission/FollowMission.java @@ -0,0 +1,216 @@ +package com.dji.ux.beta.sample.mission; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.content.SharedPreferences; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.drawable.Drawable; +import android.location.Location; +import android.location.LocationManager; +import android.util.Log; +import android.widget.Toast; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.content.ContextCompat; + +import com.dji.mapkit.core.models.DJIBitmapDescriptor; +import com.dji.mapkit.core.models.DJIBitmapDescriptorFactory; +import com.dji.mapkit.core.models.DJILatLng; +import com.dji.mapkit.core.models.annotations.DJIMarkerOptions; +import com.dji.ux.beta.sample.R; +import com.dji.ux.beta.sample.SampleApplication; +import com.dji.ux.beta.sample.utils.DroneState; +import com.dji.ux.beta.sample.utils.ToastUtils; + +import org.jetbrains.annotations.NotNull; + +import dji.common.error.DJIError; +import dji.common.flightcontroller.FlightControllerState; +import dji.common.mission.followme.FollowMeHeading; +import dji.common.mission.followme.FollowMeMission; +import dji.common.mission.followme.FollowMeMissionEvent; +import dji.common.mission.followme.FollowMeMissionState; +import dji.common.model.LocationCoordinate2D; +import dji.common.util.CommonCallbacks; +import dji.sdk.base.BaseProduct; +import dji.sdk.flightcontroller.FlightController; +import dji.sdk.mission.followme.FollowMeMissionOperator; +import dji.sdk.mission.followme.FollowMeMissionOperatorListener; +import dji.sdk.products.Aircraft; +import dji.sdk.sdkmanager.DJISDKManager; +import dji.ux.beta.map.widget.map.MapWidget; + +public class FollowMission { + + private static final String TAG = FollowMission.class.getSimpleName(); + private final Context mContext; + + private FollowMeMissionOperator instance; + private double latitude; + private double longitude; + Thread locationUpdateThread; + private boolean missionRunning = false; + + //TODO: disegnare icona nave che si muove quando cambia posizione + + public FollowMission(Context mContext){ + this.mContext = mContext; + } + + private void setResultToToast(String s) { + ToastUtils.setResultToToast(s); + } + + public FollowMeMissionOperator getFollowM(){ + if(instance == null){ + if(DJISDKManager.getInstance().getMissionControl()!=null){ + instance=DJISDKManager.getInstance().getMissionControl().getFollowMeMissionOperator(); + } + } + return instance; + } + + public void addListenerFollow() { + if (getFollowM() != null){ + getFollowM().addListener(followEventNotificationListener); + } + } + + public void removeListenerFollow(){ + if (getFollowM() != null){ + getFollowM().removeListener(followEventNotificationListener); + } + } + + private final FollowMeMissionOperatorListener followEventNotificationListener = new FollowMeMissionOperatorListener() { + @Override + public void onExecutionUpdate(@NonNull FollowMeMissionEvent followMeMissionEvent) { + Log.i(TAG, "FollowingMission: onExecutionUpdate state is: " + followMeMissionEvent.getCurrentState() + " distance to target is: " + followMeMissionEvent.getDistanceToTarget()); + } + + @Override + public void onExecutionStart() { + Log.i(TAG, "FollowMission started."); + setResultToToast("FollowMission started"); + } + + @Override + public void onExecutionFinish(@Nullable DJIError djiError) { +// locationUpdateThread.interrupt(); + missionRunning = false; + Log.i(TAG, "FollowMission Execution finished: " + (djiError == null ? "Success!" : djiError.getDescription())); + setResultToToast("FollowMission Execution finished: " + (djiError == null ? "Success!" : djiError.getDescription())); + } + }; + + public void startFollowShip(SharedPreferences preferences){ + Log.i(TAG, "Starting FollowMission, current state: " + getFollowState()); + latitude = Double.parseDouble(preferences.getString(mContext.getString(R.string.latitude_fm), "")); + longitude = Double.parseDouble(preferences.getString(mContext.getString(R.string.longitude_fm), "")); + float altitude = Float.parseFloat(preferences.getString(mContext.getString(R.string.altitude_fm), "")); + + if (getFollowM().getCurrentState().toString().equals(FollowMeMissionState.READY_TO_EXECUTE.toString())) { + setResultToToast("Ready to follow"); + + FollowMeMission followMeMission = new FollowMeMission(FollowMeHeading.TOWARD_FOLLOW_POSITION, latitude, longitude, altitude); + //followMeEvent = new FollowMeMissionEvent.Builder().distanceToTarget(34); + Log.i(TAG, "Altitude: " + altitude + "\nLatitude: " + latitude + "\nLongitude: " + longitude); + Log.i(TAG, "FollowMeInfo: " + followMeMission.getLatitude() + " " + followMeMission.getLongitude()); + + //starts the new mission just created + getFollowM().startMission(followMeMission, new CommonCallbacks.CompletionCallback() { + @Override + public void onResult(DJIError djiError) { + if(djiError==null){ + missionRunning = true; +// setResultToToast("Follow mission created successfully, starting follow thread..."); + Log.i(TAG, "Follow mission created successfully with location destination: " + getFollowM().getFollowingTarget() + ", starting follow thread..."); + locationUpdateThread = new Thread(new Runnable() { + @Override + public void run() { +// while (!Thread.currentThread().isInterrupted()) { + while (missionRunning) { +// if(getFollowState().equals(FollowMeMissionState.EXECUTING.toString())) +// { + latitude = Double.parseDouble(preferences.getString(mContext.getString(R.string.latitude_fm), "")); + longitude = Double.parseDouble(preferences.getString(mContext.getString(R.string.longitude_fm), "")); + Log.i(TAG, "FollowMission: " + getFollowState() + ", following target: " + getFollowM().getFollowingTarget()); +// Log.i(TAG, "FollowMeInfo: " + followMeMission.getLatitude() + " " + followMeMission.getLongitude()); + + getFollowM().updateFollowingTarget(new LocationCoordinate2D(latitude, longitude), new CommonCallbacks.CompletionCallback() { + @Override + public void onResult(DJIError djiError) { + if (djiError != null) { +// Log.i(TAG, "FollowMission: Failed to update target GPS: " + djiError.getDescription()); + // setResultToToast("Failed to update target GPS: " + djiError.getDescription()); + } else { + Log.i(TAG, "Location updating " + latitude + " " + longitude); + setResultToToast("Location updating " + latitude + " " + longitude); + } + } + }); + try { + //TODO: cambiare millisecondi in sleep (consigliati 10Hz) + Thread.sleep(1500); + } catch (InterruptedException e) { + e.printStackTrace(); + Thread.currentThread().interrupt(); + } + } +// } + Log.i(TAG, "Update thread ended."); + } + }); + locationUpdateThread.start(); + } else { + setResultToToast("FollowMission: error: " + djiError.getDescription()); + Log.i(TAG, "FollowMission: error: " + djiError.getDescription()); + } + } + }); + } else { + setResultToToast("Not ready to execute FollowMission"); + } + } + + public void stopFollowShip(){ + Log.i(TAG, "Stopping FollowMission, current state: " + getFollowState()); + getFollowM().stopMission(new CommonCallbacks.CompletionCallback() { + @Override + public void onResult(DJIError djiError) { + Log.i(TAG, "FollowMission Stopped: " + (djiError == null ? "Successfully" : djiError.getDescription())); + setResultToToast("FollowMission Stop: " + (djiError == null ? "Successfully" : djiError.getDescription())); + } + }); + } + + public void provaFollow(SharedPreferences preferences){ + Thread locationUpdate = new Thread(new Runnable() { + @Override + public void run() { + while (true) { + + latitude = Double.parseDouble(preferences.getString(mContext.getString(R.string.latitude_fm), "")); + longitude = Double.parseDouble(preferences.getString(mContext.getString(R.string.longitude_fm), "")); + Log.i(TAG, "Location Value: " + "\n" + "Latitude: " + latitude + + "\n" + "Longitude: " + longitude + "\n"); + + try { + Thread.sleep(5000); + } catch (InterruptedException e) { + e.printStackTrace(); + Thread.currentThread().interrupt(); + } + } + } + }); + locationUpdate.start(); + + } + + public String getFollowState() { + return getFollowM().getCurrentState().toString(); + } +} diff --git a/app/src/main/java/com/dji/ux/beta/sample/mission/GPSPlancia.java b/app/src/main/java/com/dji/ux/beta/sample/mission/GPSPlancia.java new file mode 100644 index 0000000..47939e0 --- /dev/null +++ b/app/src/main/java/com/dji/ux/beta/sample/mission/GPSPlancia.java @@ -0,0 +1,100 @@ +package com.dji.ux.beta.sample.mission; + +import android.location.Location; +import android.util.Log; +import android.widget.Toast; + +import com.dji.ux.beta.sample.R; +import com.dji.ux.beta.sample.utils.ToastUtils; +import com.google.android.gms.maps.model.LatLng; +import com.google.maps.android.SphericalUtil; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +public class GPSPlancia { + + private static final String TAG = GPSPlancia.class.getSimpleName(); + private double latitude; + private double longitude; + private float altitude; + private static CopyOnWriteArrayList gpsPlanciaList = new CopyOnWriteArrayList<>(); + private static HashSet gpsPlanciaHashSet = new HashSet<>(); + + public GPSPlancia(double latitude, double longitude, float altitude){ + this.latitude = latitude; + this.longitude = longitude; + this.altitude = altitude; + } + + public double getLatitude() { + return latitude; + } + + public float getAltitude() { + return altitude; + } + + public double getLongitude() { + return longitude; + } + + public static CopyOnWriteArrayList getGPSPlanciaList() { + return gpsPlanciaList; + } + + public static void populateGPSPlancia(String latitudeString, String longitudeString, String altitudeString) { + double latitude = Double.parseDouble(latitudeString); + double longitude = Double.parseDouble(longitudeString); + float altitude = Float.parseFloat(altitudeString); + GPSPlancia gpsPlancia = new GPSPlancia(latitude, longitude, altitude); + if(!gpsPlanciaList.contains(gpsPlancia)){ + gpsPlanciaList.add(new GPSPlancia(latitude, longitude, altitude)); + } + } + + private static boolean checkPosDistance(LatLng oldPos, LatLng currentPos, double distanceMeters){ + double distance = SphericalUtil.computeDistanceBetween(oldPos, currentPos); + return distance >= distanceMeters; + } + + //remove waypoints that are closer than interdictionRadius between them + public static List getCleanedGPSList(double interdictionRadius){ + Log.i(TAG, "Initial GPS list length: " + gpsPlanciaList.size()); + List cleanedList = new ArrayList<>(); + if(!gpsPlanciaList.isEmpty()){ + cleanedList.add(gpsPlanciaList.get(0)); + LatLng oldPos = new LatLng((gpsPlanciaList.get(0).getLatitude()), cleanedList.get(0).getLongitude()); + for (GPSPlancia gps: gpsPlanciaList) { + LatLng currentPos = new LatLng(gps.getLatitude(), gps.getLongitude()); + if(checkPosDistance(oldPos, currentPos, interdictionRadius)){ + cleanedList.add(gps); + oldPos = currentPos; + } + if(cleanedList.size()==99){ + break; + } + } + } else { + ToastUtils.setResultToToast("GPS list empty!"); +// Toast.makeText(getApplicationContext(), "GPS list empty!", Toast.LENGTH_SHORT).show(); + } + + Log.i(TAG, "Cleaned GPS list length: " + cleanedList.size()); + return cleanedList; + } + + //remove first waypointCount element from gpsPlanciaList + public static void updateGPSList(int waypointCount) { +// int wpToRemove = waypointCount/2; + //TODO capire perche' serve il /2 + int wpToRemove = waypointCount + 1; //the parameter is the number of waypoints to be deleted + Log.i(TAG, "Initial GPS list length: " + gpsPlanciaList.size() + ", waypointCount: " + waypointCount + ", therefore removing " + wpToRemove + " waypoints"); + for (int i = 0; i < wpToRemove; i++) { + gpsPlanciaList.remove(0); + } + Log.i(TAG, "Updated GPS list length: " + gpsPlanciaList.size()); + } +} diff --git a/app/src/main/java/com/dji/ux/beta/sample/mission/PointMission.java b/app/src/main/java/com/dji/ux/beta/sample/mission/PointMission.java new file mode 100644 index 0000000..c99fef4 --- /dev/null +++ b/app/src/main/java/com/dji/ux/beta/sample/mission/PointMission.java @@ -0,0 +1,352 @@ +package com.dji.ux.beta.sample.mission; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.graphics.drawable.Drawable; +import android.os.Handler; +import android.os.Looper; +import android.util.Log; +import android.widget.Toast; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.content.ContextCompat; +import androidx.localbroadcastmanager.content.LocalBroadcastManager; + +import com.dji.ux.beta.sample.R; +import com.dji.ux.beta.sample.cameraview.CameraActivity; +import com.dji.ux.beta.sample.connection.TcpClientService; +import com.dji.ux.beta.sample.utils.ToastUtils; +import com.google.android.material.floatingactionbutton.FloatingActionButton; + +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +import dji.common.error.DJIError; +import dji.common.mission.waypoint.Waypoint; +import dji.common.mission.waypoint.WaypointAction; +import dji.common.mission.waypoint.WaypointActionType; +import dji.common.mission.waypoint.WaypointMission; +import dji.common.mission.waypoint.WaypointMissionDownloadEvent; +import dji.common.mission.waypoint.WaypointMissionExecuteState; +import dji.common.mission.waypoint.WaypointMissionExecutionEvent; +import dji.common.mission.waypoint.WaypointMissionFinishedAction; +import dji.common.mission.waypoint.WaypointMissionFlightPathMode; +import dji.common.mission.waypoint.WaypointMissionHeadingMode; +import dji.common.mission.waypoint.WaypointMissionState; +import dji.common.mission.waypoint.WaypointMissionUploadEvent; +import dji.common.util.CommonCallbacks; +import dji.sdk.mission.waypoint.WaypointMissionOperator; +import dji.sdk.mission.waypoint.WaypointMissionOperatorListener; +import dji.sdk.sdkmanager.DJISDKManager; + + +public class PointMission { + + private static final String TAG = PointMission.class.getSimpleName(); + + private WaypointMissionOperator instance; + public static WaypointMission.Builder waypointMissionBuilder; + private final WaypointMissionFinishedAction mFinishedAction = WaypointMissionFinishedAction.NO_ACTION; + //private final WaypointMissionFinishedAction mFinishedAction = WaypointMissionFinishedAction.GO_HOME; this action if you want the drone to come back to first WP when execution finished + private final WaypointMissionHeadingMode mHeadingMode = WaypointMissionHeadingMode.AUTO; + private int wayPointCount = 0; + private int nextWaypointIndex = -1; + + //private float waypointSpeed = 10.0f; //range [2,15] m/s. + + private final Context mContext; + + //TODO: decidere se rimuovere i controlli di stato prima di start(pause/resume/stop (sono presenti in start di follow e pos) + + public PointMission(Context mContext){ + this.mContext = mContext; + } + +// private void setResultToToast(Context mContext, String s) { +// Toast.makeText(mContext, s, Toast.LENGTH_SHORT).show(); +// } + + private void setResultToToast(String s) { + ToastUtils.setResultToToast(s); + } + + public WaypointMissionOperator getWaypointM (){ + if (instance == null) { + if(DJISDKManager.getInstance().getMissionControl()!= null) { + instance = DJISDKManager.getInstance().getMissionControl().getWaypointMissionOperator(); + } + } + return instance; + } + + public void addListenerWaypoint() { + if (getWaypointM() != null){ + getWaypointM().addListener(waypointEventNotificationListener); + } + } + + public void removeListenerWaypoint(){ + if (getWaypointM() != null){ + getWaypointM().removeListener(waypointEventNotificationListener); + } + } + + private final WaypointMissionOperatorListener waypointEventNotificationListener = new WaypointMissionOperatorListener() { + @Override + public void onDownloadUpdate(@NonNull WaypointMissionDownloadEvent waypointMissionDownloadEvent) { + + } + + @Override + public void onUploadUpdate(@NonNull WaypointMissionUploadEvent waypointMissionUploadEvent) { + if (waypointMissionUploadEvent.getProgress() != null + && waypointMissionUploadEvent.getProgress().isSummaryUploaded + && waypointMissionUploadEvent.getProgress().uploadedWaypointIndex == (waypointMissionUploadEvent.getProgress().totalWaypointCount) - 1) { + Log.i(TAG, "onUploadUpdate: Mission uploaded successfully, current state: " + getWaypointMissionState()); + } + } + + @Override + public void onExecutionUpdate(@NonNull WaypointMissionExecutionEvent waypointMissionExecutionEvent) { + if(waypointMissionExecutionEvent.getProgress().isWaypointReached){ + nextWaypointIndex = waypointMissionExecutionEvent.getProgress().targetWaypointIndex; + Log.i(TAG, "Waypoint reached " + nextWaypointIndex); + } + } + + @Override + public void onExecutionStart() { + Log.i(TAG, "Execution started, number of waypoints: " + waypointMissionBuilder.getWaypointCount()); + } + + @Override + public void onExecutionFinish(@Nullable @org.jetbrains.annotations.Nullable DJIError djiError) { + Log.i(TAG, "Execution ended with nextWaypointIndex: " + nextWaypointIndex + " and waypointMissionBuilder.getWaypointCount(): " + waypointMissionBuilder.getWaypointCount()); + if(nextWaypointIndex >= 0 && nextWaypointIndex == waypointMissionBuilder.getWaypointCount()-1) { + Log.i(TAG, "All waypoints reached"); + setResultToToast("Execution finished: " + (djiError == null ? "Success!" : djiError.getDescription())); + deleteWaypoint(); + + CameraActivity activity = (CameraActivity) mContext; + activity.deleteMarkers(); + activity.setPersonNotification(false); + } +// waypointMissionBuilder = null; + nextWaypointIndex = -1; + } + }; + + public void createWaypointFromList(List cleanedList){ + if(waypointMissionBuilder == null){ + waypointMissionBuilder = new WaypointMission.Builder(); + } + if (cleanedList.isEmpty()){ + setResultToToast("List of positions is empty!"); + } else { + for (GPSPlancia gps: cleanedList) { + + Waypoint waypoint = new Waypoint(gps.getLatitude(), gps.getLongitude(), gps.getAltitude()); + //waypoint.addAction(new WaypointAction(WaypointActionType.STAY, 0)); + waypoint.gimbalPitch = -90.0f; //from = -135.0, to = 45.0) + waypointMissionBuilder.addWaypoint(waypoint); + } + } + Log.i(TAG, "WP MissBuildList " + waypointMissionBuilder.getWaypointList().toString()); + Log.i(TAG, "WP MissBuildList size: " + waypointMissionBuilder.getWaypointList().size()); +// Log.i(TAG, "WP WaypointCount(): " + waypointMissionBuilder.getWaypointCount()); + if(waypointMissionBuilder!= null && !waypointMissionBuilder.getWaypointList().isEmpty()){ + setResultToToast("Positions added to the map!"); + } + } + + public void configWaypointMission(float waypointSpeed){ + if (waypointMissionBuilder == null){ + waypointMissionBuilder = new WaypointMission.Builder() + .finishedAction(mFinishedAction) + .headingMode(mHeadingMode) + .autoFlightSpeed(waypointSpeed) + .maxFlightSpeed(waypointSpeed) + .flightPathMode(WaypointMissionFlightPathMode.NORMAL) + .setGimbalPitchRotationEnabled(true); //per controllare gimbal pitch + } else{ + waypointMissionBuilder + .finishedAction(mFinishedAction) + .headingMode(mHeadingMode) + .autoFlightSpeed(waypointSpeed) + .maxFlightSpeed(waypointSpeed) + .flightPathMode(WaypointMissionFlightPathMode.NORMAL) + .setGimbalPitchRotationEnabled(true); //per controllare gimbal pitch + } + + Log.i(TAG, "is gimbal rotation enabled? " + waypointMissionBuilder.isGimbalPitchRotationEnabled()); + Log.i(TAG, "Auto Flight Speed " + waypointMissionBuilder.getAutoFlightSpeed()); + + DJIError error = getWaypointM().loadMission(waypointMissionBuilder.build()); + if(error == null){ +// setResultToToast("loadMission success"); + } else { + setResultToToast("loadMission failed " + error.getDescription()); + Log.i(TAG, "loadMission failed " + error.getDescription()); + } + } + + public String getWaypointMissionState(){ + return getWaypointM().getCurrentState().toString(); + } + + public void uploadAndStartWayPointMission(){ + Log.i(TAG, "Uploading WP mission, current state is: " + getWaypointMissionState()); + getWaypointM().uploadMission(new CommonCallbacks.CompletionCallback() { + @Override + public void onResult(DJIError djiError) { + if(djiError == null){ + Log.i(TAG, "Mission uploaded successfully, current state: " + getWaypointMissionState()); + setResultToToast("Mission upload successfully!"); + startWaypointMission(); + } else { + Log.i(TAG, "Mission upload failed, error: " + djiError.getDescription() + " retrying..."); + setResultToToast("Mission upload failed, error: " + djiError.getDescription() + " retrying..."); + getWaypointM().retryUploadMission(null); + } + } + }); + } + + public void setWaypointMissionSpeed(float waypointSpeed){ //(@FloatRange(from = -15.0f, to = 15.0f) + getWaypointM().setAutoFlightSpeed(waypointSpeed, new CommonCallbacks.CompletionCallback() { + @Override + public void onResult(DJIError djiError) { + if(djiError == null){ + setResultToToast("Speed set successfully!"); + } else { + setResultToToast("Unable to set the desired speed: " + djiError.getDescription()); + } + } + }); + } + + public void startWaypointMission(){ + Log.i(TAG, "Starting WP mission, current state: " + getWaypointMissionState()); + getWaypointM().startMission(new CommonCallbacks.CompletionCallback() { + @Override + public void onResult(DJIError djiError) { + if(djiError == null){ + Log.i(TAG, "WaypointMission started successfully!"); + setResultToToast("Mission start successfully!"); + } + else { + Log.i(TAG, "Error while starting WaypointMission: " + djiError.getDescription()); + setResultToToast("Error while starting WaypointMission: " + djiError.getDescription()); + } + } + }); + } + + public int pauseWaypointMission(){ + if (getWaypointMissionState().equals(WaypointMissionState.EXECUTING.toString())){ + getWaypointM().pauseMission(new CommonCallbacks.CompletionCallback() { + @Override + public void onResult(DJIError djiError) { + Log.i(TAG, "WaypointMission Pause: " + (djiError == null ? "Successfully" : djiError.getDescription())); + setResultToToast("WaypointMission Pause: " + (djiError == null ? "Successfully" : djiError.getDescription())); + } + }); + } else { + setResultToToast("Couldn't pause WaypointMission because state is not executing, current state: " + getWaypointMissionState()); + } + return nextWaypointIndex; + } + + public void resumeWaypointMission(){ + if (getWaypointMissionState().equals(WaypointMissionState.EXECUTION_PAUSED.toString())){ + getWaypointM().resumeMission(new CommonCallbacks.CompletionCallback() { + @Override + public void onResult(DJIError djiError) { + Log.i(TAG, "WaypointMission Resume: " + (djiError == null ? "Successfully" : djiError.getDescription())); + setResultToToast("WaypointMission Resume: " + (djiError == null ? "Successfully" : djiError.getDescription())); + } + }); + } else { + setResultToToast("Couldn't resume WaypointMission because state is not paused, current state: " + getWaypointMissionState()); + } + } + + public int stopWaypointMission(){ + if (getWaypointMissionState().equals(WaypointMissionState.EXECUTING.toString()) || getWaypointMissionState().equals(WaypointMissionState.EXECUTION_PAUSED.toString())){ + getWaypointM().stopMission(new CommonCallbacks.CompletionCallback() { + @Override + public void onResult(DJIError djiError) { + Log.i(TAG, "WaypointMission Stop: " + (djiError == null ? "Successfully" : djiError.getDescription())); + setResultToToast("WaypointMission Stop: " + (djiError == null ? "Successfully" : djiError.getDescription())); + } + }); + } + else { + setResultToToast("Couldn't stop WaypointMission because state is not executing or paused, current state: " + getWaypointMissionState()); + } + return nextWaypointIndex; + } + + private boolean deleteWaypointsFromMissionBuilder() { + if(waypointMissionBuilder!=null) { + Log.i(TAG, "Deleting: " + waypointMissionBuilder.getWaypointCount() + " elements in waypointMissionBuilder.getWaypointList()"); + int totalWp = waypointMissionBuilder.getWaypointCount(); + + for (int i = 0; i < totalWp; i++) { + waypointMissionBuilder.removeWaypoint(0); + } + Log.i(TAG, "WP MissBuildList after delete" + waypointMissionBuilder.getWaypointList().toString()); + Log.i(TAG, "WP MissBuildCount after delete: " + waypointMissionBuilder.getWaypointCount()); + + return true; + } + else { + Log.i(TAG, "waypointMissionBuilder is null, nothing to delete"); + return false; + } + } + + //remove all waypoints from waypointMissionBuilder (and set it tu null) and from GPSPlancia + public void deleteWaypoint(){ + boolean deleted = deleteWaypointsFromMissionBuilder(); + + if (deleted) { + GPSPlancia.getGPSPlanciaList().clear(); + nextWaypointIndex = -1; + setResultToToast("All positions deleted"); + } else { + setResultToToast("No position to delete"); + //return "No position to delete"; + } +// wayPointCount = 0; + } + + public int getWayPointCount(){ + return this.wayPointCount; + } + + public int getNextWaypointIndex() { + return this.nextWaypointIndex; + } + + // remove visited waypoints from waypointMissionBuilder and from GPSPlancia + public void deleteWPCount(){ +// if(wayPointCount>0 && !waypointMissionBuilder.getWaypointList().isEmpty()){ +// waypointMissionBuilder.getWaypointList().subList(0, wayPointCount/2).clear(); +// //TODO capire perche' serve il /2 +// } + + boolean deleted = deleteWaypointsFromMissionBuilder(); + +// if (deleted) { +// setResultToToast("Visited positions deleted"); +// } + if(nextWaypointIndex>=0 && !GPSPlancia.getGPSPlanciaList().isEmpty()) { + GPSPlancia.updateGPSList(nextWaypointIndex); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/dji/ux/beta/sample/mission/PosMission.java b/app/src/main/java/com/dji/ux/beta/sample/mission/PosMission.java new file mode 100644 index 0000000..c6c0f7c --- /dev/null +++ b/app/src/main/java/com/dji/ux/beta/sample/mission/PosMission.java @@ -0,0 +1,163 @@ +package com.dji.ux.beta.sample.mission; + +import android.content.Context; +import android.content.SharedPreferences; +import android.os.Looper; +import android.util.Log; +import android.widget.Toast; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.dji.ux.beta.sample.R; +import com.dji.ux.beta.sample.utils.ToastUtils; + +import dji.common.error.DJIError; +import dji.common.mission.hotpoint.HotpointHeading; +import dji.common.mission.hotpoint.HotpointMission; +import dji.common.mission.hotpoint.HotpointMissionEvent; +import dji.common.mission.hotpoint.HotpointMissionState; +import dji.common.mission.hotpoint.HotpointStartPoint; +import dji.common.mission.waypoint.WaypointMissionState; +import dji.common.model.LocationCoordinate2D; +import dji.common.util.CommonCallbacks; +import dji.sdk.mission.hotpoint.HotpointMissionOperator; +import dji.sdk.mission.hotpoint.HotpointMissionOperatorListener; +import dji.sdk.sdkmanager.DJISDKManager; +//TODO: qui ho utilizzato il toast di ToastUtils +//TODO: mandare gli stati delle missioni alla plancia quando viene avviata/stoppata ecc.. + +public class PosMission { + + private static final String TAG = PosMission.class.getSimpleName(); + + private HotpointMissionOperator instance; + private final HotpointStartPoint hotpointStartPoint = HotpointStartPoint.NEAREST; + private final HotpointHeading hotpointHeading = HotpointHeading.TOWARDS_HOT_POINT; + + private final Context mContext; + + public PosMission(Context mContext) { + this.mContext = mContext; + } + + private void setResultToToast(String s) { + ToastUtils.setResultToToast(s); + } + + public HotpointMissionOperator getHotpointM(){ + if (instance==null){ + if(DJISDKManager.getInstance().getMissionControl()!=null){ + instance = DJISDKManager.getInstance().getMissionControl().getHotpointMissionOperator(); + } + } + return instance; + } + + public String getHPState(){ + return getHotpointM().getCurrentState().toString(); + } + + public void addListenerHotpoint() { + if (getHotpointM() != null){ + getHotpointM().addListener(hotpointEventNotificationListener); + } + } + + public void removeListenerHotpoint(){ + if (getHotpointM() != null){ + getHotpointM().removeListener(hotpointEventNotificationListener); + } + } + + private final HotpointMissionOperatorListener hotpointEventNotificationListener = new HotpointMissionOperatorListener() { + @Override + public void onExecutionUpdate(@NonNull HotpointMissionEvent hotpointMissionEvent) { + + } + + @Override + public void onExecutionStart() { + + } + + @Override + public void onExecutionFinish(@Nullable DJIError djiError) { + setResultToToast("HotpointMission finished: " + (djiError == null ? "Success!" : djiError.getDescription())); + } + }; + + public void startHotPoint(SharedPreferences preferences){ + Log.i(TAG, "Starting HotpointMission, current state: " + getHPState()); + if (getHPState().equals(HotpointMissionState.READY_TO_EXECUTE.toString())){ + double latitude = Double.parseDouble(preferences.getString(mContext.getString(R.string.latitude_pos), "")); + double longitude = Double.parseDouble(preferences.getString(mContext.getString(R.string.longitude_pos), "")); + double altitude = Double.parseDouble(preferences.getString(mContext.getString(R.string.altitude_pos), "")); + double radius = Double.parseDouble(preferences.getString(mContext.getString(R.string.radius_pos), "5.0")); + float angularVel = 20.0f; + + HotpointMission mHotpointMission = new HotpointMission(new LocationCoordinate2D(latitude, longitude), altitude, radius, + angularVel, true, hotpointStartPoint, hotpointHeading); + + Log.i(TAG, "Altitude: " + altitude + "\nLatitude: " + latitude + "\nLongitude: " + longitude + "\nradius: " + radius); + Log.i(TAG, "Hotpoint " + mHotpointMission.getHotpoint().toString()); + + getHotpointM().startMission(mHotpointMission, new CommonCallbacks.CompletionCallback() { + @Override + public void onResult(DJIError djiError) { + if (djiError != null) { + Log.i(TAG, "Error while starting HotpointMission, current state: " + getHPState() + ", ERROR: " + djiError.getDescription()); + setResultToToast("Could not start hotpoint mission: " + djiError.getDescription()); + } else { + Log.i(TAG, "HotpointMission started Successfully! Current state: " + getHPState()); + setResultToToast("HotpointMission started Successfully!"); + //TODO: inviare alla plancia + } + } + }); + } + } + + public void pauseHotPoint(){ + if (getHPState().equals(WaypointMissionState.EXECUTING.toString())){ + getHotpointM().pause(new CommonCallbacks.CompletionCallback() { + @Override + public void onResult(DJIError djiError) { + Log.i(TAG, "HotpointMission Paused: " + (djiError == null ? "Successfully" : djiError.getDescription())); + setResultToToast("HotpointMission Paused: " + (djiError == null ? "Successfully" : djiError.getDescription())); + } + }); + } else { + setResultToToast("Couldn't pause HotpointMission because state is not executing, current state: " + getHPState()); + } + } + + public void resumeHotPoint(){ + if (getHPState().equals(WaypointMissionState.EXECUTION_PAUSED.toString())){ + getHotpointM().resume(new CommonCallbacks.CompletionCallback() { + @Override + public void onResult(DJIError djiError) { + Log.i(TAG, "HotpointMission Resumed: " + (djiError == null ? "Successfully" : djiError.getDescription())); + setResultToToast("HotpointMission Resumed: " + (djiError == null ? "Successfully" : djiError.getDescription())); + } + }); + } else { + setResultToToast("Couldn't resume HotpointMission because state is not paused, current state: " + getHPState()); + } + } + + public void stopHotPoint(){ + if (getHPState().equals(WaypointMissionState.INITIAL_PHASE.toString()) || getHPState().equals(WaypointMissionState.EXECUTING.toString()) || getHPState().equals(WaypointMissionState.EXECUTION_PAUSED.toString())){ + getHotpointM().stop(new CommonCallbacks.CompletionCallback() { + @Override + public void onResult(DJIError djiError) { + Log.i(TAG, "HotpointMission Stopped: " + (djiError == null ? "Successfully" : djiError.getDescription())); + setResultToToast("HotpointMission Stopped: " + (djiError == null ? "Successfully" : djiError.getDescription())); + } + }); + } + else { + setResultToToast("Couldn't stop HotpointMission because state is not executing or paused, current state: " + getHPState()); + } + } +} diff --git a/app/src/main/java/com/dji/ux/beta/sample/utils/DroneState.java b/app/src/main/java/com/dji/ux/beta/sample/utils/DroneState.java new file mode 100644 index 0000000..009225f --- /dev/null +++ b/app/src/main/java/com/dji/ux/beta/sample/utils/DroneState.java @@ -0,0 +1,295 @@ +package com.dji.ux.beta.sample.utils; + +/* +Util class that provides info onm drone status such as battery charge, altitude, speed, etc.. + */ + +import android.content.Context; +import android.content.SharedPreferences; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.drawable.Drawable; +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.core.content.ContextCompat; + +import com.dji.mapkit.core.models.DJIBitmapDescriptor; +import com.dji.mapkit.core.models.DJIBitmapDescriptorFactory; +import com.dji.ux.beta.sample.R; +import com.dji.ux.beta.sample.SampleApplication; +import com.dji.ux.beta.sample.mission.GPSPlancia; +import com.google.android.gms.maps.model.LatLng; + +import org.jetbrains.annotations.NotNull; + +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Locale; +import java.util.concurrent.TimeUnit; + +import dji.common.battery.BatteryState; +import dji.common.error.DJIError; +import dji.common.flightcontroller.FlightControllerState; +import dji.common.model.LocationCoordinate2D; +import dji.common.util.CommonCallbacks; +import dji.sdk.base.BaseProduct; +import dji.sdk.flightcontroller.FlightController; +import dji.sdk.flighthub.FlightHubManager; +import dji.sdk.flighthub.model.RealTimeFlightData; +import dji.sdk.products.Aircraft; +import dji.sdk.sdkmanager.DJISDKManager; + + +public class DroneState { + + private static final String TAG = DroneState.class.getSimpleName(); + protected static StringBuilder stringState = new StringBuilder(); + protected static StringBuilder stringStatePlancia = new StringBuilder(); + protected static StringBuilder stringStream = new StringBuilder(); + protected static StringBuilder stringJetson; + protected static String altitude; + protected static String latitude; + protected static String longitude; + protected static String whichLatitude; + protected static int batteryCharge; + private static FlightController flightController; + private static String tracking; + private static String searching; + private static String following; + private static String serialNumber; + private static String speed; + private static LatLng homeLocation; + + public static LatLng getHomeLocation(){ + BaseProduct product = SampleApplication.getProductInstance(); + if (product instanceof Aircraft) { flightController = ((Aircraft) product).getFlightController(); } + if(flightController != null){ + flightController.getHomeLocation(new CommonCallbacks.CompletionCallbackWith() { + @Override + public void onSuccess(LocationCoordinate2D locationCoordinate2D) { + homeLocation = new LatLng(locationCoordinate2D.getLatitude(), locationCoordinate2D.getLongitude()); + } + + @Override + public void onFailure(DJIError djiError) { + + } + }); + } else{ + homeLocation = new LatLng(43.719259015524976, 10.421048893480233); + } + return homeLocation; + } + + + private static void getDroneLocation(BaseProduct product){ + + if (product instanceof Aircraft) { flightController = ((Aircraft) product).getFlightController(); } + + if (flightController !=null){ + flightController.setStateCallback(new FlightControllerState.Callback() { + @Override + public void onUpdate(@NonNull @NotNull FlightControllerState flightControllerState) { + + if (flightControllerState.getAircraftLocation() != null) { + if(!Double.isNaN(flightControllerState.getAircraftLocation().getLatitude()) && !Double.isNaN(flightControllerState.getAircraftLocation().getLongitude())) { + latitude = String.valueOf(flightControllerState.getAircraftLocation().getLatitude()); + longitude = String.valueOf(flightControllerState.getAircraftLocation().getLongitude()); + altitude = String.valueOf(flightControllerState.getAircraftLocation().getAltitude()); + } else { + latitude = "0.0"; + longitude = "0.0"; + latitude = "0.0"; + } + } + } + }); + } + } + + public static void getDroneBattery(){ + + try { + SampleApplication.getProductInstance().getBattery().setStateCallback(new BatteryState.Callback() { + @Override + public void onUpdate(BatteryState batteryState) { + batteryCharge = batteryState.getChargeRemainingInPercent(); + } + }); + } catch (Exception ignored){ + } + } + + public static void setTracking(){ + whichLatitude = "Drone Latitude: "; + //tracking = DJISDKManager.getInstance().getMissionControl().getWaypointMissionOperator().getCurrentState().toString(); + tracking = DJISDKManager.getInstance().getMissionControl().getHotpointMissionOperator().getCurrentState().toString(); + searching = DJISDKManager.getInstance().getMissionControl().getWaypointMissionOperator().getCurrentState().toString(); + if(searching.equals("EXECUTING")){ + whichLatitude = "Searching Drone Latitude: "; + } + following = DJISDKManager.getInstance().getMissionControl().getFollowMeMissionOperator().getCurrentState().toString(); + } + + public static StringBuilder getDroneState(){ + + BaseProduct product = SampleApplication.getProductInstance(); + if (product != null && product.isConnected()) { + + getDroneLocation(product); + getDroneBattery(); + setTracking(); + stringState.delete(0, stringState.length()); + stringState.append("\n"); + stringState.append("Product Connected!").append("\n"); + stringState.append("Product Name: ").append(product.getModel().getDisplayName()).append("\n"); + stringState.append("Battery charge: ").append(batteryCharge).append("%\n"); + stringState.append(whichLatitude).append(altitude).append("m\n"); + stringState.append("Drone latitude: ").append(latitude).append("\n"); + stringState.append("Drone longitude: ").append(longitude).append("\n"); + stringState.append("Flight State: ").append(tracking).append("\n"); + stringState.append("Following State: ").append(following).append("\n"); + + } else { + stringState.delete(0, stringState.length()); + stringState.append("No product connected.").append("\n"); + } + return stringState; + } + + public static StringBuilder getStreamInfo(){ + BaseProduct product = SampleApplication.getProductInstance(); + if (product != null && product.isConnected()) { + if(DJISDKManager.getInstance().getLiveStreamManager().isStreaming()) { + stringStream.append("Streaming live on: ").append(DJISDKManager.getInstance().getLiveStreamManager().getLiveUrl()).append("\n"); + long startTime = DJISDKManager.getInstance().getLiveStreamManager().getStartTime(); + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()); + String sd = sdf.format(new Date(Long.parseLong(String.valueOf(startTime)))); + stringStream.append("Start Time: ").append(sd).append("\n");; + stringStream.append("Video Resolution: ").append(DJISDKManager.getInstance().getLiveStreamManager().getLiveVideoResolution()).append("\n");; + stringStream.append("Video BitRate:").append(DJISDKManager.getInstance().getLiveStreamManager().getLiveVideoBitRate()).append(" kpbs\n"); + stringStream.append("Audio BitRate:").append(DJISDKManager.getInstance().getLiveStreamManager().getLiveAudioBitRate()).append(" kpbs\n"); + stringStream.append("Video FPS:").append(DJISDKManager.getInstance().getLiveStreamManager().getLiveVideoFps()).append("\n"); + stringStream.append("Video Cache size:").append(DJISDKManager.getInstance().getLiveStreamManager().getLiveVideoCacheSize()).append(" frame"); + } else { + stringStream.append("No streaming available.").append("\n"); + } + } else { + stringStream.append("No product connected.").append("\n"); + } + return stringStream; + } + + public static StringBuilder getDroneStatePlancia(boolean disableConsole){ + BaseProduct product = SampleApplication.getProductInstance(); + if (product != null && product.isConnected()) { + + getDroneLocation(product); + getDroneBattery(); + setTracking(); +// getRealTimeData(); + getSpeed(); + stringStatePlancia.delete(0, stringStatePlancia.length()); +// stringStatePlancia.append("\n"); + stringStatePlancia.append("true").append("#"); + stringStatePlancia.append(disableConsole).append("#"); + stringStatePlancia.append(batteryCharge).append("#"); + stringStatePlancia.append(speed).append("#"); +// stringStatePlancia.append("0").append("#"); + stringStatePlancia.append(altitude).append("#"); + stringStatePlancia.append(latitude).append("#"); + stringStatePlancia.append(longitude).append("#"); + if(tracking != null && tracking.equals("EXECUTING")) + stringStatePlancia.append("true").append("#"); + else + stringStatePlancia.append("false").append("#"); + + if(following != null && following.equals("EXECUTING")) + stringStatePlancia.append("true").append("#"); + else + stringStatePlancia.append("false").append("#"); + + if(searching != null && searching.equals("EXECUTING")) + stringStatePlancia.append("true").append("#"); + else + stringStatePlancia.append("false").append("#"); + if(DJISDKManager.getInstance().getLiveStreamManager().isStreaming()) { + stringStatePlancia.append(DJISDKManager.getInstance().getLiveStreamManager().getLiveUrl()).append("#"); + } else { + stringStatePlancia.append("NoStream").append("#"); + } + } else { + stringStatePlancia.delete(0, stringStatePlancia.length()); + stringStatePlancia.append("False").append("#"); + } + return stringStatePlancia; + } + + public static StringBuilder getGPSJetson(){ + stringJetson = new StringBuilder(); + BaseProduct product = SampleApplication.getProductInstance(); + if (product != null && product.isConnected()) { + getDroneLocation(product); + stringJetson.delete(0, stringJetson.length()); + //stringJetson.append("hotpoint_coordinates").append("-"); if we want to automatic search + stringJetson.append("hotpoint_jetson").append("-"); + stringJetson.append(latitude).append("-"); + stringJetson.append(longitude).append("-"); + stringJetson.append(altitude).append("-"); + stringJetson.append("5").append("\n\r"); + + } else { + stringJetson.append("no_connected").append("\n\r"); + } + return stringJetson; + } + + private static void getSpeed() { + String velX = Float.toString(flightController.getState().getVelocityX()); + String velY = Float.toString(flightController.getState().getVelocityY()); + String velZ = Float.toString(flightController.getState().getVelocityZ()); + speed = velX + ":" + velY + ":" + velZ; +// Log.i(TAG, "speed " + speed); + } + + // gets real time flight data of the aircraft with the given serial number + private static void getRealTimeData() { + //TODO:se non va togliere commento + BaseProduct product = SampleApplication.getProductInstance(); + if (product instanceof Aircraft) { flightController = ((Aircraft) product).getFlightController(); } + flightController.getSerialNumber(new CommonCallbacks.CompletionCallbackWith() { + @Override + public void onSuccess(String s) { + serialNumber = s; + } + + @Override + public void onFailure(DJIError djiError) { + + } + }); + final ArrayList serialNumbers = new ArrayList<>(1); + serialNumbers.add(serialNumber); + + try { + DJISDKManager.getInstance().getFlightHubManager().getAircraftRealTimeFlightData(serialNumbers, new CommonCallbacks.CompletionCallbackWith>() { + @Override + public void onSuccess(List realTimeFlightData) { + for (RealTimeFlightData s : realTimeFlightData) { + speed = String.valueOf(s.getSpeed()); + Log.i(TAG, "speed " + s.getSpeed()); + } + } + @Override + public void onFailure(DJIError error) { + Log.i(TAG, "getAircraftRealTimeFlightData failed: " + error.getDescription()); + } + }); + } catch (Exception e) { + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/dji/ux/beta/sample/utils/ToastUtils.java b/app/src/main/java/com/dji/ux/beta/sample/utils/ToastUtils.java new file mode 100644 index 0000000..3e01493 --- /dev/null +++ b/app/src/main/java/com/dji/ux/beta/sample/utils/ToastUtils.java @@ -0,0 +1,64 @@ +package com.dji.ux.beta.sample.utils; + +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.util.Pair; +import android.widget.TextView; +import android.widget.Toast; + +import com.dji.ux.beta.sample.SampleApplication; + +public class ToastUtils { + private static final int MESSAGE_UPDATE = 1; + private static final int MESSAGE_TOAST = 2; + private static Handler mUIHandler = new Handler(Looper.getMainLooper()) { + @Override + public void handleMessage(Message msg) { + //Get the message string + switch ((msg.what)) { + case MESSAGE_UPDATE: + showMessage((Pair) msg.obj); + break; + case MESSAGE_TOAST: + showToast((String) msg.obj); + break; + default: + super.handleMessage(msg); + } + } + }; + + private static void showMessage(Pair msg) { + if (msg != null) { + if (msg.first == null) { + Toast.makeText(SampleApplication.getInstance(), "tv is null", Toast.LENGTH_SHORT).show(); + } else { + msg.first.setText(msg.second); + } + } + } + + public static void showToast(String msg) { + Toast.makeText(SampleApplication.getInstance(), msg, Toast.LENGTH_SHORT).show(); + } + + + public static void setResultToToast(final String string) { + Message msg = new Message(); + msg.what = MESSAGE_TOAST; + msg.obj = string; + mUIHandler.sendMessage(msg); + } + + public static void setResultToText(final TextView tv, final String s) { + Message msg = new Message(); + msg.what = MESSAGE_UPDATE; + msg.obj = new Pair<>(tv, s); + mUIHandler.sendMessage(msg); + } + +// public static void seToToast(Context mContext, String s) { +// Toast.makeText(mContext, s, Toast.LENGTH_SHORT).show(); +// } +} diff --git a/app/src/main/res/drawable-hdpi/baseline_cast_black_18.png b/app/src/main/res/drawable-hdpi/baseline_cast_black_18.png new file mode 100644 index 0000000..1effc6a Binary files /dev/null and b/app/src/main/res/drawable-hdpi/baseline_cast_black_18.png differ diff --git a/app/src/main/res/drawable-hdpi/baseline_cast_black_20.png b/app/src/main/res/drawable-hdpi/baseline_cast_black_20.png new file mode 100644 index 0000000..ca005ad Binary files /dev/null and b/app/src/main/res/drawable-hdpi/baseline_cast_black_20.png differ diff --git a/app/src/main/res/drawable-hdpi/baseline_cast_black_24.png b/app/src/main/res/drawable-hdpi/baseline_cast_black_24.png new file mode 100644 index 0000000..0f7fa8d Binary files /dev/null and b/app/src/main/res/drawable-hdpi/baseline_cast_black_24.png differ diff --git a/app/src/main/res/drawable-hdpi/baseline_cast_black_36.png b/app/src/main/res/drawable-hdpi/baseline_cast_black_36.png new file mode 100644 index 0000000..8942574 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/baseline_cast_black_36.png differ diff --git a/app/src/main/res/drawable-hdpi/baseline_cast_black_48.png b/app/src/main/res/drawable-hdpi/baseline_cast_black_48.png new file mode 100644 index 0000000..428c76c Binary files /dev/null and b/app/src/main/res/drawable-hdpi/baseline_cast_black_48.png differ diff --git a/app/src/main/res/drawable-hdpi/baseline_cast_connected_black_18.png b/app/src/main/res/drawable-hdpi/baseline_cast_connected_black_18.png new file mode 100644 index 0000000..06e5b44 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/baseline_cast_connected_black_18.png differ diff --git a/app/src/main/res/drawable-hdpi/baseline_cast_connected_black_20.png b/app/src/main/res/drawable-hdpi/baseline_cast_connected_black_20.png new file mode 100644 index 0000000..fd1e160 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/baseline_cast_connected_black_20.png differ diff --git a/app/src/main/res/drawable-hdpi/baseline_cast_connected_black_24.png b/app/src/main/res/drawable-hdpi/baseline_cast_connected_black_24.png new file mode 100644 index 0000000..5e67b69 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/baseline_cast_connected_black_24.png differ diff --git a/app/src/main/res/drawable-hdpi/baseline_cast_connected_black_36.png b/app/src/main/res/drawable-hdpi/baseline_cast_connected_black_36.png new file mode 100644 index 0000000..e8cd1ad Binary files /dev/null and b/app/src/main/res/drawable-hdpi/baseline_cast_connected_black_36.png differ diff --git a/app/src/main/res/drawable-hdpi/baseline_cast_connected_black_48.png b/app/src/main/res/drawable-hdpi/baseline_cast_connected_black_48.png new file mode 100644 index 0000000..f05771d Binary files /dev/null and b/app/src/main/res/drawable-hdpi/baseline_cast_connected_black_48.png differ diff --git a/app/src/main/res/drawable-hdpi/baseline_cast_connected_white_18.png b/app/src/main/res/drawable-hdpi/baseline_cast_connected_white_18.png new file mode 100644 index 0000000..2633361 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/baseline_cast_connected_white_18.png differ diff --git a/app/src/main/res/drawable-hdpi/baseline_cast_connected_white_20.png b/app/src/main/res/drawable-hdpi/baseline_cast_connected_white_20.png new file mode 100644 index 0000000..8a10ad0 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/baseline_cast_connected_white_20.png differ diff --git a/app/src/main/res/drawable-hdpi/baseline_cast_connected_white_24.png b/app/src/main/res/drawable-hdpi/baseline_cast_connected_white_24.png new file mode 100644 index 0000000..7082ab1 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/baseline_cast_connected_white_24.png differ diff --git a/app/src/main/res/drawable-hdpi/baseline_cast_connected_white_36.png b/app/src/main/res/drawable-hdpi/baseline_cast_connected_white_36.png new file mode 100644 index 0000000..753f3c3 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/baseline_cast_connected_white_36.png differ diff --git a/app/src/main/res/drawable-hdpi/baseline_cast_connected_white_48.png b/app/src/main/res/drawable-hdpi/baseline_cast_connected_white_48.png new file mode 100644 index 0000000..c88c239 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/baseline_cast_connected_white_48.png differ diff --git a/app/src/main/res/drawable-hdpi/baseline_cast_white_18.png b/app/src/main/res/drawable-hdpi/baseline_cast_white_18.png new file mode 100644 index 0000000..3f68554 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/baseline_cast_white_18.png differ diff --git a/app/src/main/res/drawable-hdpi/baseline_cast_white_20.png b/app/src/main/res/drawable-hdpi/baseline_cast_white_20.png new file mode 100644 index 0000000..dda90d3 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/baseline_cast_white_20.png differ diff --git a/app/src/main/res/drawable-hdpi/baseline_cast_white_24.png b/app/src/main/res/drawable-hdpi/baseline_cast_white_24.png new file mode 100644 index 0000000..00ae26b Binary files /dev/null and b/app/src/main/res/drawable-hdpi/baseline_cast_white_24.png differ diff --git a/app/src/main/res/drawable-hdpi/baseline_cast_white_36.png b/app/src/main/res/drawable-hdpi/baseline_cast_white_36.png new file mode 100644 index 0000000..460ce5d Binary files /dev/null and b/app/src/main/res/drawable-hdpi/baseline_cast_white_36.png differ diff --git a/app/src/main/res/drawable-hdpi/baseline_cast_white_48.png b/app/src/main/res/drawable-hdpi/baseline_cast_white_48.png new file mode 100644 index 0000000..3a98193 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/baseline_cast_white_48.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_action_drone_map.png b/app/src/main/res/drawable-hdpi/ic_action_drone_map.png new file mode 100644 index 0000000..4b416c7 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_action_drone_map.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_noun_drrowning.png b/app/src/main/res/drawable-hdpi/ic_noun_drrowning.png new file mode 100644 index 0000000..0d2f7f4 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_noun_drrowning.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_start_search.png b/app/src/main/res/drawable-hdpi/ic_start_search.png new file mode 100644 index 0000000..d46ab0d Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_start_search.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_stat.png b/app/src/main/res/drawable-hdpi/ic_stat.png new file mode 100644 index 0000000..bcaa75d Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_stat.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_stop_search.png b/app/src/main/res/drawable-hdpi/ic_stop_search.png new file mode 100644 index 0000000..1237d57 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_stop_search.png differ diff --git a/app/src/main/res/drawable-hdpi/icon_slider.png b/app/src/main/res/drawable-hdpi/icon_slider.png new file mode 100644 index 0000000..5614a08 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/icon_slider.png differ diff --git a/app/src/main/res/drawable-hdpi/main_activity.png b/app/src/main/res/drawable-hdpi/main_activity.png new file mode 100644 index 0000000..f0ebc45 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/main_activity.png differ diff --git a/app/src/main/res/drawable-hdpi/noun_drone_161305.png b/app/src/main/res/drawable-hdpi/noun_drone_161305.png new file mode 100644 index 0000000..0244f34 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/noun_drone_161305.png differ diff --git a/app/src/main/res/drawable-hdpi/outline_pin_drop_black_18.png b/app/src/main/res/drawable-hdpi/outline_pin_drop_black_18.png new file mode 100644 index 0000000..da0f440 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/outline_pin_drop_black_18.png differ diff --git a/app/src/main/res/drawable-hdpi/outline_pin_drop_black_20.png b/app/src/main/res/drawable-hdpi/outline_pin_drop_black_20.png new file mode 100644 index 0000000..dc5b362 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/outline_pin_drop_black_20.png differ diff --git a/app/src/main/res/drawable-hdpi/outline_pin_drop_black_24.png b/app/src/main/res/drawable-hdpi/outline_pin_drop_black_24.png new file mode 100644 index 0000000..9ab4217 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/outline_pin_drop_black_24.png differ diff --git a/app/src/main/res/drawable-hdpi/outline_pin_drop_black_36.png b/app/src/main/res/drawable-hdpi/outline_pin_drop_black_36.png new file mode 100644 index 0000000..9797965 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/outline_pin_drop_black_36.png differ diff --git a/app/src/main/res/drawable-hdpi/outline_pin_drop_black_48.png b/app/src/main/res/drawable-hdpi/outline_pin_drop_black_48.png new file mode 100644 index 0000000..1aa601f Binary files /dev/null and b/app/src/main/res/drawable-hdpi/outline_pin_drop_black_48.png differ diff --git a/app/src/main/res/drawable-ldpi/fpv_gradient_bottom.xml b/app/src/main/res/drawable-ldpi/fpv_gradient_bottom.xml new file mode 100644 index 0000000..5eb89b2 --- /dev/null +++ b/app/src/main/res/drawable-ldpi/fpv_gradient_bottom.xml @@ -0,0 +1,33 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable-ldpi/fpv_gradient_left.xml b/app/src/main/res/drawable-ldpi/fpv_gradient_left.xml new file mode 100644 index 0000000..9b68930 --- /dev/null +++ b/app/src/main/res/drawable-ldpi/fpv_gradient_left.xml @@ -0,0 +1,32 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable-ldpi/fpv_gradient_right.xml b/app/src/main/res/drawable-ldpi/fpv_gradient_right.xml new file mode 100644 index 0000000..f20220f --- /dev/null +++ b/app/src/main/res/drawable-ldpi/fpv_gradient_right.xml @@ -0,0 +1,32 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable-ldpi/icon_slider.png b/app/src/main/res/drawable-ldpi/icon_slider.png new file mode 100644 index 0000000..5e01198 Binary files /dev/null and b/app/src/main/res/drawable-ldpi/icon_slider.png differ diff --git a/app/src/main/res/drawable-ldpi/main_activity.png b/app/src/main/res/drawable-ldpi/main_activity.png new file mode 100644 index 0000000..d1eaf26 Binary files /dev/null and b/app/src/main/res/drawable-ldpi/main_activity.png differ diff --git a/app/src/main/res/drawable-ldpi/noun_drone_161305.png b/app/src/main/res/drawable-ldpi/noun_drone_161305.png new file mode 100644 index 0000000..9a16a3f Binary files /dev/null and b/app/src/main/res/drawable-ldpi/noun_drone_161305.png differ diff --git a/app/src/main/res/drawable-mdpi/baseline_cast_black_18.png b/app/src/main/res/drawable-mdpi/baseline_cast_black_18.png new file mode 100644 index 0000000..18b192a Binary files /dev/null and b/app/src/main/res/drawable-mdpi/baseline_cast_black_18.png differ diff --git a/app/src/main/res/drawable-mdpi/baseline_cast_black_20.png b/app/src/main/res/drawable-mdpi/baseline_cast_black_20.png new file mode 100644 index 0000000..4de1aa5 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/baseline_cast_black_20.png differ diff --git a/app/src/main/res/drawable-mdpi/baseline_cast_black_24.png b/app/src/main/res/drawable-mdpi/baseline_cast_black_24.png new file mode 100644 index 0000000..05f6384 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/baseline_cast_black_24.png differ diff --git a/app/src/main/res/drawable-mdpi/baseline_cast_black_36.png b/app/src/main/res/drawable-mdpi/baseline_cast_black_36.png new file mode 100644 index 0000000..0f7fa8d Binary files /dev/null and b/app/src/main/res/drawable-mdpi/baseline_cast_black_36.png differ diff --git a/app/src/main/res/drawable-mdpi/baseline_cast_black_48.png b/app/src/main/res/drawable-mdpi/baseline_cast_black_48.png new file mode 100644 index 0000000..f77c4aa Binary files /dev/null and b/app/src/main/res/drawable-mdpi/baseline_cast_black_48.png differ diff --git a/app/src/main/res/drawable-mdpi/baseline_cast_connected_black_18.png b/app/src/main/res/drawable-mdpi/baseline_cast_connected_black_18.png new file mode 100644 index 0000000..6c5c8fe Binary files /dev/null and b/app/src/main/res/drawable-mdpi/baseline_cast_connected_black_18.png differ diff --git a/app/src/main/res/drawable-mdpi/baseline_cast_connected_black_20.png b/app/src/main/res/drawable-mdpi/baseline_cast_connected_black_20.png new file mode 100644 index 0000000..ebacc28 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/baseline_cast_connected_black_20.png differ diff --git a/app/src/main/res/drawable-mdpi/baseline_cast_connected_black_24.png b/app/src/main/res/drawable-mdpi/baseline_cast_connected_black_24.png new file mode 100644 index 0000000..22f0720 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/baseline_cast_connected_black_24.png differ diff --git a/app/src/main/res/drawable-mdpi/baseline_cast_connected_black_36.png b/app/src/main/res/drawable-mdpi/baseline_cast_connected_black_36.png new file mode 100644 index 0000000..5e67b69 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/baseline_cast_connected_black_36.png differ diff --git a/app/src/main/res/drawable-mdpi/baseline_cast_connected_black_48.png b/app/src/main/res/drawable-mdpi/baseline_cast_connected_black_48.png new file mode 100644 index 0000000..e3198f1 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/baseline_cast_connected_black_48.png differ diff --git a/app/src/main/res/drawable-mdpi/baseline_cast_connected_white_18.png b/app/src/main/res/drawable-mdpi/baseline_cast_connected_white_18.png new file mode 100644 index 0000000..b23785c Binary files /dev/null and b/app/src/main/res/drawable-mdpi/baseline_cast_connected_white_18.png differ diff --git a/app/src/main/res/drawable-mdpi/baseline_cast_connected_white_20.png b/app/src/main/res/drawable-mdpi/baseline_cast_connected_white_20.png new file mode 100644 index 0000000..23d9ec4 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/baseline_cast_connected_white_20.png differ diff --git a/app/src/main/res/drawable-mdpi/baseline_cast_connected_white_24.png b/app/src/main/res/drawable-mdpi/baseline_cast_connected_white_24.png new file mode 100644 index 0000000..7e4ca9b Binary files /dev/null and b/app/src/main/res/drawable-mdpi/baseline_cast_connected_white_24.png differ diff --git a/app/src/main/res/drawable-mdpi/baseline_cast_connected_white_36.png b/app/src/main/res/drawable-mdpi/baseline_cast_connected_white_36.png new file mode 100644 index 0000000..7082ab1 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/baseline_cast_connected_white_36.png differ diff --git a/app/src/main/res/drawable-mdpi/baseline_cast_connected_white_48.png b/app/src/main/res/drawable-mdpi/baseline_cast_connected_white_48.png new file mode 100644 index 0000000..28d24df Binary files /dev/null and b/app/src/main/res/drawable-mdpi/baseline_cast_connected_white_48.png differ diff --git a/app/src/main/res/drawable-mdpi/baseline_cast_white_18.png b/app/src/main/res/drawable-mdpi/baseline_cast_white_18.png new file mode 100644 index 0000000..c06a209 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/baseline_cast_white_18.png differ diff --git a/app/src/main/res/drawable-mdpi/baseline_cast_white_20.png b/app/src/main/res/drawable-mdpi/baseline_cast_white_20.png new file mode 100644 index 0000000..d486647 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/baseline_cast_white_20.png differ diff --git a/app/src/main/res/drawable-mdpi/baseline_cast_white_24.png b/app/src/main/res/drawable-mdpi/baseline_cast_white_24.png new file mode 100644 index 0000000..96b75bc Binary files /dev/null and b/app/src/main/res/drawable-mdpi/baseline_cast_white_24.png differ diff --git a/app/src/main/res/drawable-mdpi/baseline_cast_white_36.png b/app/src/main/res/drawable-mdpi/baseline_cast_white_36.png new file mode 100644 index 0000000..00ae26b Binary files /dev/null and b/app/src/main/res/drawable-mdpi/baseline_cast_white_36.png differ diff --git a/app/src/main/res/drawable-mdpi/baseline_cast_white_48.png b/app/src/main/res/drawable-mdpi/baseline_cast_white_48.png new file mode 100644 index 0000000..44b65f7 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/baseline_cast_white_48.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_action_drone_map.png b/app/src/main/res/drawable-mdpi/ic_action_drone_map.png new file mode 100644 index 0000000..a2282f9 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_action_drone_map.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_noun_drrowning.png b/app/src/main/res/drawable-mdpi/ic_noun_drrowning.png new file mode 100644 index 0000000..224e36a Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_noun_drrowning.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_start_search.png b/app/src/main/res/drawable-mdpi/ic_start_search.png new file mode 100644 index 0000000..08d2970 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_start_search.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_stat.png b/app/src/main/res/drawable-mdpi/ic_stat.png new file mode 100644 index 0000000..adbd4ee Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_stat.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_stop_search.png b/app/src/main/res/drawable-mdpi/ic_stop_search.png new file mode 100644 index 0000000..33789b5 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_stop_search.png differ diff --git a/app/src/main/res/drawable-mdpi/icon_slider.png b/app/src/main/res/drawable-mdpi/icon_slider.png new file mode 100644 index 0000000..f73df49 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/icon_slider.png differ diff --git a/app/src/main/res/drawable-mdpi/main_activity.png b/app/src/main/res/drawable-mdpi/main_activity.png new file mode 100644 index 0000000..fdaeb0b Binary files /dev/null and b/app/src/main/res/drawable-mdpi/main_activity.png differ diff --git a/app/src/main/res/drawable-mdpi/noun_drone_161305.png b/app/src/main/res/drawable-mdpi/noun_drone_161305.png new file mode 100644 index 0000000..48c6cfe Binary files /dev/null and b/app/src/main/res/drawable-mdpi/noun_drone_161305.png differ diff --git a/app/src/main/res/drawable-mdpi/outline_pin_drop_black_18.png b/app/src/main/res/drawable-mdpi/outline_pin_drop_black_18.png new file mode 100644 index 0000000..7f14fb9 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/outline_pin_drop_black_18.png differ diff --git a/app/src/main/res/drawable-mdpi/outline_pin_drop_black_20.png b/app/src/main/res/drawable-mdpi/outline_pin_drop_black_20.png new file mode 100644 index 0000000..e42fabd Binary files /dev/null and b/app/src/main/res/drawable-mdpi/outline_pin_drop_black_20.png differ diff --git a/app/src/main/res/drawable-mdpi/outline_pin_drop_black_24.png b/app/src/main/res/drawable-mdpi/outline_pin_drop_black_24.png new file mode 100644 index 0000000..53b786d Binary files /dev/null and b/app/src/main/res/drawable-mdpi/outline_pin_drop_black_24.png differ diff --git a/app/src/main/res/drawable-mdpi/outline_pin_drop_black_36.png b/app/src/main/res/drawable-mdpi/outline_pin_drop_black_36.png new file mode 100644 index 0000000..9ab4217 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/outline_pin_drop_black_36.png differ diff --git a/app/src/main/res/drawable-mdpi/outline_pin_drop_black_48.png b/app/src/main/res/drawable-mdpi/outline_pin_drop_black_48.png new file mode 100644 index 0000000..df5c920 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/outline_pin_drop_black_48.png differ diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable-xhdpi/baseline_cast_black_18.png b/app/src/main/res/drawable-xhdpi/baseline_cast_black_18.png new file mode 100644 index 0000000..0f7fa8d Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/baseline_cast_black_18.png differ diff --git a/app/src/main/res/drawable-xhdpi/baseline_cast_black_20.png b/app/src/main/res/drawable-xhdpi/baseline_cast_black_20.png new file mode 100644 index 0000000..894b7e2 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/baseline_cast_black_20.png differ diff --git a/app/src/main/res/drawable-xhdpi/baseline_cast_black_24.png b/app/src/main/res/drawable-xhdpi/baseline_cast_black_24.png new file mode 100644 index 0000000..f77c4aa Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/baseline_cast_black_24.png differ diff --git a/app/src/main/res/drawable-xhdpi/baseline_cast_black_36.png b/app/src/main/res/drawable-xhdpi/baseline_cast_black_36.png new file mode 100644 index 0000000..428c76c Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/baseline_cast_black_36.png differ diff --git a/app/src/main/res/drawable-xhdpi/baseline_cast_black_48.png b/app/src/main/res/drawable-xhdpi/baseline_cast_black_48.png new file mode 100644 index 0000000..f7b7d67 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/baseline_cast_black_48.png differ diff --git a/app/src/main/res/drawable-xhdpi/baseline_cast_connected_black_18.png b/app/src/main/res/drawable-xhdpi/baseline_cast_connected_black_18.png new file mode 100644 index 0000000..5e67b69 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/baseline_cast_connected_black_18.png differ diff --git a/app/src/main/res/drawable-xhdpi/baseline_cast_connected_black_20.png b/app/src/main/res/drawable-xhdpi/baseline_cast_connected_black_20.png new file mode 100644 index 0000000..c432dc6 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/baseline_cast_connected_black_20.png differ diff --git a/app/src/main/res/drawable-xhdpi/baseline_cast_connected_black_24.png b/app/src/main/res/drawable-xhdpi/baseline_cast_connected_black_24.png new file mode 100644 index 0000000..e3198f1 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/baseline_cast_connected_black_24.png differ diff --git a/app/src/main/res/drawable-xhdpi/baseline_cast_connected_black_36.png b/app/src/main/res/drawable-xhdpi/baseline_cast_connected_black_36.png new file mode 100644 index 0000000..f05771d Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/baseline_cast_connected_black_36.png differ diff --git a/app/src/main/res/drawable-xhdpi/baseline_cast_connected_black_48.png b/app/src/main/res/drawable-xhdpi/baseline_cast_connected_black_48.png new file mode 100644 index 0000000..12dc070 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/baseline_cast_connected_black_48.png differ diff --git a/app/src/main/res/drawable-xhdpi/baseline_cast_connected_white_18.png b/app/src/main/res/drawable-xhdpi/baseline_cast_connected_white_18.png new file mode 100644 index 0000000..7082ab1 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/baseline_cast_connected_white_18.png differ diff --git a/app/src/main/res/drawable-xhdpi/baseline_cast_connected_white_20.png b/app/src/main/res/drawable-xhdpi/baseline_cast_connected_white_20.png new file mode 100644 index 0000000..342b169 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/baseline_cast_connected_white_20.png differ diff --git a/app/src/main/res/drawable-xhdpi/baseline_cast_connected_white_24.png b/app/src/main/res/drawable-xhdpi/baseline_cast_connected_white_24.png new file mode 100644 index 0000000..28d24df Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/baseline_cast_connected_white_24.png differ diff --git a/app/src/main/res/drawable-xhdpi/baseline_cast_connected_white_36.png b/app/src/main/res/drawable-xhdpi/baseline_cast_connected_white_36.png new file mode 100644 index 0000000..c88c239 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/baseline_cast_connected_white_36.png differ diff --git a/app/src/main/res/drawable-xhdpi/baseline_cast_connected_white_48.png b/app/src/main/res/drawable-xhdpi/baseline_cast_connected_white_48.png new file mode 100644 index 0000000..8458178 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/baseline_cast_connected_white_48.png differ diff --git a/app/src/main/res/drawable-xhdpi/baseline_cast_white_18.png b/app/src/main/res/drawable-xhdpi/baseline_cast_white_18.png new file mode 100644 index 0000000..00ae26b Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/baseline_cast_white_18.png differ diff --git a/app/src/main/res/drawable-xhdpi/baseline_cast_white_20.png b/app/src/main/res/drawable-xhdpi/baseline_cast_white_20.png new file mode 100644 index 0000000..d3174f9 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/baseline_cast_white_20.png differ diff --git a/app/src/main/res/drawable-xhdpi/baseline_cast_white_24.png b/app/src/main/res/drawable-xhdpi/baseline_cast_white_24.png new file mode 100644 index 0000000..44b65f7 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/baseline_cast_white_24.png differ diff --git a/app/src/main/res/drawable-xhdpi/baseline_cast_white_36.png b/app/src/main/res/drawable-xhdpi/baseline_cast_white_36.png new file mode 100644 index 0000000..3a98193 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/baseline_cast_white_36.png differ diff --git a/app/src/main/res/drawable-xhdpi/baseline_cast_white_48.png b/app/src/main/res/drawable-xhdpi/baseline_cast_white_48.png new file mode 100644 index 0000000..c7ed343 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/baseline_cast_white_48.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_action_drone_map.png b/app/src/main/res/drawable-xhdpi/ic_action_drone_map.png new file mode 100644 index 0000000..9f2cd5a Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_action_drone_map.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_noun_drrowning.png b/app/src/main/res/drawable-xhdpi/ic_noun_drrowning.png new file mode 100644 index 0000000..d35e804 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_noun_drrowning.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_start_search.png b/app/src/main/res/drawable-xhdpi/ic_start_search.png new file mode 100644 index 0000000..36ed225 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_start_search.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_stat.png b/app/src/main/res/drawable-xhdpi/ic_stat.png new file mode 100644 index 0000000..36bc708 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_stat.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_stop_search.png b/app/src/main/res/drawable-xhdpi/ic_stop_search.png new file mode 100644 index 0000000..1b6f9cb Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_stop_search.png differ diff --git a/app/src/main/res/drawable-xhdpi/icon_slider.png b/app/src/main/res/drawable-xhdpi/icon_slider.png new file mode 100644 index 0000000..674696e Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/icon_slider.png differ diff --git a/app/src/main/res/drawable-xhdpi/main_activity.png b/app/src/main/res/drawable-xhdpi/main_activity.png new file mode 100644 index 0000000..0605f73 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/main_activity.png differ diff --git a/app/src/main/res/drawable-xhdpi/noun_drone_161305.png b/app/src/main/res/drawable-xhdpi/noun_drone_161305.png new file mode 100644 index 0000000..864183f Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/noun_drone_161305.png differ diff --git a/app/src/main/res/drawable-xhdpi/outline_pin_drop_black_18.png b/app/src/main/res/drawable-xhdpi/outline_pin_drop_black_18.png new file mode 100644 index 0000000..9ab4217 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/outline_pin_drop_black_18.png differ diff --git a/app/src/main/res/drawable-xhdpi/outline_pin_drop_black_20.png b/app/src/main/res/drawable-xhdpi/outline_pin_drop_black_20.png new file mode 100644 index 0000000..e235d25 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/outline_pin_drop_black_20.png differ diff --git a/app/src/main/res/drawable-xhdpi/outline_pin_drop_black_24.png b/app/src/main/res/drawable-xhdpi/outline_pin_drop_black_24.png new file mode 100644 index 0000000..df5c920 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/outline_pin_drop_black_24.png differ diff --git a/app/src/main/res/drawable-xhdpi/outline_pin_drop_black_36.png b/app/src/main/res/drawable-xhdpi/outline_pin_drop_black_36.png new file mode 100644 index 0000000..1aa601f Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/outline_pin_drop_black_36.png differ diff --git a/app/src/main/res/drawable-xhdpi/outline_pin_drop_black_48.png b/app/src/main/res/drawable-xhdpi/outline_pin_drop_black_48.png new file mode 100644 index 0000000..b0cb553 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/outline_pin_drop_black_48.png differ diff --git a/app/src/main/res/drawable-xxhdpi/baseline_cast_black_18.png b/app/src/main/res/drawable-xxhdpi/baseline_cast_black_18.png new file mode 100644 index 0000000..8942574 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/baseline_cast_black_18.png differ diff --git a/app/src/main/res/drawable-xxhdpi/baseline_cast_black_20.png b/app/src/main/res/drawable-xxhdpi/baseline_cast_black_20.png new file mode 100644 index 0000000..f45b118 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/baseline_cast_black_20.png differ diff --git a/app/src/main/res/drawable-xxhdpi/baseline_cast_black_24.png b/app/src/main/res/drawable-xxhdpi/baseline_cast_black_24.png new file mode 100644 index 0000000..428c76c Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/baseline_cast_black_24.png differ diff --git a/app/src/main/res/drawable-xxhdpi/baseline_cast_black_36.png b/app/src/main/res/drawable-xxhdpi/baseline_cast_black_36.png new file mode 100644 index 0000000..8ba3da8 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/baseline_cast_black_36.png differ diff --git a/app/src/main/res/drawable-xxhdpi/baseline_cast_black_48.png b/app/src/main/res/drawable-xxhdpi/baseline_cast_black_48.png new file mode 100644 index 0000000..7fd1448 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/baseline_cast_black_48.png differ diff --git a/app/src/main/res/drawable-xxhdpi/baseline_cast_connected_black_18.png b/app/src/main/res/drawable-xxhdpi/baseline_cast_connected_black_18.png new file mode 100644 index 0000000..e8cd1ad Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/baseline_cast_connected_black_18.png differ diff --git a/app/src/main/res/drawable-xxhdpi/baseline_cast_connected_black_20.png b/app/src/main/res/drawable-xxhdpi/baseline_cast_connected_black_20.png new file mode 100644 index 0000000..005c236 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/baseline_cast_connected_black_20.png differ diff --git a/app/src/main/res/drawable-xxhdpi/baseline_cast_connected_black_24.png b/app/src/main/res/drawable-xxhdpi/baseline_cast_connected_black_24.png new file mode 100644 index 0000000..f05771d Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/baseline_cast_connected_black_24.png differ diff --git a/app/src/main/res/drawable-xxhdpi/baseline_cast_connected_black_36.png b/app/src/main/res/drawable-xxhdpi/baseline_cast_connected_black_36.png new file mode 100644 index 0000000..9a17e78 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/baseline_cast_connected_black_36.png differ diff --git a/app/src/main/res/drawable-xxhdpi/baseline_cast_connected_black_48.png b/app/src/main/res/drawable-xxhdpi/baseline_cast_connected_black_48.png new file mode 100644 index 0000000..bf6c245 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/baseline_cast_connected_black_48.png differ diff --git a/app/src/main/res/drawable-xxhdpi/baseline_cast_connected_white_18.png b/app/src/main/res/drawable-xxhdpi/baseline_cast_connected_white_18.png new file mode 100644 index 0000000..753f3c3 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/baseline_cast_connected_white_18.png differ diff --git a/app/src/main/res/drawable-xxhdpi/baseline_cast_connected_white_20.png b/app/src/main/res/drawable-xxhdpi/baseline_cast_connected_white_20.png new file mode 100644 index 0000000..73c12de Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/baseline_cast_connected_white_20.png differ diff --git a/app/src/main/res/drawable-xxhdpi/baseline_cast_connected_white_24.png b/app/src/main/res/drawable-xxhdpi/baseline_cast_connected_white_24.png new file mode 100644 index 0000000..c88c239 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/baseline_cast_connected_white_24.png differ diff --git a/app/src/main/res/drawable-xxhdpi/baseline_cast_connected_white_36.png b/app/src/main/res/drawable-xxhdpi/baseline_cast_connected_white_36.png new file mode 100644 index 0000000..5a1e0fe Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/baseline_cast_connected_white_36.png differ diff --git a/app/src/main/res/drawable-xxhdpi/baseline_cast_connected_white_48.png b/app/src/main/res/drawable-xxhdpi/baseline_cast_connected_white_48.png new file mode 100644 index 0000000..b7886d3 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/baseline_cast_connected_white_48.png differ diff --git a/app/src/main/res/drawable-xxhdpi/baseline_cast_white_18.png b/app/src/main/res/drawable-xxhdpi/baseline_cast_white_18.png new file mode 100644 index 0000000..460ce5d Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/baseline_cast_white_18.png differ diff --git a/app/src/main/res/drawable-xxhdpi/baseline_cast_white_20.png b/app/src/main/res/drawable-xxhdpi/baseline_cast_white_20.png new file mode 100644 index 0000000..54f249b Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/baseline_cast_white_20.png differ diff --git a/app/src/main/res/drawable-xxhdpi/baseline_cast_white_24.png b/app/src/main/res/drawable-xxhdpi/baseline_cast_white_24.png new file mode 100644 index 0000000..3a98193 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/baseline_cast_white_24.png differ diff --git a/app/src/main/res/drawable-xxhdpi/baseline_cast_white_36.png b/app/src/main/res/drawable-xxhdpi/baseline_cast_white_36.png new file mode 100644 index 0000000..e26d40c Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/baseline_cast_white_36.png differ diff --git a/app/src/main/res/drawable-xxhdpi/baseline_cast_white_48.png b/app/src/main/res/drawable-xxhdpi/baseline_cast_white_48.png new file mode 100644 index 0000000..803dc1f Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/baseline_cast_white_48.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_drone_map.png b/app/src/main/res/drawable-xxhdpi/ic_action_drone_map.png new file mode 100644 index 0000000..07345f7 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_action_drone_map.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_noun_drrowning.png b/app/src/main/res/drawable-xxhdpi/ic_noun_drrowning.png new file mode 100644 index 0000000..da59d6a Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_noun_drrowning.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_start_search.png b/app/src/main/res/drawable-xxhdpi/ic_start_search.png new file mode 100644 index 0000000..dcb13ac Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_start_search.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_stat.png b/app/src/main/res/drawable-xxhdpi/ic_stat.png new file mode 100644 index 0000000..9211d63 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_stat.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_stop_search.png b/app/src/main/res/drawable-xxhdpi/ic_stop_search.png new file mode 100644 index 0000000..44ebf90 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_stop_search.png differ diff --git a/app/src/main/res/drawable-xxhdpi/icon_slider.png b/app/src/main/res/drawable-xxhdpi/icon_slider.png new file mode 100644 index 0000000..a0f9eaf Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/icon_slider.png differ diff --git a/app/src/main/res/drawable-xxhdpi/main_activity.png b/app/src/main/res/drawable-xxhdpi/main_activity.png new file mode 100644 index 0000000..950f3ee Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/main_activity.png differ diff --git a/app/src/main/res/drawable-xxhdpi/noun_drone_161305.png b/app/src/main/res/drawable-xxhdpi/noun_drone_161305.png new file mode 100644 index 0000000..874c533 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/noun_drone_161305.png differ diff --git a/app/src/main/res/drawable-xxhdpi/outline_pin_drop_black_18.png b/app/src/main/res/drawable-xxhdpi/outline_pin_drop_black_18.png new file mode 100644 index 0000000..9797965 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/outline_pin_drop_black_18.png differ diff --git a/app/src/main/res/drawable-xxhdpi/outline_pin_drop_black_20.png b/app/src/main/res/drawable-xxhdpi/outline_pin_drop_black_20.png new file mode 100644 index 0000000..70c5323 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/outline_pin_drop_black_20.png differ diff --git a/app/src/main/res/drawable-xxhdpi/outline_pin_drop_black_24.png b/app/src/main/res/drawable-xxhdpi/outline_pin_drop_black_24.png new file mode 100644 index 0000000..1aa601f Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/outline_pin_drop_black_24.png differ diff --git a/app/src/main/res/drawable-xxhdpi/outline_pin_drop_black_36.png b/app/src/main/res/drawable-xxhdpi/outline_pin_drop_black_36.png new file mode 100644 index 0000000..b4bc479 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/outline_pin_drop_black_36.png differ diff --git a/app/src/main/res/drawable-xxhdpi/outline_pin_drop_black_48.png b/app/src/main/res/drawable-xxhdpi/outline_pin_drop_black_48.png new file mode 100644 index 0000000..4f821cc Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/outline_pin_drop_black_48.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/baseline_cast_black_18.png b/app/src/main/res/drawable-xxxhdpi/baseline_cast_black_18.png new file mode 100644 index 0000000..428c76c Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/baseline_cast_black_18.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/baseline_cast_black_20.png b/app/src/main/res/drawable-xxxhdpi/baseline_cast_black_20.png new file mode 100644 index 0000000..b5040dc Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/baseline_cast_black_20.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/baseline_cast_black_24.png b/app/src/main/res/drawable-xxxhdpi/baseline_cast_black_24.png new file mode 100644 index 0000000..f7b7d67 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/baseline_cast_black_24.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/baseline_cast_black_36.png b/app/src/main/res/drawable-xxxhdpi/baseline_cast_black_36.png new file mode 100644 index 0000000..7fd1448 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/baseline_cast_black_36.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/baseline_cast_black_48.png b/app/src/main/res/drawable-xxxhdpi/baseline_cast_black_48.png new file mode 100644 index 0000000..189faa2 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/baseline_cast_black_48.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/baseline_cast_connected_black_18.png b/app/src/main/res/drawable-xxxhdpi/baseline_cast_connected_black_18.png new file mode 100644 index 0000000..f05771d Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/baseline_cast_connected_black_18.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/baseline_cast_connected_black_20.png b/app/src/main/res/drawable-xxxhdpi/baseline_cast_connected_black_20.png new file mode 100644 index 0000000..17bc1b2 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/baseline_cast_connected_black_20.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/baseline_cast_connected_black_24.png b/app/src/main/res/drawable-xxxhdpi/baseline_cast_connected_black_24.png new file mode 100644 index 0000000..12dc070 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/baseline_cast_connected_black_24.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/baseline_cast_connected_black_36.png b/app/src/main/res/drawable-xxxhdpi/baseline_cast_connected_black_36.png new file mode 100644 index 0000000..bf6c245 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/baseline_cast_connected_black_36.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/baseline_cast_connected_black_48.png b/app/src/main/res/drawable-xxxhdpi/baseline_cast_connected_black_48.png new file mode 100644 index 0000000..8f5511a Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/baseline_cast_connected_black_48.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/baseline_cast_connected_white_18.png b/app/src/main/res/drawable-xxxhdpi/baseline_cast_connected_white_18.png new file mode 100644 index 0000000..c88c239 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/baseline_cast_connected_white_18.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/baseline_cast_connected_white_20.png b/app/src/main/res/drawable-xxxhdpi/baseline_cast_connected_white_20.png new file mode 100644 index 0000000..49ea1f6 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/baseline_cast_connected_white_20.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/baseline_cast_connected_white_24.png b/app/src/main/res/drawable-xxxhdpi/baseline_cast_connected_white_24.png new file mode 100644 index 0000000..8458178 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/baseline_cast_connected_white_24.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/baseline_cast_connected_white_36.png b/app/src/main/res/drawable-xxxhdpi/baseline_cast_connected_white_36.png new file mode 100644 index 0000000..b7886d3 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/baseline_cast_connected_white_36.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/baseline_cast_connected_white_48.png b/app/src/main/res/drawable-xxxhdpi/baseline_cast_connected_white_48.png new file mode 100644 index 0000000..02a51cf Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/baseline_cast_connected_white_48.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/baseline_cast_white_18.png b/app/src/main/res/drawable-xxxhdpi/baseline_cast_white_18.png new file mode 100644 index 0000000..3a98193 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/baseline_cast_white_18.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/baseline_cast_white_20.png b/app/src/main/res/drawable-xxxhdpi/baseline_cast_white_20.png new file mode 100644 index 0000000..dea954b Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/baseline_cast_white_20.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/baseline_cast_white_24.png b/app/src/main/res/drawable-xxxhdpi/baseline_cast_white_24.png new file mode 100644 index 0000000..c7ed343 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/baseline_cast_white_24.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/baseline_cast_white_36.png b/app/src/main/res/drawable-xxxhdpi/baseline_cast_white_36.png new file mode 100644 index 0000000..803dc1f Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/baseline_cast_white_36.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/baseline_cast_white_48.png b/app/src/main/res/drawable-xxxhdpi/baseline_cast_white_48.png new file mode 100644 index 0000000..2a23e9e Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/baseline_cast_white_48.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_action_drone_map.png b/app/src/main/res/drawable-xxxhdpi/ic_action_drone_map.png new file mode 100644 index 0000000..9f5b487 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_action_drone_map.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_noun_drrowning.png b/app/src/main/res/drawable-xxxhdpi/ic_noun_drrowning.png new file mode 100644 index 0000000..ebce103 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_noun_drrowning.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_start_search.png b/app/src/main/res/drawable-xxxhdpi/ic_start_search.png new file mode 100644 index 0000000..9e05f4c Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_start_search.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_stat.png b/app/src/main/res/drawable-xxxhdpi/ic_stat.png new file mode 100644 index 0000000..1f52449 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_stat.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_stop_search.png b/app/src/main/res/drawable-xxxhdpi/ic_stop_search.png new file mode 100644 index 0000000..1d4090b Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_stop_search.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/icon_slider.png b/app/src/main/res/drawable-xxxhdpi/icon_slider.png new file mode 100644 index 0000000..2b340d3 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/icon_slider.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/main_activity.png b/app/src/main/res/drawable-xxxhdpi/main_activity.png new file mode 100644 index 0000000..9a1018a Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/main_activity.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/noun_drone_161305.png b/app/src/main/res/drawable-xxxhdpi/noun_drone_161305.png new file mode 100644 index 0000000..753aa54 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/noun_drone_161305.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/outline_pin_drop_black_18.png b/app/src/main/res/drawable-xxxhdpi/outline_pin_drop_black_18.png new file mode 100644 index 0000000..1aa601f Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/outline_pin_drop_black_18.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/outline_pin_drop_black_20.png b/app/src/main/res/drawable-xxxhdpi/outline_pin_drop_black_20.png new file mode 100644 index 0000000..74725d4 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/outline_pin_drop_black_20.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/outline_pin_drop_black_24.png b/app/src/main/res/drawable-xxxhdpi/outline_pin_drop_black_24.png new file mode 100644 index 0000000..b0cb553 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/outline_pin_drop_black_24.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/outline_pin_drop_black_36.png b/app/src/main/res/drawable-xxxhdpi/outline_pin_drop_black_36.png new file mode 100644 index 0000000..4f821cc Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/outline_pin_drop_black_36.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/outline_pin_drop_black_48.png b/app/src/main/res/drawable-xxxhdpi/outline_pin_drop_black_48.png new file mode 100644 index 0000000..243fc41 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/outline_pin_drop_black_48.png differ diff --git a/app/src/main/res/drawable/baseline_cast_24.xml b/app/src/main/res/drawable/baseline_cast_24.xml new file mode 100644 index 0000000..dceb6e7 --- /dev/null +++ b/app/src/main/res/drawable/baseline_cast_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/baseline_cast_connected_24.xml b/app/src/main/res/drawable/baseline_cast_connected_24.xml new file mode 100644 index 0000000..0e44ec6 --- /dev/null +++ b/app/src/main/res/drawable/baseline_cast_connected_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/rounded_button.xml b/app/src/main/res/drawable/rounded_button.xml new file mode 100644 index 0000000..651f244 --- /dev/null +++ b/app/src/main/res/drawable/rounded_button.xml @@ -0,0 +1,10 @@ + + + + + + diff --git a/app/src/main/res/font-v26/font.xml b/app/src/main/res/font-v26/font.xml new file mode 100644 index 0000000..3807808 --- /dev/null +++ b/app/src/main/res/font-v26/font.xml @@ -0,0 +1,13 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/font/font.xml b/app/src/main/res/font/font.xml new file mode 100644 index 0000000..3807808 --- /dev/null +++ b/app/src/main/res/font/font.xml @@ -0,0 +1,13 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/font/montserrat_black.ttf b/app/src/main/res/font/montserrat_black.ttf new file mode 100644 index 0000000..437b115 Binary files /dev/null and b/app/src/main/res/font/montserrat_black.ttf differ diff --git a/app/src/main/res/font/montserrat_bold.ttf b/app/src/main/res/font/montserrat_bold.ttf new file mode 100644 index 0000000..221819b Binary files /dev/null and b/app/src/main/res/font/montserrat_bold.ttf differ diff --git a/app/src/main/res/font/montserrat_medium.ttf b/app/src/main/res/font/montserrat_medium.ttf new file mode 100644 index 0000000..6e079f6 Binary files /dev/null and b/app/src/main/res/font/montserrat_medium.ttf differ diff --git a/app/src/main/res/font/montserrat_regular.ttf b/app/src/main/res/font/montserrat_regular.ttf new file mode 100644 index 0000000..8d443d5 Binary files /dev/null and b/app/src/main/res/font/montserrat_regular.ttf differ diff --git a/app/src/main/res/font/montserrat_semibold.ttf b/app/src/main/res/font/montserrat_semibold.ttf new file mode 100644 index 0000000..f8a43f2 Binary files /dev/null and b/app/src/main/res/font/montserrat_semibold.ttf differ diff --git a/app/src/main/res/font/montserrat_thin.ttf b/app/src/main/res/font/montserrat_thin.ttf new file mode 100644 index 0000000..b985875 Binary files /dev/null and b/app/src/main/res/font/montserrat_thin.ttf differ diff --git a/app/src/main/res/layout/activity_camera.xml b/app/src/main/res/layout/activity_camera.xml new file mode 100644 index 0000000..61390ed --- /dev/null +++ b/app/src/main/res/layout/activity_camera.xml @@ -0,0 +1,239 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_camera_backup.xml b/app/src/main/res/layout/activity_camera_backup.xml new file mode 100644 index 0000000..3dd83d0 --- /dev/null +++ b/app/src/main/res/layout/activity_camera_backup.xml @@ -0,0 +1,205 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..5808533 --- /dev/null +++ b/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,130 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000..eca70cf --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 0000000..eca70cf --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..a571e60 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 0000000..61da551 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..c41dd28 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 0000000..db5080a Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..6dba46d Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 0000000..da31a87 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..15ac681 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 0000000..b216f2d Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..f25a419 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 0000000..e96783c Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/values-1440x2560/dimens.xml b/app/src/main/res/values-1440x2560/dimens.xml new file mode 100644 index 0000000..43ca700 --- /dev/null +++ b/app/src/main/res/values-1440x2560/dimens.xml @@ -0,0 +1,32 @@ + + + + + 4sp + 4sp + 2sp + 3sp + 3sp + 6sp + \ No newline at end of file diff --git a/app/src/main/res/values-sw600dp/dimens.xml b/app/src/main/res/values-sw600dp/dimens.xml new file mode 100644 index 0000000..ba2e3c6 --- /dev/null +++ b/app/src/main/res/values-sw600dp/dimens.xml @@ -0,0 +1,33 @@ + + + + + 18sp + 18sp + 18sp + 16sp + 15sp + 15sp + 20sp + \ No newline at end of file diff --git a/app/src/main/res/values-v26/styles.xml b/app/src/main/res/values-v26/styles.xml new file mode 100644 index 0000000..bb4cd8d --- /dev/null +++ b/app/src/main/res/values-v26/styles.xml @@ -0,0 +1,10 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml new file mode 100644 index 0000000..ba08461 --- /dev/null +++ b/app/src/main/res/values/attrs.xml @@ -0,0 +1,756 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..2243e0a --- /dev/null +++ b/app/src/main/res/values/colors.xml @@ -0,0 +1,18 @@ + + + #EE6C4D + #001F54 + #FF4081 + #FFFFFF + #242d34 + #000000 + #FFFFFF + #444444 + #303030 + #B3FFFFFF + #80000000 + #00000000 + #727272 + #F7F9FA + #D8DBE2 + diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml new file mode 100644 index 0000000..107e227 --- /dev/null +++ b/app/src/main/res/values/dimens.xml @@ -0,0 +1,103 @@ + + + + + 2dp + 4dp + 8dp + 4dp + + 4dp + 4dp + + + 20sp + 18sp + 16sp + 14sp + 12sp + 10sp + 8sp + 6sp + + 350dp + + 42dp + 21dp + 42dp + + 45dp + 10dp + 5dp + 15sp + 14sp + + 1dp + 2dp + + 4sp + 3sp + + 10sp + + 100dp + + 32dp + 16dp + + 13sp + + 10dp + + 5dp + 55dp + 5dp + 10dp + 60dp + 6sp + 6sp + 4sp + 5sp + 5sp + 8sp + 55dp + + 5dp + 2dp + 6dp + + 15dp + 5dp + 100dp + 150dp + 12dp + + 3dp + 35dp + 35dp + 6dp + 0.1 + + + \ No newline at end of file diff --git a/app/src/main/res/values/integers.xml b/app/src/main/res/values/integers.xml new file mode 100755 index 0000000..b5c6ad6 --- /dev/null +++ b/app/src/main/res/values/integers.xml @@ -0,0 +1,28 @@ + + + + + 200 + 333 + diff --git a/app/src/main/res/values/keystrings.xml b/app/src/main/res/values/keystrings.xml new file mode 100644 index 0000000..1f2e51c --- /dev/null +++ b/app/src/main/res/values/keystrings.xml @@ -0,0 +1,5 @@ + + + 8898c38d0f08ab74e1ae6c4a + AIzaSyAsUQFx5VR0WutTZZXyljmevNKzTYHX16M + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..31c64ac --- /dev/null +++ b/app/src/main/res/values/strings.xml @@ -0,0 +1,46 @@ + + MOB + No product connected + No server connected + Server disconnected + SDK Version: %1$s + Product Name: %1$s + Remote Controller Only + Unregistered + Registered + Logs + Showcase + Widget List + Default Layout + Map + Camera View + + + my_pref + speed_wp + interdiction area + latitude + longitude + altitude + + latitudeUpdate + longitudeUpdate + + latitude follow + longitude follow + altitude follow + + latitude ship + longitude ship + altitude ship + + latitude hotpoint + longitude hotpoint + altitude hotpoint + radius hotpoint + search + disable console + warning + live + stop + \ No newline at end of file diff --git a/app/src/main/res/values/strings_dimension_ratios.xml b/app/src/main/res/values/strings_dimension_ratios.xml new file mode 100644 index 0000000..7981e62 --- /dev/null +++ b/app/src/main/res/values/strings_dimension_ratios.xml @@ -0,0 +1,43 @@ + + + + + W,1:1 + W,25:11 + W,19:11 + W,19:11 + W,37:32 + W,21:11 + W,1:1 + W,81:16 + W,476:91 + W,52:11 + W,16:9 + W,17:8 + W,4:1 + W,400:3 + W,1:1 + W,23:16 + W,1:1 + \ No newline at end of file diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..064954c --- /dev/null +++ b/app/src/main/res/values/styles.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/xml/accessory_filter.xml b/app/src/main/res/xml/accessory_filter.xml new file mode 100644 index 0000000..afd33ce --- /dev/null +++ b/app/src/main/res/xml/accessory_filter.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/test/java/com/dji/ux/beta/sample/ExampleUnitTest.java b/app/src/test/java/com/dji/ux/beta/sample/ExampleUnitTest.java new file mode 100644 index 0000000..90cd839 --- /dev/null +++ b/app/src/test/java/com/dji/ux/beta/sample/ExampleUnitTest.java @@ -0,0 +1,17 @@ +package com.dji.ux.beta.sample; + +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * Example local unit test, which will execute on the development machine (host). + * + * @see Testing documentation + */ +public class ExampleUnitTest { + @Test + public void addition_isCorrect() { + assertEquals(4, 2 + 2); + } +} \ No newline at end of file diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..bceab52 --- /dev/null +++ b/build.gradle @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2018-2020 DJI + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +// Top-level build file where you can add configuration options common to all sub-projects/modules. + +buildscript { + ext.kotlin_version = '1.3.50' + + repositories { + google() + jcenter() + mavenCentral() + } + dependencies { + classpath 'com.android.tools.build:gradle:7.1.2' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath "com.google.android.libraries.mapsplatform.secrets-gradle-plugin:secrets-gradle-plugin:2.0.0" + + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + } +} + + +allprojects { + repositories { + google() + jcenter() + mavenCentral() + maven { url 'https://jitpack.io' } + maven { url "https://oss.jfrog.org/libs-snapshot" } + } +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..6826e61 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,17 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app"s APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..f6b961f Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..d499c60 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Fri Oct 15 11:15:21 CEST 2021 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-all.zip diff --git a/gradlew b/gradlew new file mode 100755 index 0000000..cccdd3d --- /dev/null +++ b/gradlew @@ -0,0 +1,172 @@ +#!/usr/bin/env sh + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..f955316 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..b053106 --- /dev/null +++ b/settings.gradle @@ -0,0 +1,2 @@ +rootProject.name = "MOB" +include ':app'