├── .gitignore ├── DJISimulatorDemo ├── app │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src │ │ ├── androidTest │ │ └── java │ │ │ └── com │ │ │ └── dji │ │ │ └── simulatorDemo │ │ │ └── ApplicationTest.java │ │ └── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ └── com │ │ │ └── dji │ │ │ └── simulatorDemo │ │ │ ├── DJISimulatorApplication.java │ │ │ ├── MApplication.java │ │ │ ├── MainActivity.java │ │ │ ├── OnScreenJoystick.java │ │ │ └── OnScreenJoystickListener.java │ │ └── res │ │ ├── drawable │ │ ├── back_button_disable.png │ │ ├── back_button_normal.png │ │ ├── back_button_press.png │ │ ├── round_btn.xml │ │ ├── round_btn_disable.xml │ │ ├── round_btn_normal.xml │ │ ├── round_btn_pressed.xml │ │ └── selector_back_button.xml │ │ ├── layout │ │ └── activity_main.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ ├── joystick.png │ │ └── joystick_bg.png │ │ ├── raw │ │ └── keep.xml │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── import-summary.txt └── settings.gradle ├── LICENSE.txt └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # files for the dex VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # generated files 12 | bin/ 13 | gen/ 14 | obj/ 15 | DJI-SDK-LIB_3.1/build/ 16 | build/ 17 | 18 | # generated file 19 | lint.xml 20 | 21 | # Local configuration file (sdk/lib path, etc) 22 | local.properties 23 | project.properties 24 | 25 | # Eclipse project files 26 | .classpath 27 | .settings/ 28 | 29 | # Proguard folder generated by Eclipse 30 | proguard/ 31 | 32 | # Intellij project files 33 | *.iml 34 | *.ipr 35 | *.iws 36 | .idea/ 37 | 38 | # Mac files 39 | .DS_Store 40 | 41 | # Gradle generated files 42 | .gradle/ 43 | 44 | # Signing files 45 | .signing/ 46 | 47 | # User-specific configurations 48 | .idea/libraries/ 49 | .idea/workspace.xml 50 | .idea/tasks.xml 51 | .idea/.name 52 | .idea/compiler.xml 53 | .idea/copyright/profiles_settings.xml 54 | .idea/encodings.xml 55 | .idea/misc.xml 56 | .idea/modules.xml 57 | .idea/scopes/scope_settings.xml 58 | .idea/vcs.xml 59 | *.iml 60 | 61 | # OS-specific files 62 | .DS_Store 63 | .DS_Store? 64 | ._* 65 | .Spotlight-V100 66 | .Trashes 67 | ehthumbs.db 68 | Thumbs.db 69 | 70 | # Import summary file 71 | import-summary.txt 72 | -------------------------------------------------------------------------------- /DJISimulatorDemo/app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /DJISimulatorDemo/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | repositories { 4 | mavenLocal() 5 | } 6 | 7 | android { 8 | compileSdkVersion 30 9 | buildToolsVersion '30.0.2' 10 | useLibrary 'org.apache.http.legacy' 11 | 12 | defaultConfig { 13 | minSdkVersion 19 14 | targetSdkVersion 30 15 | multiDexEnabled true 16 | ndk { 17 | abiFilters 'armeabi-v7a', 'arm64-v8a' 18 | } 19 | } 20 | 21 | buildTypes { 22 | release { 23 | minifyEnabled false 24 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 25 | } 26 | debug { 27 | shrinkResources true 28 | minifyEnabled true 29 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 30 | } 31 | } 32 | 33 | dexOptions { 34 | javaMaxHeapSize "4g" 35 | } 36 | 37 | packagingOptions { 38 | doNotStrip "*/*/libdjivideo.so" 39 | doNotStrip "*/*/libSDKRelativeJNI.so" 40 | doNotStrip "*/*/libFlyForbid.so" 41 | doNotStrip "*/*/libduml_vision_bokeh.so" 42 | doNotStrip "*/*/libyuv2.so" 43 | doNotStrip "*/*/libGroudStation.so" 44 | doNotStrip "*/*/libFRCorkscrew.so" 45 | doNotStrip "*/*/libUpgradeVerify.so" 46 | doNotStrip "*/*/libFR.so" 47 | doNotStrip "*/*/libDJIFlySafeCore.so" 48 | doNotStrip "*/*/libdjifs_jni.so" 49 | doNotStrip "*/*/libsfjni.so" 50 | doNotStrip "*/*/libDJICommonJNI.so" 51 | doNotStrip "*/*/libDJICSDKCommon.so" 52 | doNotStrip "*/*/libDJIUpgradeCore.so" 53 | doNotStrip "*/*/libDJIUpgradeJNI.so" 54 | exclude 'META-INF/rxjava.properties' 55 | } 56 | 57 | compileOptions { 58 | sourceCompatibility JavaVersion.VERSION_1_8 59 | targetCompatibility JavaVersion.VERSION_1_8 60 | } 61 | } 62 | 63 | 64 | dependencies { 65 | implementation 'androidx.multidex:multidex:2.0.1' 66 | implementation 'com.squareup:otto:1.3.8' 67 | implementation('com.dji:dji-sdk:4.15', { 68 | /** 69 | * Uncomment the "library-anti-distortion" if your app does not need Anti Distortion for Mavic 2 Pro and Mavic 2 Zoom. 70 | * Uncomment the "fly-safe-database" if you need database for release, or we will download it when DJISDKManager.getInstance().registerApp 71 | * is called. 72 | * Both will greatly reducing the size of the APK. 73 | */ 74 | exclude module: 'library-anti-distortion' 75 | //exclude module: 'fly-safe-database' 76 | }) 77 | compileOnly 'com.dji:dji-sdk-provided:4.15' 78 | 79 | implementation 'androidx.appcompat:appcompat:1.2.0' 80 | implementation 'androidx.core:core:1.3.2' 81 | implementation 'androidx.constraintlayout:constraintlayout:2.0.4' 82 | implementation 'androidx.recyclerview:recyclerview:1.1.0' 83 | implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0' 84 | implementation 'androidx.annotation:annotation:1.2.0' 85 | } 86 | -------------------------------------------------------------------------------- /DJISimulatorDemo/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | -keepattributes Exceptions,InnerClasses,*Annotation*,Signature,EnclosingMethod 2 | 3 | -dontoptimize 4 | -dontpreverify 5 | -dontwarn okio.** 6 | -dontwarn org.bouncycastle.** 7 | -dontwarn dji.** 8 | -dontwarn com.dji.** 9 | -dontwarn sun.** 10 | -dontwarn java.** 11 | -dontwarn com.amap.api.** 12 | -dontwarn com.here.** 13 | -dontwarn com.mapbox.** 14 | -dontwarn okhttp3.** 15 | -dontwarn retrofit2.** 16 | 17 | -keepclassmembers enum * { 18 | public static ; 19 | } 20 | 21 | -keepnames class * implements java.io.Serializable 22 | -keepclassmembers class * implements java.io.Serializable { 23 | static final long serialVersionUID; 24 | private static final java.io.ObjectStreamField[] serialPersistentFields; 25 | !static !transient ; 26 | private void writeObject(java.io.ObjectOutputStream); 27 | private void readObject(java.io.ObjectInputStream); 28 | java.lang.Object writeReplace(); 29 | java.lang.Object readResolve(); 30 | } 31 | -keep class * extends android.os.Parcelable { 32 | public static final android.os.Parcelable$Creator *; 33 | } 34 | 35 | -keep,allowshrinking class * extends dji.publics.DJIUI.** { 36 | public ; 37 | } 38 | 39 | -keep class net.sqlcipher.** { *; } 40 | 41 | -keep class net.sqlcipher.database.* { *; } 42 | 43 | -keep class dji.** { *; } 44 | 45 | -keep class com.dji.** { *; } 46 | 47 | -keep class com.google.** { *; } 48 | 49 | -keep class org.bouncycastle.** { *; } 50 | 51 | -keep,allowshrinking class org.** { *; } 52 | 53 | -keep class com.squareup.wire.** { *; } 54 | 55 | -keep class sun.misc.Unsafe { *; } 56 | 57 | -keep class com.secneo.** { *; } 58 | 59 | -keep class org.greenrobot.eventbus.**{*;} 60 | 61 | -keep class it.sauronsoftware.ftp4j.**{*;} 62 | 63 | -keepclasseswithmembers,allowshrinking class * { 64 | native ; 65 | } 66 | 67 | -keep class * implements com.google.gson.TypeAdapterFactory 68 | -keep class * implements com.google.gson.JsonSerializer 69 | -keep class * implements com.google.gson.JsonDeserializer 70 | 71 | -keep class androidx.appcompat.widget.SearchView { *; } 72 | 73 | -keepclassmembers class * extends android.app.Service 74 | -keepclassmembers public class * extends android.view.View { 75 | void set*(***); 76 | *** get*(); 77 | } 78 | -keepclassmembers class * extends android.app.Activity { 79 | public void *(android.view.View); 80 | } 81 | -keep class androidx.** { *; } 82 | -keep class android.media.** { *; } 83 | -keep class okio.** { *; } 84 | -keep class com.lmax.disruptor.** { *; } 85 | -keep class com.qx.wz.dj.rtcm.* { *; } 86 | 87 | -dontwarn com.mapbox.services.android.location.LostLocationEngine 88 | -dontwarn com.mapbox.services.android.location.MockLocationEngine 89 | -keepclassmembers class * implements android.arch.lifecycle.LifecycleObserver { 90 | (...); 91 | } 92 | # ViewModel's empty constructor is considered to be unused by proguard 93 | -keepclassmembers class * extends android.arch.lifecycle.ViewModel { 94 | (...); 95 | } 96 | # keep Lifecycle State and Event enums values 97 | -keepclassmembers class android.arch.lifecycle.Lifecycle$State { *; } 98 | -keepclassmembers class android.arch.lifecycle.Lifecycle$Event { *; } 99 | # keep methods annotated with @OnLifecycleEvent even if they seem to be unused 100 | # (Mostly for LiveData.LifecycleBoundObserver.onStateChange(), but who knows) 101 | -keepclassmembers class * { 102 | @android.arch.lifecycle.OnLifecycleEvent *; 103 | } 104 | 105 | -keepclassmembers class * implements android.arch.lifecycle.LifecycleObserver { 106 | (...); 107 | } 108 | 109 | -keep class * implements android.arch.lifecycle.LifecycleObserver { 110 | (...); 111 | } 112 | -keepclassmembers class android.arch.** { *; } 113 | -keep class android.arch.** { *; } 114 | -dontwarn android.arch.** 115 | 116 | -keep class org.apache.commons.** {*;} 117 | 118 | 119 | #<------------ utmiss config start------------> 120 | -keep class dji.sdk.utmiss.** { *; } 121 | -keep class utmisslib.** { *; } 122 | #<------------ utmiss config end------------> -------------------------------------------------------------------------------- /DJISimulatorDemo/app/src/androidTest/java/com/dji/simulatorDemo/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.dji.simulatorDemo; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /DJISimulatorDemo/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 23 | 26 | 27 | 28 | 29 | 36 | 37 | 42 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /DJISimulatorDemo/app/src/main/java/com/dji/simulatorDemo/DJISimulatorApplication.java: -------------------------------------------------------------------------------- 1 | package com.dji.simulatorDemo; 2 | 3 | import android.app.Application; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.os.Build; 7 | import android.os.Handler; 8 | import android.os.Looper; 9 | import android.util.Log; 10 | import android.widget.Toast; 11 | 12 | import androidx.core.content.ContextCompat; 13 | 14 | import dji.common.error.DJIError; 15 | import dji.common.error.DJISDKError; 16 | import dji.sdk.base.BaseComponent; 17 | import dji.sdk.base.BaseProduct; 18 | import dji.sdk.products.Aircraft; 19 | import dji.sdk.sdkmanager.DJISDKInitEvent; 20 | import dji.sdk.sdkmanager.DJISDKManager; 21 | 22 | public class DJISimulatorApplication extends Application { 23 | 24 | private static final String TAG = DJISimulatorApplication.class.getName(); 25 | public static final String FLAG_CONNECTION_CHANGE = "com_dji_simulatorDemo_connection_change"; 26 | private static BaseProduct mProduct; 27 | private Handler mHandler; 28 | private DJISDKManager.SDKManagerCallback mDJISDKManagerCallback; 29 | 30 | private Application instance; 31 | 32 | public void setContext(Application application) { 33 | instance = application; 34 | } 35 | 36 | @Override 37 | public Context getApplicationContext() { 38 | return instance; 39 | } 40 | 41 | public DJISimulatorApplication() { 42 | 43 | } 44 | 45 | /** 46 | * Gets instance of the specific product connected after the 47 | * API KEY is successfully validated. Please make sure the 48 | * API_KEY has been added in the Manifest 49 | */ 50 | public static synchronized BaseProduct getProductInstance() { 51 | if (null == mProduct) { 52 | mProduct = DJISDKManager.getInstance().getProduct(); 53 | } 54 | return mProduct; 55 | } 56 | 57 | public static boolean isAircraftConnected() { 58 | return getProductInstance() != null && getProductInstance() instanceof Aircraft; 59 | } 60 | 61 | public static synchronized Aircraft getAircraftInstance() { 62 | if (!isAircraftConnected()) return null; 63 | return (Aircraft) getProductInstance(); 64 | } 65 | 66 | @Override 67 | public void onCreate() { 68 | super.onCreate(); 69 | mHandler = new Handler(Looper.getMainLooper()); 70 | 71 | 72 | /** 73 | * When starting SDK services, an instance of interface DJISDKManager.DJISDKManagerCallback will be used to listen to 74 | * the SDK Registration result and the product changing. 75 | */ 76 | mDJISDKManagerCallback = new DJISDKManager.SDKManagerCallback() { 77 | 78 | //Listens to the SDK registration result 79 | @Override 80 | public void onRegister(DJIError error) { 81 | 82 | if(error == DJISDKError.REGISTRATION_SUCCESS) { 83 | 84 | Handler handler = new Handler(Looper.getMainLooper()); 85 | handler.post(new Runnable() { 86 | @Override 87 | public void run() { 88 | Toast.makeText(getApplicationContext(), "Register Success", Toast.LENGTH_LONG).show(); 89 | } 90 | }); 91 | 92 | DJISDKManager.getInstance().startConnectionToProduct(); 93 | 94 | } else { 95 | 96 | Handler handler = new Handler(Looper.getMainLooper()); 97 | handler.post(new Runnable() { 98 | 99 | @Override 100 | public void run() { 101 | Toast.makeText(getApplicationContext(), "Register sdk fails, check network is available", Toast.LENGTH_LONG).show(); 102 | } 103 | }); 104 | 105 | } 106 | Log.e("TAG", error.toString()); 107 | } 108 | 109 | @Override 110 | public void onProductDisconnect() { 111 | Log.d("TAG", "onProductDisconnect"); 112 | notifyStatusChange(); 113 | } 114 | @Override 115 | public void onProductConnect(BaseProduct baseProduct) { 116 | Log.d("TAG", String.format("onProductConnect newProduct:%s", baseProduct)); 117 | notifyStatusChange(); 118 | 119 | } 120 | 121 | @Override 122 | public void onProductChanged(BaseProduct baseProduct) { 123 | 124 | } 125 | 126 | @Override 127 | public void onComponentChange(BaseProduct.ComponentKey componentKey, BaseComponent oldComponent, 128 | BaseComponent newComponent) { 129 | if (newComponent != null) { 130 | newComponent.setComponentListener(new BaseComponent.ComponentListener() { 131 | 132 | @Override 133 | public void onConnectivityChange(boolean isConnected) { 134 | Log.d("TAG", "onComponentConnectivityChanged: " + isConnected); 135 | notifyStatusChange(); 136 | } 137 | }); 138 | } 139 | 140 | Log.d("TAG", 141 | String.format("onComponentChange key:%s, oldComponent:%s, newComponent:%s", 142 | componentKey, 143 | oldComponent, 144 | newComponent)); 145 | 146 | } 147 | @Override 148 | public void onInitProcess(DJISDKInitEvent djisdkInitEvent, int i) { 149 | 150 | } 151 | 152 | @Override 153 | public void onDatabaseDownloadProgress(long l, long l1) { 154 | 155 | } 156 | 157 | 158 | }; 159 | //Check the permissions before registering the application for android system 6.0 above. 160 | int permissionCheck = ContextCompat.checkSelfPermission(getApplicationContext(), android.Manifest.permission.WRITE_EXTERNAL_STORAGE); 161 | int permissionCheck2 = ContextCompat.checkSelfPermission(getApplicationContext(), android.Manifest.permission.READ_PHONE_STATE); 162 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M || (permissionCheck == 0 && permissionCheck2 == 0)) { 163 | //This is used to start SDK services and initiate SDK. 164 | DJISDKManager.getInstance().registerApp(getApplicationContext(), mDJISDKManagerCallback); 165 | Toast.makeText(getApplicationContext(), "registering, pls wait...", Toast.LENGTH_LONG).show(); 166 | 167 | } else { 168 | Toast.makeText(getApplicationContext(), "Please check if the permission is granted.", Toast.LENGTH_LONG).show(); 169 | } 170 | } 171 | 172 | private void notifyStatusChange() { 173 | mHandler.removeCallbacks(updateRunnable); 174 | mHandler.postDelayed(updateRunnable, 500); 175 | } 176 | 177 | private Runnable updateRunnable = new Runnable() { 178 | 179 | @Override 180 | public void run() { 181 | Intent intent = new Intent(FLAG_CONNECTION_CHANGE); 182 | getApplicationContext().sendBroadcast(intent); 183 | } 184 | }; 185 | 186 | } 187 | -------------------------------------------------------------------------------- /DJISimulatorDemo/app/src/main/java/com/dji/simulatorDemo/MApplication.java: -------------------------------------------------------------------------------- 1 | package com.dji.simulatorDemo; 2 | 3 | import android.app.Application; 4 | import android.content.Context; 5 | 6 | import com.secneo.sdk.Helper; 7 | 8 | 9 | public class MApplication extends Application { 10 | 11 | private DJISimulatorApplication simulatorApplication; 12 | @Override 13 | protected void attachBaseContext(Context paramContext) { 14 | super.attachBaseContext(paramContext); 15 | Helper.install(MApplication.this); 16 | if (simulatorApplication == null) { 17 | simulatorApplication = new DJISimulatorApplication(); 18 | simulatorApplication.setContext(this); 19 | } 20 | } 21 | 22 | @Override 23 | public void onCreate() { 24 | super.onCreate(); 25 | simulatorApplication.onCreate(); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /DJISimulatorDemo/app/src/main/java/com/dji/simulatorDemo/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.dji.simulatorDemo; 2 | 3 | import android.Manifest; 4 | import android.app.Activity; 5 | import android.content.BroadcastReceiver; 6 | import android.content.Context; 7 | import android.content.Intent; 8 | import android.content.IntentFilter; 9 | import android.content.pm.PackageManager; 10 | import android.os.AsyncTask; 11 | import android.os.Handler; 12 | import android.os.Looper; 13 | import android.os.Build; 14 | import android.os.Bundle; 15 | import android.util.Log; 16 | import android.view.View; 17 | import android.widget.Button; 18 | import android.widget.CompoundButton; 19 | import android.widget.TextView; 20 | import android.widget.Toast; 21 | import android.widget.ToggleButton; 22 | 23 | import androidx.annotation.NonNull; 24 | import androidx.core.app.ActivityCompat; 25 | import androidx.core.content.ContextCompat; 26 | 27 | import java.util.ArrayList; 28 | import java.util.List; 29 | import java.util.Timer; 30 | import java.util.TimerTask; 31 | import java.util.concurrent.atomic.AtomicBoolean; 32 | 33 | import dji.common.error.DJISDKError; 34 | import dji.common.flightcontroller.simulator.InitializationData; 35 | import dji.common.flightcontroller.simulator.SimulatorState; 36 | import dji.common.flightcontroller.virtualstick.FlightControlData; 37 | import dji.common.flightcontroller.virtualstick.FlightCoordinateSystem; 38 | import dji.common.flightcontroller.virtualstick.RollPitchControlMode; 39 | import dji.common.flightcontroller.virtualstick.VerticalControlMode; 40 | import dji.common.flightcontroller.virtualstick.YawControlMode; 41 | import dji.common.model.LocationCoordinate2D; 42 | import dji.common.useraccount.UserAccountState; 43 | import dji.common.util.CommonCallbacks; 44 | import dji.log.DJILog; 45 | import dji.sdk.base.BaseComponent; 46 | import dji.sdk.base.BaseProduct; 47 | import dji.sdk.flightcontroller.FlightController; 48 | import dji.sdk.products.Aircraft; 49 | import dji.common.error.DJIError; 50 | import dji.sdk.sdkmanager.DJISDKInitEvent; 51 | import dji.sdk.sdkmanager.DJISDKManager; 52 | import dji.sdk.useraccount.UserAccountManager; 53 | 54 | public class MainActivity extends Activity implements View.OnClickListener { 55 | 56 | private static final String TAG = MainActivity.class.getName(); 57 | 58 | private static final String[] REQUIRED_PERMISSION_LIST = new String[]{ 59 | Manifest.permission.BLUETOOTH, 60 | Manifest.permission.BLUETOOTH_ADMIN, 61 | Manifest.permission.VIBRATE, 62 | Manifest.permission.INTERNET, 63 | Manifest.permission.ACCESS_WIFI_STATE, 64 | Manifest.permission.ACCESS_COARSE_LOCATION, 65 | Manifest.permission.ACCESS_NETWORK_STATE, 66 | Manifest.permission.ACCESS_FINE_LOCATION, 67 | Manifest.permission.CHANGE_WIFI_STATE, 68 | Manifest.permission.RECORD_AUDIO, 69 | Manifest.permission.WRITE_EXTERNAL_STORAGE, 70 | Manifest.permission.READ_EXTERNAL_STORAGE, 71 | Manifest.permission.READ_PHONE_STATE, 72 | }; 73 | private List missingPermission = new ArrayList<>(); 74 | private AtomicBoolean isRegistrationInProgress = new AtomicBoolean(false); 75 | private static final int REQUEST_PERMISSION_CODE = 12345; 76 | 77 | private FlightController mFlightController; 78 | protected TextView mConnectStatusTextView; 79 | private Button mBtnEnableVirtualStick; 80 | private Button mBtnDisableVirtualStick; 81 | private ToggleButton mBtnSimulator; 82 | private Button mBtnTakeOff; 83 | private Button mBtnLand; 84 | 85 | private TextView mTextView; 86 | 87 | private OnScreenJoystick mScreenJoystickRight; 88 | private OnScreenJoystick mScreenJoystickLeft; 89 | 90 | private Timer mSendVirtualStickDataTimer; 91 | private SendVirtualStickDataTask mSendVirtualStickDataTask; 92 | 93 | private float mPitch; 94 | private float mRoll; 95 | private float mYaw; 96 | private float mThrottle; 97 | 98 | @Override 99 | protected void onCreate(Bundle savedInstanceState) { 100 | super.onCreate(savedInstanceState); 101 | checkAndRequestPermissions(); 102 | setContentView(R.layout.activity_main); 103 | 104 | initUI(); 105 | 106 | // Register the broadcast receiver for receiving the device connection's changes. 107 | IntentFilter filter = new IntentFilter(); 108 | filter.addAction(DJISimulatorApplication.FLAG_CONNECTION_CHANGE); 109 | registerReceiver(mReceiver, filter); 110 | } 111 | 112 | /** 113 | * Checks if there is any missing permissions, and 114 | * requests runtime permission if needed. 115 | */ 116 | private void checkAndRequestPermissions() { 117 | // Check for permissions 118 | for (String eachPermission : REQUIRED_PERMISSION_LIST) { 119 | if (ContextCompat.checkSelfPermission(this, eachPermission) != PackageManager.PERMISSION_GRANTED) { 120 | missingPermission.add(eachPermission); 121 | } 122 | } 123 | // Request for missing permissions 124 | if (!missingPermission.isEmpty() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 125 | ActivityCompat.requestPermissions(this, 126 | missingPermission.toArray(new String[missingPermission.size()]), 127 | REQUEST_PERMISSION_CODE); 128 | } 129 | 130 | } 131 | 132 | /** 133 | * Result of runtime permission request 134 | */ 135 | @Override 136 | public void onRequestPermissionsResult(int requestCode, 137 | @NonNull String[] permissions, 138 | @NonNull int[] grantResults) { 139 | super.onRequestPermissionsResult(requestCode, permissions, grantResults); 140 | // Check for granted permission and remove from missing list 141 | if (requestCode == REQUEST_PERMISSION_CODE) { 142 | for (int i = grantResults.length - 1; i >= 0; i--) { 143 | if (grantResults[i] == PackageManager.PERMISSION_GRANTED) { 144 | missingPermission.remove(permissions[i]); 145 | } 146 | } 147 | } 148 | // If there is enough permission, we will start the registration 149 | if (missingPermission.isEmpty()) { 150 | startSDKRegistration(); 151 | } else { 152 | showToast("Missing permissions!!!"); 153 | } 154 | } 155 | 156 | private void startSDKRegistration() { 157 | if (isRegistrationInProgress.compareAndSet(false, true)) { 158 | AsyncTask.execute(new Runnable() { 159 | @Override 160 | public void run() { 161 | showToast( "registering, pls wait..."); 162 | DJISDKManager.getInstance().registerApp(getApplicationContext(), new DJISDKManager.SDKManagerCallback() { 163 | @Override 164 | public void onRegister(DJIError djiError) { 165 | if (djiError == DJISDKError.REGISTRATION_SUCCESS) { 166 | DJILog.e("App registration", DJISDKError.REGISTRATION_SUCCESS.getDescription()); 167 | DJISDKManager.getInstance().startConnectionToProduct(); 168 | showToast("Register Success"); 169 | } else { 170 | showToast( "Register sdk fails, check network is available"); 171 | } 172 | Log.v(TAG, djiError.getDescription()); 173 | } 174 | 175 | @Override 176 | public void onProductDisconnect() { 177 | Log.d(TAG, "onProductDisconnect"); 178 | showToast("Product Disconnected"); 179 | 180 | } 181 | @Override 182 | public void onProductConnect(BaseProduct baseProduct) { 183 | Log.d(TAG, String.format("onProductConnect newProduct:%s", baseProduct)); 184 | showToast("Product Connected"); 185 | 186 | } 187 | 188 | @Override 189 | public void onProductChanged(BaseProduct baseProduct) { 190 | 191 | } 192 | 193 | @Override 194 | public void onComponentChange(BaseProduct.ComponentKey componentKey, BaseComponent oldComponent, 195 | BaseComponent newComponent) { 196 | 197 | if (newComponent != null) { 198 | newComponent.setComponentListener(new BaseComponent.ComponentListener() { 199 | 200 | @Override 201 | public void onConnectivityChange(boolean isConnected) { 202 | Log.d(TAG, "onComponentConnectivityChanged: " + isConnected); 203 | } 204 | }); 205 | } 206 | Log.d(TAG, 207 | String.format("onComponentChange key:%s, oldComponent:%s, newComponent:%s", 208 | componentKey, 209 | oldComponent, 210 | newComponent)); 211 | 212 | } 213 | @Override 214 | public void onInitProcess(DJISDKInitEvent djisdkInitEvent, int i) { 215 | 216 | } 217 | 218 | @Override 219 | public void onDatabaseDownloadProgress(long l, long l1) { 220 | 221 | } 222 | }); 223 | } 224 | }); 225 | } 226 | } 227 | 228 | protected BroadcastReceiver mReceiver = new BroadcastReceiver() { 229 | 230 | @Override 231 | public void onReceive(Context context, Intent intent) { 232 | updateTitleBar(); 233 | } 234 | }; 235 | 236 | public void showToast(final String msg) { 237 | runOnUiThread(new Runnable() { 238 | public void run() { 239 | Toast.makeText(MainActivity.this, msg, Toast.LENGTH_SHORT).show(); 240 | } 241 | }); 242 | } 243 | 244 | private void updateTitleBar() { 245 | if(mConnectStatusTextView == null) return; 246 | boolean ret = false; 247 | BaseProduct product = DJISimulatorApplication.getProductInstance(); 248 | if (product != null) { 249 | if(product.isConnected()) { 250 | //The product is connected 251 | mConnectStatusTextView.setText(DJISimulatorApplication.getProductInstance().getModel() + " Connected"); 252 | ret = true; 253 | } else { 254 | if(product instanceof Aircraft) { 255 | Aircraft aircraft = (Aircraft)product; 256 | if(aircraft.getRemoteController() != null && aircraft.getRemoteController().isConnected()) { 257 | // The product is not connected, but the remote controller is connected 258 | mConnectStatusTextView.setText("only RC Connected"); 259 | ret = true; 260 | } 261 | } 262 | } 263 | } 264 | 265 | if(!ret) { 266 | // The product or the remote controller are not connected. 267 | mConnectStatusTextView.setText("Disconnected"); 268 | } 269 | } 270 | 271 | @Override 272 | public void onResume() { 273 | Log.e(TAG, "onResume"); 274 | super.onResume(); 275 | updateTitleBar(); 276 | initFlightController(); 277 | loginAccount(); 278 | 279 | } 280 | 281 | @Override 282 | public void onPause() { 283 | Log.e(TAG, "onPause"); 284 | super.onPause(); 285 | } 286 | 287 | @Override 288 | public void onStop() { 289 | Log.e(TAG, "onStop"); 290 | super.onStop(); 291 | } 292 | 293 | public void onReturn(View view){ 294 | Log.e(TAG, "onReturn"); 295 | this.finish(); 296 | } 297 | 298 | @Override 299 | protected void onDestroy() { 300 | Log.e(TAG, "onDestroy"); 301 | unregisterReceiver(mReceiver); 302 | if (null != mSendVirtualStickDataTimer) { 303 | mSendVirtualStickDataTask.cancel(); 304 | mSendVirtualStickDataTask = null; 305 | mSendVirtualStickDataTimer.cancel(); 306 | mSendVirtualStickDataTimer.purge(); 307 | mSendVirtualStickDataTimer = null; 308 | } 309 | super.onDestroy(); 310 | } 311 | 312 | private void loginAccount(){ 313 | 314 | UserAccountManager.getInstance().logIntoDJIUserAccount(this, 315 | new CommonCallbacks.CompletionCallbackWith() { 316 | @Override 317 | public void onSuccess(final UserAccountState userAccountState) { 318 | Log.e(TAG, "Login Success"); 319 | } 320 | @Override 321 | public void onFailure(DJIError error) { 322 | showToast("Login Error:" 323 | + error.getDescription()); 324 | } 325 | }); 326 | } 327 | 328 | private void initFlightController() { 329 | 330 | Aircraft aircraft = DJISimulatorApplication.getAircraftInstance(); 331 | if (aircraft == null || !aircraft.isConnected()) { 332 | showToast("Disconnected"); 333 | mFlightController = null; 334 | return; 335 | } else { 336 | mFlightController = aircraft.getFlightController(); 337 | mFlightController.setRollPitchControlMode(RollPitchControlMode.VELOCITY); 338 | mFlightController.setYawControlMode(YawControlMode.ANGULAR_VELOCITY); 339 | mFlightController.setVerticalControlMode(VerticalControlMode.VELOCITY); 340 | mFlightController.setRollPitchCoordinateSystem(FlightCoordinateSystem.BODY); 341 | mFlightController.getSimulator().setStateCallback(new SimulatorState.Callback() { 342 | @Override 343 | public void onUpdate(final SimulatorState stateData) { 344 | new Handler(Looper.getMainLooper()).post(new Runnable() { 345 | @Override 346 | public void run() { 347 | 348 | String yaw = String.format("%.2f", stateData.getYaw()); 349 | String pitch = String.format("%.2f", stateData.getPitch()); 350 | String roll = String.format("%.2f", stateData.getRoll()); 351 | String positionX = String.format("%.2f", stateData.getPositionX()); 352 | String positionY = String.format("%.2f", stateData.getPositionY()); 353 | String positionZ = String.format("%.2f", stateData.getPositionZ()); 354 | 355 | mTextView.setText("Yaw : " + yaw + ", Pitch : " + pitch + ", Roll : " + roll + "\n" + ", PosX : " + positionX + 356 | ", PosY : " + positionY + 357 | ", PosZ : " + positionZ); 358 | } 359 | }); 360 | } 361 | }); 362 | } 363 | } 364 | 365 | private void initUI() { 366 | 367 | mBtnEnableVirtualStick = (Button) findViewById(R.id.btn_enable_virtual_stick); 368 | mBtnDisableVirtualStick = (Button) findViewById(R.id.btn_disable_virtual_stick); 369 | mBtnTakeOff = (Button) findViewById(R.id.btn_take_off); 370 | mBtnLand = (Button) findViewById(R.id.btn_land); 371 | mBtnSimulator = (ToggleButton) findViewById(R.id.btn_start_simulator); 372 | mTextView = (TextView) findViewById(R.id.textview_simulator); 373 | mConnectStatusTextView = (TextView) findViewById(R.id.ConnectStatusTextView); 374 | mScreenJoystickRight = (OnScreenJoystick)findViewById(R.id.directionJoystickRight); 375 | mScreenJoystickLeft = (OnScreenJoystick)findViewById(R.id.directionJoystickLeft); 376 | 377 | mBtnEnableVirtualStick.setOnClickListener(this); 378 | mBtnDisableVirtualStick.setOnClickListener(this); 379 | mBtnTakeOff.setOnClickListener(this); 380 | mBtnLand.setOnClickListener(this); 381 | 382 | mBtnSimulator.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { 383 | @Override 384 | public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 385 | if (isChecked) { 386 | 387 | mTextView.setVisibility(View.VISIBLE); 388 | 389 | if (mFlightController != null) { 390 | 391 | mFlightController.getSimulator() 392 | .start(InitializationData.createInstance(new LocationCoordinate2D(23, 113), 10, 10), 393 | new CommonCallbacks.CompletionCallback() { 394 | @Override 395 | public void onResult(DJIError djiError) { 396 | if (djiError != null) { 397 | showToast(djiError.getDescription()); 398 | }else 399 | { 400 | showToast("Start Simulator Success"); 401 | } 402 | } 403 | }); 404 | } 405 | 406 | } else { 407 | 408 | mTextView.setVisibility(View.INVISIBLE); 409 | 410 | if (mFlightController != null) { 411 | mFlightController.getSimulator() 412 | .stop(new CommonCallbacks.CompletionCallback() { 413 | @Override 414 | public void onResult(DJIError djiError) { 415 | if (djiError != null) { 416 | showToast(djiError.getDescription()); 417 | }else 418 | { 419 | showToast("Stop Simulator Success"); 420 | } 421 | } 422 | } 423 | ); 424 | } 425 | } 426 | } 427 | }); 428 | 429 | mScreenJoystickRight.setJoystickListener(new OnScreenJoystickListener(){ 430 | 431 | @Override 432 | public void onTouch(OnScreenJoystick joystick, float pX, float pY) { 433 | if(Math.abs(pX) < 0.02 ){ 434 | pX = 0; 435 | } 436 | 437 | if(Math.abs(pY) < 0.02 ){ 438 | pY = 0; 439 | } 440 | 441 | float pitchJoyControlMaxSpeed = 10; 442 | float rollJoyControlMaxSpeed = 10; 443 | 444 | mPitch = (float)(pitchJoyControlMaxSpeed * pX); 445 | 446 | mRoll = (float)(rollJoyControlMaxSpeed * pY); 447 | 448 | if (null == mSendVirtualStickDataTimer) { 449 | mSendVirtualStickDataTask = new SendVirtualStickDataTask(); 450 | mSendVirtualStickDataTimer = new Timer(); 451 | mSendVirtualStickDataTimer.schedule(mSendVirtualStickDataTask, 100, 200); 452 | } 453 | 454 | } 455 | 456 | }); 457 | 458 | mScreenJoystickLeft.setJoystickListener(new OnScreenJoystickListener() { 459 | 460 | @Override 461 | public void onTouch(OnScreenJoystick joystick, float pX, float pY) { 462 | if(Math.abs(pX) < 0.02 ){ 463 | pX = 0; 464 | } 465 | 466 | if(Math.abs(pY) < 0.02 ){ 467 | pY = 0; 468 | } 469 | float verticalJoyControlMaxSpeed = 2; 470 | float yawJoyControlMaxSpeed = 30; 471 | 472 | mYaw = (float)(yawJoyControlMaxSpeed * pX); 473 | mThrottle = (float)(verticalJoyControlMaxSpeed * pY); 474 | 475 | if (null == mSendVirtualStickDataTimer) { 476 | mSendVirtualStickDataTask = new SendVirtualStickDataTask(); 477 | mSendVirtualStickDataTimer = new Timer(); 478 | mSendVirtualStickDataTimer.schedule(mSendVirtualStickDataTask, 0, 200); 479 | } 480 | 481 | } 482 | }); 483 | } 484 | 485 | @Override 486 | public void onClick(View v) { 487 | 488 | switch (v.getId()) { 489 | case R.id.btn_enable_virtual_stick: 490 | if (mFlightController != null){ 491 | 492 | mFlightController.setVirtualStickModeEnabled(true, new CommonCallbacks.CompletionCallback() { 493 | @Override 494 | public void onResult(DJIError djiError) { 495 | if (djiError != null){ 496 | showToast(djiError.getDescription()); 497 | }else 498 | { 499 | showToast("Enable Virtual Stick Success"); 500 | } 501 | } 502 | }); 503 | 504 | } 505 | break; 506 | 507 | case R.id.btn_disable_virtual_stick: 508 | 509 | if (mFlightController != null){ 510 | mFlightController.setVirtualStickModeEnabled(false, new CommonCallbacks.CompletionCallback() { 511 | @Override 512 | public void onResult(DJIError djiError) { 513 | if (djiError != null) { 514 | showToast(djiError.getDescription()); 515 | } else { 516 | showToast("Disable Virtual Stick Success"); 517 | } 518 | } 519 | }); 520 | } 521 | break; 522 | 523 | case R.id.btn_take_off: 524 | if (mFlightController != null){ 525 | mFlightController.startTakeoff( 526 | new CommonCallbacks.CompletionCallback() { 527 | @Override 528 | public void onResult(DJIError djiError) { 529 | if (djiError != null) { 530 | showToast(djiError.getDescription()); 531 | } else { 532 | showToast("Take off Success"); 533 | } 534 | } 535 | } 536 | ); 537 | } 538 | 539 | break; 540 | 541 | case R.id.btn_land: 542 | if (mFlightController != null){ 543 | 544 | mFlightController.startLanding( 545 | djiError -> { 546 | if (djiError != null) { 547 | showToast(djiError.getDescription()); 548 | } else { 549 | showToast("Start Landing"); 550 | } 551 | } 552 | ); 553 | 554 | } 555 | 556 | break; 557 | 558 | default: 559 | break; 560 | } 561 | } 562 | 563 | class SendVirtualStickDataTask extends TimerTask { 564 | 565 | @Override 566 | public void run() { 567 | 568 | if (mFlightController != null) { 569 | mFlightController.sendVirtualStickFlightControlData( 570 | new FlightControlData( 571 | mPitch, mRoll, mYaw, mThrottle 572 | ), djiError -> { 573 | 574 | } 575 | ); 576 | } 577 | } 578 | } 579 | 580 | } 581 | -------------------------------------------------------------------------------- /DJISimulatorDemo/app/src/main/java/com/dji/simulatorDemo/OnScreenJoystick.java: -------------------------------------------------------------------------------- 1 | package com.dji.simulatorDemo; 2 | 3 | /* 4 | * Copyright (c) 2014 Ville Saarinen 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | */ 24 | 25 | import android.content.Context; 26 | import android.content.res.Resources; 27 | import android.graphics.Bitmap; 28 | import android.graphics.BitmapFactory; 29 | import android.graphics.Canvas; 30 | import android.graphics.Color; 31 | import android.graphics.PixelFormat; 32 | import android.graphics.PorterDuff; 33 | import android.graphics.Rect; 34 | import android.util.AttributeSet; 35 | import android.view.MotionEvent; 36 | import android.view.SurfaceHolder; 37 | import android.view.SurfaceView; 38 | import android.view.View; 39 | 40 | 41 | public class OnScreenJoystick extends SurfaceView implements 42 | SurfaceHolder.Callback, View.OnTouchListener { 43 | 44 | private Bitmap mJoystick; 45 | private SurfaceHolder mHolder; 46 | private Rect mKnobBounds; 47 | 48 | private JoystickThread mThread; 49 | 50 | private int mKnobX, mKnobY; 51 | private int mKnobSize; 52 | private int mBackgroundSize; 53 | private float mRadius; 54 | 55 | private OnScreenJoystickListener mJoystickListener; 56 | 57 | private boolean mAutoCentering = true; 58 | 59 | public OnScreenJoystick(Context context, AttributeSet attrs) { 60 | super(context, attrs); 61 | 62 | initGraphics(attrs); 63 | init(); 64 | } 65 | 66 | private void initGraphics(AttributeSet attrs) { 67 | Resources res = getContext().getResources(); 68 | mJoystick = BitmapFactory 69 | .decodeResource( 70 | res, R.mipmap.joystick); 71 | 72 | } 73 | 74 | private void initBounds(final Canvas pCanvas) { 75 | mBackgroundSize = pCanvas.getHeight(); 76 | mKnobSize = Math.round(mBackgroundSize * 0.6f); 77 | 78 | mKnobBounds = new Rect(); 79 | 80 | mRadius = mBackgroundSize * 0.5f; 81 | mKnobX = Math.round((mBackgroundSize - mKnobSize) * 0.5f); 82 | mKnobY = Math.round((mBackgroundSize - mKnobSize) * 0.5f); 83 | 84 | } 85 | 86 | private void init() { 87 | mHolder = getHolder(); 88 | mHolder.addCallback(this); 89 | 90 | mThread = new JoystickThread(); 91 | 92 | setZOrderOnTop(true); 93 | mHolder.setFormat(PixelFormat.TRANSPARENT); 94 | setOnTouchListener(this); 95 | setEnabled(true); 96 | setAutoCentering(true); 97 | } 98 | 99 | public void setAutoCentering(final boolean pAutoCentering) { 100 | mAutoCentering = pAutoCentering; 101 | } 102 | 103 | public boolean isAutoCentering() { 104 | return mAutoCentering; 105 | } 106 | 107 | public void setJoystickListener( 108 | final OnScreenJoystickListener pJoystickListener) { 109 | mJoystickListener = pJoystickListener; 110 | } 111 | 112 | @Override 113 | public void surfaceChanged(final SurfaceHolder arg0, final int arg1, 114 | final int arg2, final int arg3) { 115 | 116 | // mThread.setRunning(false); 117 | } 118 | 119 | @Override 120 | public void surfaceCreated(final SurfaceHolder arg0) { 121 | mThread.start(); 122 | 123 | } 124 | 125 | @Override 126 | public void surfaceDestroyed(final SurfaceHolder arg0) { 127 | boolean retry = true; 128 | mThread.setRunning(false); 129 | 130 | while (retry) { 131 | try { 132 | // code to kill Thread 133 | mThread.join(); 134 | retry = false; 135 | } catch (InterruptedException e) { 136 | } 137 | } 138 | 139 | } 140 | 141 | public void doDraw(final Canvas pCanvas) { 142 | if (mKnobBounds == null) { 143 | initBounds(pCanvas); 144 | } 145 | 146 | // pCanvas.drawBitmap(mJoystickBg, null, mBgBounds, null); 147 | 148 | mKnobBounds.set(mKnobX, mKnobY, mKnobX + mKnobSize, mKnobY + mKnobSize); 149 | pCanvas.drawBitmap(mJoystick, null, mKnobBounds, null); 150 | } 151 | 152 | @Override 153 | public boolean onTouch(final View arg0, final MotionEvent pEvent) { 154 | final float x = pEvent.getX(); 155 | final float y = pEvent.getY(); 156 | 157 | switch (pEvent.getAction()) { 158 | 159 | case MotionEvent.ACTION_UP: 160 | if (isAutoCentering()) { 161 | mKnobX = Math.round((mBackgroundSize - mKnobSize) * 0.5f); 162 | mKnobY = Math.round((mBackgroundSize - mKnobSize) * 0.5f); 163 | } 164 | break; 165 | default: 166 | // Check if coordinates are in bounds. If they aren't move the knob 167 | // to the closest coordinate inbounds. 168 | if (checkBounds(x, y)) { 169 | mKnobX = Math.round(x - mKnobSize * 0.5f); 170 | mKnobY = Math.round(y - mKnobSize * 0.5f); 171 | } else { 172 | final double angle = Math.atan2(y - mRadius, x - mRadius); 173 | mKnobX = (int) (Math.round(mRadius 174 | + (mRadius - mKnobSize * 0.5f) * Math.cos(angle)) - mKnobSize * 0.5f); 175 | mKnobY = (int) (Math.round(mRadius 176 | + (mRadius - mKnobSize * 0.5f) * Math.sin(angle)) - mKnobSize * 0.5f); 177 | } 178 | } 179 | 180 | if (mJoystickListener != null) { 181 | mJoystickListener.onTouch(this, 182 | (0.5f - (mKnobX / (mRadius * 2 - mKnobSize))) * -2, 183 | (0.5f - (mKnobY / (mRadius * 2 - mKnobSize))) * 2); 184 | 185 | } 186 | 187 | return true; 188 | } 189 | 190 | private boolean checkBounds(final float pX, final float pY) { 191 | return Math.pow(mRadius - pX, 2) + Math.pow(mRadius - pY, 2) <= Math 192 | .pow(mRadius - mKnobSize * 0.5f, 2); 193 | } 194 | 195 | private class JoystickThread extends Thread { 196 | 197 | private boolean running = false; 198 | 199 | @Override 200 | public synchronized void start() { 201 | running = true; 202 | super.start(); 203 | } 204 | 205 | public void setRunning(final boolean pRunning) { 206 | running = pRunning; 207 | } 208 | 209 | @Override 210 | public void run() { 211 | while (running) { 212 | // draw everything to the canvas 213 | Canvas canvas = null; 214 | try { 215 | canvas = mHolder.lockCanvas(null); 216 | synchronized (mHolder) { 217 | // reset canvas 218 | canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); 219 | doDraw(canvas); 220 | } 221 | } 222 | catch(Exception e){} 223 | finally { 224 | if (canvas != null) { 225 | mHolder.unlockCanvasAndPost(canvas); 226 | } 227 | } 228 | 229 | } 230 | } 231 | } 232 | 233 | } 234 | -------------------------------------------------------------------------------- /DJISimulatorDemo/app/src/main/java/com/dji/simulatorDemo/OnScreenJoystickListener.java: -------------------------------------------------------------------------------- 1 | package com.dji.simulatorDemo; 2 | 3 | /* 4 | * Copyright (c) 2014 Ville Saarinen 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | */ 24 | 25 | public interface OnScreenJoystickListener { 26 | 27 | /** Called when the joystick is touched. 28 | * @param joystick The joystick which has been touched. 29 | * @param pX The x coordinate of the knob. Values are between -1 (left) and 1 (right). 30 | * @param pY The y coordinate of the knob. Values are between -1 (down) and 1 (up). 31 | */ 32 | public void onTouch(final OnScreenJoystick joystick, final float pX, final float pY); 33 | } 34 | -------------------------------------------------------------------------------- /DJISimulatorDemo/app/src/main/res/drawable/back_button_disable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DJI-Mobile-SDK-Tutorials/Android-SimulatorDemo/100cdc50b9dab9049248ed450284f6db953a5a65/DJISimulatorDemo/app/src/main/res/drawable/back_button_disable.png -------------------------------------------------------------------------------- /DJISimulatorDemo/app/src/main/res/drawable/back_button_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DJI-Mobile-SDK-Tutorials/Android-SimulatorDemo/100cdc50b9dab9049248ed450284f6db953a5a65/DJISimulatorDemo/app/src/main/res/drawable/back_button_normal.png -------------------------------------------------------------------------------- /DJISimulatorDemo/app/src/main/res/drawable/back_button_press.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DJI-Mobile-SDK-Tutorials/Android-SimulatorDemo/100cdc50b9dab9049248ed450284f6db953a5a65/DJISimulatorDemo/app/src/main/res/drawable/back_button_press.png -------------------------------------------------------------------------------- /DJISimulatorDemo/app/src/main/res/drawable/round_btn.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /DJISimulatorDemo/app/src/main/res/drawable/round_btn_disable.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | -------------------------------------------------------------------------------- /DJISimulatorDemo/app/src/main/res/drawable/round_btn_normal.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | -------------------------------------------------------------------------------- /DJISimulatorDemo/app/src/main/res/drawable/round_btn_pressed.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | -------------------------------------------------------------------------------- /DJISimulatorDemo/app/src/main/res/drawable/selector_back_button.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /DJISimulatorDemo/app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 14 | 26 | 27 | 35 | 36 | 37 | 44 | 45 |