├── .gitignore ├── LICENSE.txt ├── MediaManagerDemo ├── app │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src │ │ ├── androidTest │ │ └── java │ │ │ └── com │ │ │ └── dji │ │ │ └── mediaManagerDemo │ │ │ └── ExampleInstrumentedTest.java │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ │ └── com │ │ │ │ └── dji │ │ │ │ └── mediaManagerDemo │ │ │ │ ├── ConnectionActivity.java │ │ │ │ ├── DefaultLayoutActivity.java │ │ │ │ ├── DemoApplication.java │ │ │ │ ├── MApplication.java │ │ │ │ └── MainActivity.java │ │ └── res │ │ │ ├── drawable │ │ │ ├── back_button_disable.png │ │ │ ├── back_button_normal.png │ │ │ ├── back_button_press.png │ │ │ ├── background_selector.xml │ │ │ ├── round_btn.xml │ │ │ ├── round_btn_disable.xml │ │ │ ├── round_btn_normal.xml │ │ │ ├── round_btn_pressed.xml │ │ │ └── selector_back_button.xml │ │ │ ├── layout │ │ │ ├── activity_connection.xml │ │ │ ├── activity_default_layout.xml │ │ │ ├── activity_main.xml │ │ │ ├── media_info_item.xml │ │ │ └── prompt_input_position.xml │ │ │ ├── mipmap-hdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── raw │ │ │ └── keep.xml │ │ │ └── values │ │ │ ├── colors.xml │ │ │ ├── strings.xml │ │ │ └── styles.xml │ │ └── test │ │ └── java │ │ └── com │ │ └── dji │ │ └── mediaManagerDemo │ │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle └── 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 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | The MIT License (MIT) 3 | Copyright (c) 2019 DJI 4 | 5 | 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: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | 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. 10 | -------------------------------------------------------------------------------- /MediaManagerDemo/app/.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # Files for the ART/Dalvik VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # Generated files 12 | bin/ 13 | gen/ 14 | out/ 15 | 16 | # Gradle files 17 | .gradle/ 18 | gradle/ 19 | build/ 20 | gradlew 21 | gradlew.bat 22 | 23 | # Local configuration file (sdk path, etc) 24 | local.properties 25 | 26 | # Proguard folder generated by Eclipse 27 | proguard/ 28 | 29 | # Log Files 30 | *.log 31 | 32 | # Android Studio Navigation editor temp files 33 | .navigation/ 34 | 35 | # Android Studio captures folder 36 | captures/ 37 | 38 | # Intellij 39 | *.iml 40 | .idea/ 41 | .idea/* 42 | .idea/libraries 43 | 44 | # Keystore files 45 | *.jks 46 | 47 | .DS_Store 48 | -------------------------------------------------------------------------------- /MediaManagerDemo/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | repositories { 4 | mavenLocal() 5 | mavenCentral() 6 | maven { url 'https://mapbox.bintray.com/mapbox' } 7 | } 8 | 9 | android { 10 | compileSdkVersion 30 11 | buildToolsVersion '30.0.2' 12 | 13 | defaultConfig { 14 | minSdkVersion 21 15 | targetSdkVersion 30 16 | multiDexEnabled true 17 | ndk { 18 | abiFilters 'armeabi-v7a', 'arm64-v8a' 19 | } 20 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 21 | 22 | } 23 | 24 | buildTypes { 25 | release { 26 | minifyEnabled false 27 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 28 | } 29 | debug { 30 | shrinkResources true 31 | minifyEnabled true 32 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 33 | } 34 | } 35 | 36 | dexOptions { 37 | javaMaxHeapSize "4g" 38 | } 39 | 40 | packagingOptions { 41 | doNotStrip "*/*/libdjivideo.so" 42 | doNotStrip "*/*/libSDKRelativeJNI.so" 43 | doNotStrip "*/*/libFlyForbid.so" 44 | doNotStrip "*/*/libduml_vision_bokeh.so" 45 | doNotStrip "*/*/libyuv2.so" 46 | doNotStrip "*/*/libGroudStation.so" 47 | doNotStrip "*/*/libFRCorkscrew.so" 48 | doNotStrip "*/*/libUpgradeVerify.so" 49 | doNotStrip "*/*/libFR.so" 50 | doNotStrip "*/*/libDJIFlySafeCore.so" 51 | doNotStrip "*/*/libdjifs_jni.so" 52 | doNotStrip "*/*/libsfjni.so" 53 | doNotStrip "*/*/libDJICommonJNI.so" 54 | doNotStrip "*/*/libDJICSDKCommon.so" 55 | doNotStrip "*/*/libDJIUpgradeCore.so" 56 | doNotStrip "*/*/libDJIUpgradeJNI.so" 57 | exclude 'META-INF/rxjava.properties' 58 | } 59 | 60 | compileOptions { 61 | sourceCompatibility JavaVersion.VERSION_1_8 62 | targetCompatibility JavaVersion.VERSION_1_8 63 | } 64 | } 65 | 66 | 67 | dependencies { 68 | implementation 'androidx.multidex:multidex:2.0.1' 69 | implementation 'com.squareup:otto:1.3.8' 70 | implementation('com.dji:dji-sdk:4.16', { 71 | /** 72 | * Uncomment the "library-anti-distortion" if your app does not need Anti Distortion for Mavic 2 Pro and Mavic 2 Zoom. 73 | * Uncomment the "fly-safe-database" if you need database for release, or we will download it when DJISDKManager.getInstance().registerApp 74 | * is called. 75 | * Both will greatly reducing the size of the APK. 76 | */ 77 | exclude module: 'library-anti-distortion' 78 | //exclude module: 'fly-safe-database' 79 | }) 80 | 81 | androidTestImplementation('androidx.test.espresso:espresso-core:3.1.0', { 82 | exclude group: 'com.android.support', module: 'support-annotations' 83 | }) 84 | 85 | compileOnly 'com.dji:dji-sdk-provided:4.16' 86 | implementation ('com.dji:dji-uxsdk:4.16', { 87 | /** 88 | * Uncomment the following line to exclude amap from the app. 89 | * Note that Google Play Store does not allow APKs that include this library. 90 | */ 91 | // exclude group: 'com.amap.api' 92 | exclude module: 'dji-sdk' 93 | }) 94 | 95 | implementation 'androidx.appcompat:appcompat:1.2.0' 96 | implementation 'androidx.core:core:1.3.2' 97 | implementation 'androidx.constraintlayout:constraintlayout:2.0.4' 98 | implementation 'androidx.recyclerview:recyclerview:1.1.0' 99 | implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0' 100 | implementation 'androidx.annotation:annotation:1.2.0' 101 | } 102 | -------------------------------------------------------------------------------- /MediaManagerDemo/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------------> -------------------------------------------------------------------------------- /MediaManagerDemo/app/src/androidTest/java/com/dji/mediaManagerDemo/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.dji.mediaManagerDemo; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumentation test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.dji.mediaManagerDemo", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /MediaManagerDemo/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 26 | 29 | 30 | 31 | 40 | 41 | 42 | 43 | 44 | 45 | 48 | 49 | 50 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 67 | 68 | 72 | 75 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /MediaManagerDemo/app/src/main/java/com/dji/mediaManagerDemo/ConnectionActivity.java: -------------------------------------------------------------------------------- 1 | package com.dji.mediaManagerDemo; 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.Build; 12 | 13 | import androidx.annotation.NonNull; 14 | import androidx.core.app.ActivityCompat; 15 | import android.os.Bundle; 16 | import androidx.core.content.ContextCompat; 17 | import android.text.TextUtils; 18 | import android.util.Log; 19 | import android.view.View; 20 | import android.widget.Button; 21 | import android.widget.TextView; 22 | import android.widget.Toast; 23 | 24 | import dji.common.error.DJIError; 25 | import dji.common.error.DJISDKError; 26 | import dji.common.useraccount.UserAccountState; 27 | import dji.common.util.CommonCallbacks; 28 | import dji.keysdk.DJIKey; 29 | import dji.keysdk.KeyManager; 30 | import dji.keysdk.ProductKey; 31 | import dji.keysdk.callback.KeyListener; 32 | import dji.log.DJILog; 33 | import dji.sdk.base.BaseComponent; 34 | import dji.sdk.base.BaseProduct; 35 | import dji.sdk.products.Aircraft; 36 | import dji.sdk.sdkmanager.DJISDKInitEvent; 37 | import dji.sdk.sdkmanager.DJISDKManager; 38 | import dji.sdk.useraccount.UserAccountManager; 39 | import java.util.ArrayList; 40 | import java.util.List; 41 | import java.util.concurrent.atomic.AtomicBoolean; 42 | 43 | public class ConnectionActivity extends Activity implements View.OnClickListener { 44 | 45 | private static final String TAG = MainActivity.class.getName(); 46 | 47 | private TextView mTextConnectionStatus; 48 | private TextView mTextProduct; 49 | private TextView mTextModelAvailable; 50 | private TextView mVersionTv; 51 | 52 | private Button mBtnOpen; 53 | private static final String[] REQUIRED_PERMISSION_LIST = new String[]{ 54 | Manifest.permission.BLUETOOTH, 55 | Manifest.permission.BLUETOOTH_ADMIN, 56 | Manifest.permission.VIBRATE, 57 | Manifest.permission.INTERNET, 58 | Manifest.permission.ACCESS_WIFI_STATE, 59 | Manifest.permission.ACCESS_COARSE_LOCATION, 60 | Manifest.permission.ACCESS_NETWORK_STATE, 61 | Manifest.permission.ACCESS_FINE_LOCATION, 62 | Manifest.permission.CHANGE_WIFI_STATE, 63 | Manifest.permission.RECORD_AUDIO, 64 | Manifest.permission.WRITE_EXTERNAL_STORAGE, 65 | Manifest.permission.READ_EXTERNAL_STORAGE, 66 | Manifest.permission.READ_PHONE_STATE, 67 | }; 68 | private List missingPermission = new ArrayList<>(); 69 | private AtomicBoolean isRegistrationInProgress = new AtomicBoolean(false); 70 | private static final int REQUEST_PERMISSION_CODE = 12345; 71 | private DJIKey firmwareKey; 72 | private KeyListener firmwareVersionUpdater; 73 | private boolean hasStartedFirmVersionListener = false; 74 | 75 | @Override 76 | protected void onCreate(Bundle savedInstanceState) { 77 | super.onCreate(savedInstanceState); 78 | checkAndRequestPermissions(); 79 | setContentView(R.layout.activity_connection); 80 | initUI(); 81 | 82 | // Register the broadcast receiver for receiving the device connection's changes. 83 | IntentFilter filter = new IntentFilter(); 84 | filter.addAction(DemoApplication.FLAG_CONNECTION_CHANGE); 85 | registerReceiver(mReceiver, filter); 86 | } 87 | 88 | /** 89 | * Checks if there is any missing permissions, and 90 | * requests runtime permission if needed. 91 | */ 92 | private void checkAndRequestPermissions() { 93 | // Check for permissions 94 | for (String eachPermission : REQUIRED_PERMISSION_LIST) { 95 | if (ContextCompat.checkSelfPermission(this, eachPermission) != PackageManager.PERMISSION_GRANTED) { 96 | missingPermission.add(eachPermission); 97 | } 98 | } 99 | // Request for missing permissions 100 | if (!missingPermission.isEmpty() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 101 | ActivityCompat.requestPermissions(this, 102 | missingPermission.toArray(new String[missingPermission.size()]), 103 | REQUEST_PERMISSION_CODE); 104 | } 105 | } 106 | 107 | /** 108 | * Result of runtime permission request 109 | */ 110 | @Override 111 | public void onRequestPermissionsResult(int requestCode, 112 | @NonNull String[] permissions, 113 | @NonNull int[] grantResults) { 114 | super.onRequestPermissionsResult(requestCode, permissions, grantResults); 115 | // Check for granted permission and remove from missing list 116 | if (requestCode == REQUEST_PERMISSION_CODE) { 117 | for (int i = grantResults.length - 1; i >= 0; i--) { 118 | if (grantResults[i] == PackageManager.PERMISSION_GRANTED) { 119 | missingPermission.remove(permissions[i]); 120 | } 121 | } 122 | } 123 | // If there is enough permission, we will start the registration 124 | if (missingPermission.isEmpty()) { 125 | startSDKRegistration(); 126 | } else { 127 | showToast("Missing permissions!!!"); 128 | } 129 | } 130 | 131 | private void startSDKRegistration() { 132 | if (isRegistrationInProgress.compareAndSet(false, true)) { 133 | AsyncTask.execute(new Runnable() { 134 | @Override 135 | public void run() { 136 | showToast( "registering, pls wait..."); 137 | DJISDKManager.getInstance().registerApp(ConnectionActivity.this.getApplicationContext(), new DJISDKManager.SDKManagerCallback() { 138 | @Override 139 | public void onRegister(DJIError djiError) { 140 | if (djiError == DJISDKError.REGISTRATION_SUCCESS) { 141 | DJILog.e("App registration", DJISDKError.REGISTRATION_SUCCESS.getDescription()); 142 | DJISDKManager.getInstance().startConnectionToProduct(); 143 | showToast("Register Success"); 144 | } else { 145 | showToast( "Register sdk fails, check network is available: djiError:"+djiError); 146 | } 147 | Log.v(TAG, djiError.getDescription()); 148 | } 149 | 150 | @Override 151 | public void onProductDisconnect() { 152 | Log.d(TAG, "onProductDisconnect"); 153 | showToast("Product Disconnected"); 154 | 155 | } 156 | 157 | @Override 158 | public void onProductConnect(BaseProduct baseProduct) { 159 | Log.d(TAG, String.format("onProductConnect newProduct:%s", baseProduct)); 160 | showToast("Product Connected"); 161 | 162 | } 163 | 164 | @Override 165 | public void onProductChanged(BaseProduct baseProduct) { 166 | 167 | } 168 | 169 | @Override 170 | public void onComponentChange(BaseProduct.ComponentKey componentKey, BaseComponent oldComponent, 171 | BaseComponent newComponent) { 172 | if (newComponent != null) { 173 | newComponent.setComponentListener(new BaseComponent.ComponentListener() { 174 | 175 | @Override 176 | public void onConnectivityChange(boolean isConnected) { 177 | Log.d(TAG, "onComponentConnectivityChanged: " + isConnected); 178 | } 179 | }); 180 | } 181 | Log.d(TAG, 182 | String.format("onComponentChange key:%s, oldComponent:%s, newComponent:%s", 183 | componentKey, 184 | oldComponent, 185 | newComponent)); 186 | } 187 | @Override 188 | public void onInitProcess(DJISDKInitEvent djisdkInitEvent, int i) { 189 | 190 | } 191 | 192 | @Override 193 | public void onDatabaseDownloadProgress(long l, long l1) { 194 | 195 | } 196 | }); 197 | } 198 | }); 199 | } 200 | } 201 | 202 | @Override 203 | public void onResume() { 204 | Log.e(TAG, "onResume"); 205 | super.onResume(); 206 | updateTitleBar(); 207 | } 208 | 209 | @Override 210 | public void onPause() { 211 | Log.e(TAG, "onPause"); 212 | super.onPause(); 213 | } 214 | 215 | @Override 216 | public void onStop() { 217 | Log.e(TAG, "onStop"); 218 | super.onStop(); 219 | } 220 | 221 | public void onReturn(View view){ 222 | Log.e(TAG, "onReturn"); 223 | this.finish(); 224 | } 225 | 226 | @Override 227 | protected void onDestroy() { 228 | Log.e(TAG, "onDestroy"); 229 | unregisterReceiver(mReceiver); 230 | removeFirmwareVersionListener(); 231 | super.onDestroy(); 232 | } 233 | 234 | private void initUI() { 235 | mTextConnectionStatus = (TextView) findViewById(R.id.text_connection_status); 236 | mTextModelAvailable = (TextView) findViewById(R.id.text_model_available); 237 | mTextProduct = (TextView) findViewById(R.id.text_product_info); 238 | mVersionTv = (TextView) findViewById(R.id.textView2); 239 | mVersionTv.setText(getResources().getString(R.string.sdk_version, DJISDKManager.getInstance().getSDKVersion())); 240 | mBtnOpen = (Button) findViewById(R.id.btn_open); 241 | mBtnOpen.setOnClickListener(this); 242 | mBtnOpen.setEnabled(false); 243 | } 244 | 245 | protected BroadcastReceiver mReceiver = new BroadcastReceiver() { 246 | @Override 247 | public void onReceive(Context context, Intent intent) { 248 | refreshSDKRelativeUI(); 249 | } 250 | }; 251 | 252 | private void updateTitleBar() { 253 | boolean ret = false; 254 | BaseProduct product = DemoApplication.getProductInstance(); 255 | if (product != null) { 256 | if(product.isConnected()) { 257 | //The product is connected 258 | showToast(DemoApplication.getProductInstance().getModel() + " Connected"); 259 | ret = true; 260 | } else { 261 | if(product instanceof Aircraft) { 262 | Aircraft aircraft = (Aircraft)product; 263 | if(aircraft.getRemoteController() != null && aircraft.getRemoteController().isConnected()) { 264 | // The product is not connected, but the remote controller is connected 265 | showToast("only RC Connected"); 266 | ret = true; 267 | } 268 | } 269 | } 270 | } 271 | } 272 | 273 | public void showToast(final String msg) { 274 | runOnUiThread(new Runnable() { 275 | public void run() { 276 | Toast.makeText(ConnectionActivity.this, msg, Toast.LENGTH_SHORT).show(); 277 | } 278 | }); 279 | } 280 | 281 | private void updateVersion() { 282 | String version = null; 283 | if (DemoApplication.getProductInstance() != null) { 284 | version = DemoApplication.getProductInstance().getFirmwarePackageVersion(); 285 | } 286 | if (TextUtils.isEmpty(version)) { 287 | mTextModelAvailable.setText("Firmware version:N/A"); //Firmware version: 288 | } else { 289 | mTextModelAvailable.setText("Firmware version:"+version); //"Firmware version: " + 290 | removeFirmwareVersionListener(); 291 | } 292 | } 293 | 294 | @Override 295 | public void onClick(View v) { 296 | if (v.getId() == R.id.btn_open) { 297 | Intent intent = new Intent(this, DefaultLayoutActivity.class); 298 | startActivity(intent); 299 | } 300 | } 301 | 302 | private void refreshSDKRelativeUI() { 303 | BaseProduct mProduct = DemoApplication.getProductInstance(); 304 | 305 | if (null != mProduct && mProduct.isConnected()) { 306 | Log.v(TAG, "refreshSDK: True"); 307 | mBtnOpen.setEnabled(true); 308 | String str = mProduct instanceof Aircraft ? "DJIAircraft" : "DJIHandHeld"; 309 | mTextConnectionStatus.setText("Status: " + str + " connected"); 310 | tryUpdateFirmwareVersionWithListener(); 311 | if (null != mProduct.getModel()) { 312 | mTextProduct.setText("" + mProduct.getModel().getDisplayName()); 313 | } else { 314 | mTextProduct.setText(R.string.product_information); 315 | } 316 | loginAccount(); 317 | } else { 318 | Log.v(TAG, "refreshSDK: False"); 319 | mBtnOpen.setEnabled(false); 320 | 321 | mTextProduct.setText(R.string.product_information); 322 | mTextConnectionStatus.setText(R.string.connection_loose); 323 | } 324 | } 325 | 326 | private void loginAccount(){ 327 | UserAccountManager.getInstance().logIntoDJIUserAccount(this, 328 | new CommonCallbacks.CompletionCallbackWith() { 329 | @Override 330 | public void onSuccess(final UserAccountState userAccountState) { 331 | Log.e(TAG, "Login Success"); 332 | showToast("Login Success!"); 333 | } 334 | @Override 335 | public void onFailure(DJIError error) { 336 | showToast("Login Error:" 337 | + error.getDescription()); 338 | } 339 | }); 340 | } 341 | 342 | private void tryUpdateFirmwareVersionWithListener() { 343 | if (!hasStartedFirmVersionListener) { 344 | firmwareVersionUpdater = new KeyListener() { 345 | @Override 346 | public void onValueChange(final Object o, final Object o1) { 347 | runOnUiThread(new Runnable() { 348 | @Override 349 | public void run() { 350 | updateVersion(); 351 | } 352 | }); 353 | } 354 | }; 355 | firmwareKey = ProductKey.create(ProductKey.FIRMWARE_PACKAGE_VERSION); 356 | if (KeyManager.getInstance() != null) { 357 | KeyManager.getInstance().addListener(firmwareKey, firmwareVersionUpdater ); 358 | } 359 | hasStartedFirmVersionListener = true; 360 | } 361 | updateVersion(); 362 | } 363 | private void removeFirmwareVersionListener() { 364 | if (hasStartedFirmVersionListener) { 365 | if (KeyManager.getInstance() != null) { 366 | KeyManager.getInstance().removeListener(firmwareVersionUpdater); 367 | } 368 | } 369 | hasStartedFirmVersionListener = false; 370 | } 371 | } 372 | -------------------------------------------------------------------------------- /MediaManagerDemo/app/src/main/java/com/dji/mediaManagerDemo/DefaultLayoutActivity.java: -------------------------------------------------------------------------------- 1 | package com.dji.mediaManagerDemo; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.view.View; 6 | import android.widget.Button; 7 | 8 | import androidx.appcompat.app.AppCompatActivity; 9 | 10 | import dji.keysdk.ProductKey; 11 | import dji.ux.widget.FPVWidget; 12 | 13 | public class DefaultLayoutActivity extends AppCompatActivity implements View.OnClickListener{ 14 | 15 | private Button mMediaManagerBtn; 16 | private FPVWidget fpvWidget; 17 | 18 | @Override 19 | protected void onCreate(Bundle savedInstanceState) { 20 | super.onCreate(savedInstanceState); 21 | setContentView(R.layout.activity_default_layout); 22 | mMediaManagerBtn = (Button)findViewById(R.id.btn_mediaManager); 23 | fpvWidget = findViewById(R.id.fpv_widget); 24 | mMediaManagerBtn.setOnClickListener(this); 25 | } 26 | 27 | @Override 28 | public void onClick(View v) { 29 | if (v.getId() == R.id.btn_mediaManager) { 30 | Intent intent = new Intent(this, MainActivity.class); 31 | startActivity(intent); 32 | } 33 | } 34 | 35 | @Override 36 | protected void onResume() { 37 | super.onResume(); 38 | fpvWidget.updateWidget(ProductKey.create(ProductKey.MODEL_NAME)); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /MediaManagerDemo/app/src/main/java/com/dji/mediaManagerDemo/DemoApplication.java: -------------------------------------------------------------------------------- 1 | package com.dji.mediaManagerDemo; 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 androidx.core.content.ContextCompat; 10 | import android.util.Log; 11 | import android.widget.Toast; 12 | 13 | import dji.common.error.DJIError; 14 | import dji.common.error.DJISDKError; 15 | import dji.sdk.base.BaseComponent; 16 | import dji.sdk.base.BaseProduct; 17 | import dji.sdk.camera.Camera; 18 | import dji.sdk.products.Aircraft; 19 | import dji.sdk.products.HandHeld; 20 | import dji.sdk.sdkmanager.DJISDKInitEvent; 21 | import dji.sdk.sdkmanager.DJISDKManager; 22 | 23 | public class DemoApplication extends Application { 24 | 25 | public static final String FLAG_CONNECTION_CHANGE = "mediaManager_demo_connection_change"; 26 | 27 | private static BaseProduct mProduct; 28 | private Handler mHandler; 29 | private DJISDKManager.SDKManagerCallback mDJISDKManagerCallback; 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 DemoApplication() { 42 | } 43 | /** 44 | * This function is used to get the instance of DJIBaseProduct. 45 | * If no product is connected, it returns null. 46 | */ 47 | public static synchronized BaseProduct getProductInstance() { 48 | if (null == mProduct) { 49 | mProduct = DJISDKManager.getInstance().getProduct(); 50 | } 51 | return mProduct; 52 | } 53 | 54 | public static synchronized Aircraft getAircraftInstance() { 55 | if (!isAircraftConnected()) return null; 56 | return (Aircraft) getProductInstance(); 57 | } 58 | 59 | public static synchronized Camera getCameraInstance() { 60 | 61 | if (getProductInstance() == null) return null; 62 | 63 | Camera camera = null; 64 | 65 | if (getProductInstance() instanceof Aircraft){ 66 | camera = ((Aircraft) getProductInstance()).getCamera(); 67 | 68 | } else if (getProductInstance() instanceof HandHeld) { 69 | camera = ((HandHeld) getProductInstance()).getCamera(); 70 | } 71 | 72 | return camera; 73 | } 74 | 75 | public static boolean isAircraftConnected() { 76 | return getProductInstance() != null && getProductInstance() instanceof Aircraft; 77 | } 78 | 79 | public static boolean isHandHeldConnected() { 80 | return getProductInstance() != null && getProductInstance() instanceof HandHeld; 81 | } 82 | 83 | public static boolean isProductModuleAvailable() { 84 | return (null != DemoApplication.getProductInstance()); 85 | } 86 | 87 | public static boolean isCameraModuleAvailable() { 88 | return isProductModuleAvailable() && 89 | (null != DemoApplication.getProductInstance().getCamera()); 90 | } 91 | 92 | public static boolean isPlaybackAvailable() { 93 | return isCameraModuleAvailable() && 94 | (null != DemoApplication.getProductInstance().getCamera().getPlaybackManager()); 95 | } 96 | 97 | 98 | @Override 99 | public void onCreate() { 100 | super.onCreate(); 101 | mHandler = new Handler(Looper.getMainLooper()); 102 | 103 | /** 104 | * When starting SDK services, an instance of interface DJISDKManager.DJISDKManagerCallback will be used to listen to 105 | * the SDK Registration result and the product changing. 106 | */ 107 | mDJISDKManagerCallback = new DJISDKManager.SDKManagerCallback() { 108 | 109 | //Listens to the SDK registration result 110 | @Override 111 | public void onRegister(DJIError error) { 112 | 113 | if(error == DJISDKError.REGISTRATION_SUCCESS) { 114 | 115 | Handler handler = new Handler(Looper.getMainLooper()); 116 | handler.post(new Runnable() { 117 | @Override 118 | public void run() { 119 | Toast.makeText(getApplicationContext(), "Register Success", Toast.LENGTH_LONG).show(); 120 | } 121 | }); 122 | 123 | DJISDKManager.getInstance().startConnectionToProduct(); 124 | 125 | } else { 126 | 127 | Handler handler = new Handler(Looper.getMainLooper()); 128 | handler.post(new Runnable() { 129 | 130 | @Override 131 | public void run() { 132 | Toast.makeText(getApplicationContext(), "Register sdk fails, check network is available", Toast.LENGTH_LONG).show(); 133 | } 134 | }); 135 | 136 | } 137 | Log.e("TAG", error.toString()); 138 | } 139 | 140 | @Override 141 | public void onProductDisconnect() { 142 | Log.d("TAG", "onProductDisconnect"); 143 | notifyStatusChange(); 144 | } 145 | @Override 146 | public void onProductConnect(BaseProduct baseProduct) { 147 | Log.d("TAG", String.format("onProductConnect newProduct:%s", baseProduct)); 148 | notifyStatusChange(); 149 | 150 | } 151 | 152 | @Override 153 | public void onProductChanged(BaseProduct baseProduct) { 154 | 155 | } 156 | 157 | @Override 158 | public void onComponentChange(BaseProduct.ComponentKey componentKey, BaseComponent oldComponent, 159 | BaseComponent newComponent) { 160 | if (newComponent != null) { 161 | newComponent.setComponentListener(new BaseComponent.ComponentListener() { 162 | 163 | @Override 164 | public void onConnectivityChange(boolean isConnected) { 165 | Log.d("TAG", "onComponentConnectivityChanged: " + isConnected); 166 | notifyStatusChange(); 167 | } 168 | }); 169 | } 170 | 171 | Log.d("TAG", 172 | String.format("onComponentChange key:%s, oldComponent:%s, newComponent:%s", 173 | componentKey, 174 | oldComponent, 175 | newComponent)); 176 | 177 | } 178 | @Override 179 | public void onInitProcess(DJISDKInitEvent djisdkInitEvent, int i) { 180 | 181 | } 182 | 183 | @Override 184 | public void onDatabaseDownloadProgress(long l, long l1) { 185 | 186 | } 187 | }; 188 | 189 | //Check the permissions before registering the application for android system 6.0 above. 190 | int permissionCheck = ContextCompat.checkSelfPermission(getApplicationContext(), android.Manifest.permission.WRITE_EXTERNAL_STORAGE); 191 | int permissionCheck2 = ContextCompat.checkSelfPermission(getApplicationContext(), android.Manifest.permission.READ_PHONE_STATE); 192 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M || (permissionCheck == 0 && permissionCheck2 == 0)) { 193 | //This is used to start SDK services and initiate SDK. 194 | DJISDKManager.getInstance().registerApp(getApplicationContext(), mDJISDKManagerCallback); 195 | Toast.makeText(getApplicationContext(), "registering, pls wait...", Toast.LENGTH_LONG).show(); 196 | 197 | } else { 198 | Toast.makeText(getApplicationContext(), "Please check if the permission is granted.", Toast.LENGTH_LONG).show(); 199 | } 200 | } 201 | 202 | private void notifyStatusChange() { 203 | mHandler.removeCallbacks(updateRunnable); 204 | mHandler.postDelayed(updateRunnable, 500); 205 | } 206 | 207 | private Runnable updateRunnable = new Runnable() { 208 | 209 | @Override 210 | public void run() { 211 | Intent intent = new Intent(FLAG_CONNECTION_CHANGE); 212 | getApplicationContext().sendBroadcast(intent); 213 | } 214 | }; 215 | 216 | } 217 | -------------------------------------------------------------------------------- /MediaManagerDemo/app/src/main/java/com/dji/mediaManagerDemo/MApplication.java: -------------------------------------------------------------------------------- 1 | package com.dji.mediaManagerDemo; 2 | 3 | import android.app.Application; 4 | import android.content.Context; 5 | 6 | import com.secneo.sdk.Helper; 7 | 8 | public class MApplication extends Application { 9 | 10 | private DemoApplication demoApplication; 11 | @Override 12 | protected void attachBaseContext(Context paramContext) { 13 | super.attachBaseContext(paramContext); 14 | Helper.install(MApplication.this); 15 | if (demoApplication == null) { 16 | demoApplication = new DemoApplication(); 17 | demoApplication.setContext(this); 18 | } 19 | } 20 | 21 | @Override 22 | public void onCreate() { 23 | super.onCreate(); 24 | demoApplication.onCreate(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /MediaManagerDemo/app/src/main/java/com/dji/mediaManagerDemo/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.dji.mediaManagerDemo; 2 | 3 | import android.app.Activity; 4 | import android.app.AlertDialog; 5 | import android.app.ProgressDialog; 6 | import android.content.DialogInterface; 7 | import android.graphics.Bitmap; 8 | import android.os.Bundle; 9 | import android.os.Environment; 10 | import android.view.LayoutInflater; 11 | import android.view.View; 12 | import android.view.ViewGroup; 13 | import android.widget.Button; 14 | import android.widget.EditText; 15 | import android.widget.ImageView; 16 | import android.widget.SlidingDrawer; 17 | import android.widget.TextView; 18 | import android.widget.Toast; 19 | 20 | import org.jetbrains.annotations.NotNull; 21 | 22 | import androidx.annotation.NonNull; 23 | import androidx.recyclerview.widget.LinearLayoutManager; 24 | import androidx.recyclerview.widget.OrientationHelper; 25 | import androidx.recyclerview.widget.RecyclerView; 26 | 27 | import java.io.File; 28 | import java.util.ArrayList; 29 | import java.util.Collections; 30 | import java.util.Comparator; 31 | import java.util.List; 32 | 33 | import dji.common.airlink.PhysicalSource; 34 | import dji.common.camera.SettingsDefinitions; 35 | import dji.common.camera.StorageState; 36 | import dji.common.error.DJICameraError; 37 | import dji.common.error.DJIError; 38 | import dji.common.product.Model; 39 | import dji.common.util.CommonCallbacks; 40 | import dji.log.DJILog; 41 | import dji.sdk.base.BaseProduct; 42 | import dji.sdk.media.DownloadListener; 43 | import dji.sdk.media.FetchMediaTask; 44 | import dji.sdk.media.FetchMediaTaskContent; 45 | import dji.sdk.media.FetchMediaTaskScheduler; 46 | import dji.sdk.media.MediaFile; 47 | import dji.sdk.media.MediaManager; 48 | 49 | public class MainActivity extends Activity implements View.OnClickListener { 50 | 51 | private static final String TAG = MainActivity.class.getName(); 52 | 53 | private Button mBackBtn, mDeleteBtn, mReloadBtn, mDownloadBtn, mStatusBtn; 54 | private Button mPlayBtn, mResumeBtn, mPauseBtn, mStopBtn, mMoveToBtn; 55 | private RecyclerView listView; 56 | private FileListAdapter mListAdapter; 57 | private List mediaFileList = new ArrayList(); 58 | private MediaManager mMediaManager; 59 | private MediaManager.FileListState currentFileListState = MediaManager.FileListState.UNKNOWN; 60 | private FetchMediaTaskScheduler scheduler; 61 | private ProgressDialog mLoadingDialog; 62 | private ProgressDialog mDownloadDialog; 63 | private SlidingDrawer mPushDrawerSd; 64 | File destDir = new File(Environment.getExternalStorageDirectory().getPath() + "/MediaManagerDemo/"); 65 | private int currentProgress = -1; 66 | private ImageView mDisplayImageView; 67 | private int lastClickViewIndex =-1; 68 | private View lastClickView; 69 | private TextView mPushTv; 70 | private SettingsDefinitions.StorageLocation storageLocation; 71 | 72 | @Override 73 | protected void onCreate(Bundle savedInstanceState) { 74 | super.onCreate(savedInstanceState); 75 | setContentView(R.layout.activity_main); 76 | initUI(); 77 | DemoApplication.getAircraftInstance().getCamera().setStorageStateCallBack(new StorageState.Callback() { 78 | @Override 79 | public void onUpdate(@NonNull @NotNull StorageState storageState) { 80 | if(storageState.isInserted()) { 81 | storageLocation = SettingsDefinitions.StorageLocation.SDCARD; 82 | DemoApplication.getAircraftInstance().getCamera().setStorageLocation(SettingsDefinitions.StorageLocation.SDCARD, new CommonCallbacks.CompletionCallback() { 83 | @Override 84 | public void onResult(DJIError djiError) { 85 | } 86 | }); 87 | } else { 88 | storageLocation = SettingsDefinitions.StorageLocation.INTERNAL_STORAGE; 89 | DemoApplication.getAircraftInstance().getCamera().setStorageLocation(SettingsDefinitions.StorageLocation.INTERNAL_STORAGE, new CommonCallbacks.CompletionCallback() { 90 | @Override 91 | public void onResult(DJIError djiError) { 92 | } 93 | }); 94 | } 95 | } 96 | }); 97 | } 98 | 99 | @Override 100 | protected void onResume() { 101 | super.onResume(); 102 | initMediaManager(); 103 | } 104 | 105 | @Override 106 | protected void onPause() { 107 | super.onPause(); 108 | } 109 | 110 | @Override 111 | protected void onStop() { 112 | super.onStop(); 113 | } 114 | 115 | @Override 116 | protected void onDestroy() { 117 | lastClickView = null; 118 | if (mMediaManager != null) { 119 | mMediaManager.stop(null); 120 | mMediaManager.removeFileListStateCallback(this.updateFileListStateListener); 121 | mMediaManager.removeMediaUpdatedVideoPlaybackStateListener(updatedVideoPlaybackStateListener); 122 | mMediaManager.exitMediaDownloading(); 123 | if (scheduler!=null) { 124 | scheduler.removeAllTasks(); 125 | } 126 | } 127 | 128 | if (DemoApplication.getCameraInstance() != null) { 129 | if (isMavicAir2() || isAir2S() || isM300()) { 130 | DemoApplication.getCameraInstance().exitPlayback(djiError -> { 131 | if (djiError != null) { 132 | DemoApplication.getCameraInstance().setFlatMode(SettingsDefinitions.FlatCameraMode.PHOTO_SINGLE, djiError1 -> { 133 | if (djiError1 != null) { 134 | setResultToToast("Set PHOTO_SINGLE Mode Failed. " + djiError1.getDescription()); 135 | } 136 | }); 137 | } 138 | }); 139 | } else { 140 | DemoApplication.getCameraInstance().setMode(SettingsDefinitions.CameraMode.SHOOT_PHOTO, djiError -> { 141 | if (djiError != null) { 142 | setResultToToast("Set SHOOT_PHOTO Mode Failed. " + djiError.getDescription()); 143 | } 144 | }); 145 | } 146 | } 147 | 148 | if (mediaFileList != null) { 149 | mediaFileList.clear(); 150 | } 151 | super.onDestroy(); 152 | } 153 | 154 | void initUI() { 155 | 156 | //Init RecyclerView 157 | listView = (RecyclerView) findViewById(R.id.filelistView); 158 | LinearLayoutManager layoutManager = new LinearLayoutManager(MainActivity.this, RecyclerView.VERTICAL,false); 159 | listView.setLayoutManager(layoutManager); 160 | 161 | //Init FileListAdapter 162 | mListAdapter = new FileListAdapter(); 163 | listView.setAdapter(mListAdapter); 164 | 165 | //Init Loading Dialog 166 | mLoadingDialog = new ProgressDialog(MainActivity.this); 167 | mLoadingDialog.setMessage("Please wait"); 168 | mLoadingDialog.setCanceledOnTouchOutside(false); 169 | mLoadingDialog.setCancelable(false); 170 | 171 | //Init Download Dialog 172 | mDownloadDialog = new ProgressDialog(MainActivity.this); 173 | mDownloadDialog.setTitle("Downloading file"); 174 | mDownloadDialog.setIcon(android.R.drawable.ic_dialog_info); 175 | mDownloadDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); 176 | mDownloadDialog.setCanceledOnTouchOutside(false); 177 | mDownloadDialog.setCancelable(true); 178 | mDownloadDialog.setOnCancelListener(new DialogInterface.OnCancelListener() { 179 | @Override 180 | public void onCancel(DialogInterface dialog) { 181 | if (mMediaManager != null) { 182 | mMediaManager.exitMediaDownloading(); 183 | } 184 | } 185 | }); 186 | 187 | mPushDrawerSd = (SlidingDrawer)findViewById(R.id.pointing_drawer_sd); 188 | mPushTv = (TextView)findViewById(R.id.pointing_push_tv); 189 | mBackBtn = (Button) findViewById(R.id.back_btn); 190 | mDeleteBtn = (Button) findViewById(R.id.delete_btn); 191 | mDownloadBtn = (Button) findViewById(R.id.download_btn); 192 | mReloadBtn = (Button) findViewById(R.id.reload_btn); 193 | mStatusBtn = (Button) findViewById(R.id.status_btn); 194 | mPlayBtn = (Button) findViewById(R.id.play_btn); 195 | mResumeBtn = (Button) findViewById(R.id.resume_btn); 196 | mPauseBtn = (Button) findViewById(R.id.pause_btn); 197 | mStopBtn = (Button) findViewById(R.id.stop_btn); 198 | mMoveToBtn = (Button) findViewById(R.id.moveTo_btn); 199 | mDisplayImageView = (ImageView) findViewById(R.id.imageView); 200 | mDisplayImageView.setVisibility(View.VISIBLE); 201 | 202 | mBackBtn.setOnClickListener(this); 203 | mDeleteBtn.setOnClickListener(this); 204 | mDownloadBtn.setOnClickListener(this); 205 | mReloadBtn.setOnClickListener(this); 206 | mDownloadBtn.setOnClickListener(this); 207 | mStatusBtn.setOnClickListener(this); 208 | mPlayBtn.setOnClickListener(this); 209 | mResumeBtn.setOnClickListener(this); 210 | mPauseBtn.setOnClickListener(this); 211 | mStopBtn.setOnClickListener(this); 212 | mMoveToBtn.setOnClickListener(this); 213 | 214 | } 215 | 216 | private void showProgressDialog() { 217 | runOnUiThread(new Runnable() { 218 | public void run() { 219 | if (mLoadingDialog != null) { 220 | mLoadingDialog.show(); 221 | } 222 | } 223 | }); 224 | } 225 | 226 | private void hideProgressDialog() { 227 | runOnUiThread(new Runnable() { 228 | public void run() { 229 | if (null != mLoadingDialog && mLoadingDialog.isShowing()) { 230 | mLoadingDialog.dismiss(); 231 | } 232 | } 233 | }); 234 | } 235 | 236 | private void ShowDownloadProgressDialog() { 237 | if (mDownloadDialog != null) { 238 | runOnUiThread(new Runnable() { 239 | public void run() { 240 | mDownloadDialog.incrementProgressBy(-mDownloadDialog.getProgress()); 241 | mDownloadDialog.show(); 242 | } 243 | }); 244 | } 245 | } 246 | 247 | private void HideDownloadProgressDialog() { 248 | if (null != mDownloadDialog && mDownloadDialog.isShowing()) { 249 | runOnUiThread(new Runnable() { 250 | public void run() { 251 | mDownloadDialog.dismiss(); 252 | } 253 | }); 254 | } 255 | } 256 | 257 | private void setResultToToast(final String result) { 258 | runOnUiThread(new Runnable() { 259 | public void run() { 260 | Toast.makeText(MainActivity.this, result, Toast.LENGTH_SHORT).show(); 261 | } 262 | }); 263 | } 264 | 265 | private void setResultToText(final String string) { 266 | if (mPushTv == null) { 267 | setResultToToast("Push info tv has not be init..."); 268 | } 269 | MainActivity.this.runOnUiThread(new Runnable() { 270 | @Override 271 | public void run() { 272 | mPushTv.setText(string); 273 | } 274 | }); 275 | } 276 | 277 | private void initMediaManager() { 278 | if (DemoApplication.getProductInstance() == null) { 279 | mediaFileList.clear(); 280 | mListAdapter.notifyDataSetChanged(); 281 | DJILog.e(TAG, "Product disconnected"); 282 | return; 283 | } else { 284 | if (null != DemoApplication.getCameraInstance() && DemoApplication.getCameraInstance().isMediaDownloadModeSupported()) { 285 | mMediaManager = DemoApplication.getCameraInstance().getMediaManager(); 286 | if (null != mMediaManager) { 287 | mMediaManager.addUpdateFileListStateListener(this.updateFileListStateListener); 288 | mMediaManager.addMediaUpdatedVideoPlaybackStateListener(this.updatedVideoPlaybackStateListener); 289 | if (isMavicAir2() || isAir2S() || isM300()) { 290 | DemoApplication.getCameraInstance().enterPlayback(djiError -> { 291 | if (djiError == null) { 292 | DJILog.e(TAG, "Set cameraMode success"); 293 | showProgressDialog(); 294 | getFileList(); 295 | } else { 296 | setResultToToast("Set cameraMode failed"); 297 | } 298 | }); 299 | } else { 300 | DemoApplication.getCameraInstance().setMode(SettingsDefinitions.CameraMode.MEDIA_DOWNLOAD, error -> { 301 | if (error == null) { 302 | DJILog.e(TAG, "Set cameraMode success"); 303 | showProgressDialog(); 304 | getFileList(); 305 | } else { 306 | setResultToToast("Set cameraMode failed"); 307 | } 308 | }); 309 | } 310 | 311 | if (mMediaManager.isVideoPlaybackSupported()) { 312 | DJILog.e(TAG, "Camera support video playback!"); 313 | } else { 314 | setResultToToast("Camera does not support video playback!"); 315 | } 316 | scheduler = mMediaManager.getScheduler(); 317 | } 318 | 319 | } else if (null != DemoApplication.getCameraInstance() 320 | && !DemoApplication.getCameraInstance().isMediaDownloadModeSupported()) { 321 | setResultToToast("Media Download Mode not Supported"); 322 | } 323 | } 324 | return; 325 | } 326 | 327 | private void getFileList() { 328 | mMediaManager = DemoApplication.getCameraInstance().getMediaManager(); 329 | if (mMediaManager != null) { 330 | 331 | if ((currentFileListState == MediaManager.FileListState.SYNCING) || (currentFileListState == MediaManager.FileListState.DELETING)){ 332 | DJILog.e(TAG, "Media Manager is busy."); 333 | }else{ 334 | mMediaManager.refreshFileListOfStorageLocation(storageLocation, djiError -> { 335 | if (null == djiError) { 336 | hideProgressDialog(); 337 | 338 | //Reset data 339 | if (currentFileListState != MediaManager.FileListState.INCOMPLETE) { 340 | mediaFileList.clear(); 341 | lastClickViewIndex = -1; 342 | lastClickView = null; 343 | } 344 | 345 | List tempList; 346 | if (storageLocation == SettingsDefinitions.StorageLocation.SDCARD) { 347 | tempList = mMediaManager.getSDCardFileListSnapshot(); 348 | } else { 349 | tempList = mMediaManager.getInternalStorageFileListSnapshot(); 350 | } 351 | if (tempList != null) { 352 | mediaFileList.addAll(tempList); 353 | } 354 | if (mediaFileList != null) { 355 | Collections.sort(mediaFileList, (lhs, rhs) -> { 356 | if (lhs.getTimeCreated() < rhs.getTimeCreated()) { 357 | return 1; 358 | } else if (lhs.getTimeCreated() > rhs.getTimeCreated()) { 359 | return -1; 360 | } 361 | return 0; 362 | }); 363 | } 364 | scheduler.resume(error -> { 365 | if (error == null) { 366 | getThumbnails(); 367 | } 368 | }); 369 | } else { 370 | hideProgressDialog(); 371 | setResultToToast("Get Media File List Failed:" + djiError.getDescription()); 372 | } 373 | }); 374 | } 375 | } 376 | } 377 | 378 | private void getThumbnails() { 379 | if (mediaFileList.size() <= 0) { 380 | setResultToToast("No File info for downloading thumbnails"); 381 | return; 382 | } 383 | for (int i = 0; i < mediaFileList.size(); i++) { 384 | getThumbnailByIndex(i); 385 | } 386 | } 387 | 388 | private FetchMediaTask.Callback taskCallback = new FetchMediaTask.Callback() { 389 | @Override 390 | public void onUpdate(MediaFile file, FetchMediaTaskContent option, DJIError error) { 391 | if (null == error) { 392 | if (option == FetchMediaTaskContent.PREVIEW) { 393 | runOnUiThread(new Runnable() { 394 | public void run() { 395 | mListAdapter.notifyDataSetChanged(); 396 | } 397 | }); 398 | } 399 | if (option == FetchMediaTaskContent.THUMBNAIL) { 400 | runOnUiThread(new Runnable() { 401 | public void run() { 402 | mListAdapter.notifyDataSetChanged(); 403 | } 404 | }); 405 | } 406 | } else { 407 | DJILog.e(TAG, "Fetch Media Task Failed" + error.getDescription()); 408 | } 409 | } 410 | }; 411 | 412 | private void getThumbnailByIndex(final int index) { 413 | FetchMediaTask task = new FetchMediaTask(mediaFileList.get(index), FetchMediaTaskContent.THUMBNAIL, taskCallback); 414 | scheduler.moveTaskToEnd(task); 415 | } 416 | 417 | private static class ItemHolder extends RecyclerView.ViewHolder { 418 | ImageView thumbnail_img; 419 | TextView file_name; 420 | TextView file_type; 421 | TextView file_size; 422 | TextView file_time; 423 | 424 | public ItemHolder(View itemView) { 425 | super(itemView); 426 | this.thumbnail_img = (ImageView) itemView.findViewById(R.id.filethumbnail); 427 | this.file_name = (TextView) itemView.findViewById(R.id.filename); 428 | this.file_type = (TextView) itemView.findViewById(R.id.filetype); 429 | this.file_size = (TextView) itemView.findViewById(R.id.fileSize); 430 | this.file_time = (TextView) itemView.findViewById(R.id.filetime); 431 | } 432 | } 433 | 434 | private class FileListAdapter extends RecyclerView.Adapter { 435 | @Override 436 | public int getItemCount() { 437 | if (mediaFileList != null) { 438 | return mediaFileList.size(); 439 | } 440 | return 0; 441 | } 442 | 443 | @Override 444 | public ItemHolder onCreateViewHolder(ViewGroup parent, int viewType) { 445 | View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.media_info_item, parent, false); 446 | return new ItemHolder(view); 447 | } 448 | 449 | @Override 450 | public void onBindViewHolder(ItemHolder mItemHolder, final int index) { 451 | 452 | final MediaFile mediaFile = mediaFileList.get(index); 453 | if (mediaFile != null) { 454 | if (mediaFile.getMediaType() != MediaFile.MediaType.MOV && mediaFile.getMediaType() != MediaFile.MediaType.MP4) { 455 | mItemHolder.file_time.setVisibility(View.GONE); 456 | } else { 457 | mItemHolder.file_time.setVisibility(View.VISIBLE); 458 | mItemHolder.file_time.setText(mediaFile.getDurationInSeconds() + " s"); 459 | } 460 | mItemHolder.file_name.setText(mediaFile.getFileName()); 461 | mItemHolder.file_type.setText(mediaFile.getMediaType().name()); 462 | mItemHolder.file_size.setText(mediaFile.getFileSize() + " Bytes"); 463 | mItemHolder.thumbnail_img.setImageBitmap(mediaFile.getThumbnail()); 464 | mItemHolder.thumbnail_img.setOnClickListener(ImgOnClickListener); 465 | mItemHolder.thumbnail_img.setTag(mediaFile); 466 | mItemHolder.itemView.setTag(index); 467 | 468 | if (lastClickViewIndex == index) { 469 | mItemHolder.itemView.setSelected(true); 470 | } else { 471 | mItemHolder.itemView.setSelected(false); 472 | } 473 | mItemHolder.itemView.setOnClickListener(itemViewOnClickListener); 474 | 475 | } 476 | } 477 | } 478 | 479 | private View.OnClickListener itemViewOnClickListener = new View.OnClickListener() { 480 | @Override 481 | public void onClick(View v) { 482 | lastClickViewIndex = (int) (v.getTag()); 483 | 484 | if (lastClickView != null && lastClickView != v) { 485 | lastClickView.setSelected(false); 486 | } 487 | v.setSelected(true); 488 | lastClickView = v; 489 | } 490 | }; 491 | 492 | private View.OnClickListener ImgOnClickListener = new View.OnClickListener() { 493 | @Override 494 | public void onClick(View v) { 495 | MediaFile selectedMedia = (MediaFile) v.getTag(); 496 | if (selectedMedia != null && mMediaManager != null) { 497 | addMediaTask(selectedMedia); 498 | } 499 | } 500 | }; 501 | 502 | private void addMediaTask(final MediaFile mediaFile) { 503 | final FetchMediaTaskScheduler scheduler = mMediaManager.getScheduler(); 504 | final FetchMediaTask task = 505 | new FetchMediaTask(mediaFile, FetchMediaTaskContent.PREVIEW, new FetchMediaTask.Callback() { 506 | @Override 507 | public void onUpdate(final MediaFile mediaFile, FetchMediaTaskContent fetchMediaTaskContent, DJIError error) { 508 | if (null == error) { 509 | if (mediaFile.getPreview() != null) { 510 | runOnUiThread(new Runnable() { 511 | @Override 512 | public void run() { 513 | final Bitmap previewBitmap = mediaFile.getPreview(); 514 | mDisplayImageView.setVisibility(View.VISIBLE); 515 | mDisplayImageView.setImageBitmap(previewBitmap); 516 | } 517 | }); 518 | } else { 519 | setResultToToast("null bitmap!"); 520 | } 521 | } else { 522 | setResultToToast("fetch preview image failed: " + error.getDescription()); 523 | } 524 | } 525 | }); 526 | 527 | scheduler.resume(error -> { 528 | if (error == null) { 529 | scheduler.moveTaskToNext(task); 530 | } else { 531 | setResultToToast("resume scheduler failed: " + error.getDescription()); 532 | } 533 | }); 534 | } 535 | 536 | //Listeners 537 | private MediaManager.FileListStateListener updateFileListStateListener = state -> currentFileListState = state; 538 | 539 | private MediaManager.VideoPlaybackStateListener updatedVideoPlaybackStateListener = 540 | new MediaManager.VideoPlaybackStateListener() { 541 | @Override 542 | public void onUpdate(MediaManager.VideoPlaybackState videoPlaybackState) { 543 | updateStatusTextView(videoPlaybackState); 544 | } 545 | }; 546 | 547 | private void updateStatusTextView(MediaManager.VideoPlaybackState videoPlaybackState) { 548 | final StringBuffer pushInfo = new StringBuffer(); 549 | 550 | addLineToSB(pushInfo, "Video Playback State", null); 551 | if (videoPlaybackState != null) { 552 | if (videoPlaybackState.getPlayingMediaFile() != null) { 553 | addLineToSB(pushInfo, "media index", videoPlaybackState.getPlayingMediaFile().getIndex()); 554 | addLineToSB(pushInfo, "media size", videoPlaybackState.getPlayingMediaFile().getFileSize()); 555 | addLineToSB(pushInfo, 556 | "media duration", 557 | videoPlaybackState.getPlayingMediaFile().getDurationInSeconds()); 558 | addLineToSB(pushInfo, "media created date", videoPlaybackState.getPlayingMediaFile().getDateCreated()); 559 | addLineToSB(pushInfo, 560 | "media orientation", 561 | videoPlaybackState.getPlayingMediaFile().getVideoOrientation()); 562 | } else { 563 | addLineToSB(pushInfo, "media index", "None"); 564 | } 565 | addLineToSB(pushInfo, "media current position", videoPlaybackState.getPlayingPosition()); 566 | addLineToSB(pushInfo, "media current status", videoPlaybackState.getPlaybackStatus()); 567 | addLineToSB(pushInfo, "media cached percentage", videoPlaybackState.getCachedPercentage()); 568 | addLineToSB(pushInfo, "media cached position", videoPlaybackState.getCachedPosition()); 569 | pushInfo.append("\n"); 570 | setResultToText(pushInfo.toString()); 571 | } 572 | } 573 | 574 | private void addLineToSB(StringBuffer sb, String name, Object value) { 575 | if (sb == null) return; 576 | sb. 577 | append((name == null || "".equals(name)) ? "" : name + ": "). 578 | append(value == null ? "" : value + ""). 579 | append("\n"); 580 | } 581 | 582 | private void downloadFileByIndex(final int index){ 583 | if ((mediaFileList.get(index).getMediaType() == MediaFile.MediaType.PANORAMA) 584 | || (mediaFileList.get(index).getMediaType() == MediaFile.MediaType.SHALLOW_FOCUS)) { 585 | return; 586 | } 587 | 588 | mediaFileList.get(index).fetchFileData(destDir, null, new DownloadListener() { 589 | @Override 590 | public void onFailure(DJIError error) { 591 | HideDownloadProgressDialog(); 592 | setResultToToast("Download File Failed" + error.getDescription()); 593 | currentProgress = -1; 594 | } 595 | 596 | @Override 597 | public void onProgress(long total, long current) { 598 | } 599 | 600 | @Override 601 | public void onRateUpdate(long total, long current, long persize) { 602 | int tmpProgress = (int) (1.0 * current / total * 100); 603 | if (tmpProgress != currentProgress) { 604 | mDownloadDialog.setProgress(tmpProgress); 605 | currentProgress = tmpProgress; 606 | } 607 | } 608 | 609 | @Override 610 | public void onRealtimeDataUpdate(byte[] bytes, long l, boolean b) { 611 | 612 | } 613 | 614 | @Override 615 | public void onStart() { 616 | currentProgress = -1; 617 | ShowDownloadProgressDialog(); 618 | } 619 | 620 | @Override 621 | public void onSuccess(String filePath) { 622 | HideDownloadProgressDialog(); 623 | setResultToToast("Download File Success" + ":" + filePath); 624 | currentProgress = -1; 625 | } 626 | }); 627 | } 628 | 629 | private void deleteFileByIndex(final int index) { 630 | ArrayList fileToDelete = new ArrayList(); 631 | if (mediaFileList.size() > index) { 632 | fileToDelete.add(mediaFileList.get(index)); 633 | mMediaManager.deleteFiles(fileToDelete, new CommonCallbacks.CompletionCallbackWithTwoParam, DJICameraError>() { 634 | @Override 635 | public void onSuccess(List x, DJICameraError y) { 636 | DJILog.e(TAG, "Delete file success"); 637 | runOnUiThread(new Runnable() { 638 | public void run() { 639 | MediaFile file = mediaFileList.remove(index); 640 | 641 | //Reset select view 642 | lastClickViewIndex = -1; 643 | lastClickView = null; 644 | 645 | //Update recyclerView 646 | mListAdapter.notifyItemRemoved(index); 647 | } 648 | }); 649 | } 650 | 651 | @Override 652 | public void onFailure(DJIError error) { 653 | setResultToToast("Delete file failed"); 654 | } 655 | }); 656 | } 657 | } 658 | 659 | private void playVideo() { 660 | mDisplayImageView.setVisibility(View.INVISIBLE); 661 | MediaFile selectedMediaFile = mediaFileList.get(lastClickViewIndex); 662 | if ((selectedMediaFile.getMediaType() == MediaFile.MediaType.MOV) || (selectedMediaFile.getMediaType() == MediaFile.MediaType.MP4)) { 663 | mMediaManager.playVideoMediaFile(selectedMediaFile, error -> { 664 | if (null != error) { 665 | setResultToToast("Play Video Failed " + error.getDescription()); 666 | } else { 667 | DJILog.e(TAG, "Play Video Success"); 668 | } 669 | }); 670 | } 671 | } 672 | 673 | private void moveToPosition(){ 674 | 675 | LayoutInflater li = LayoutInflater.from(this); 676 | View promptsView = li.inflate(R.layout.prompt_input_position, null); 677 | AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this); 678 | alertDialogBuilder.setView(promptsView); 679 | final EditText userInput = (EditText) promptsView.findViewById(R.id.editTextDialogUserInput); 680 | alertDialogBuilder.setCancelable(false).setPositiveButton("OK", (dialog, id) -> { 681 | String ms = userInput.getText().toString(); 682 | mMediaManager.moveToPosition(Integer.parseInt(ms), 683 | new CommonCallbacks.CompletionCallback() { 684 | @Override 685 | public void onResult(DJIError error) { 686 | if (null != error) { 687 | setResultToToast("Move to video position failed" + error.getDescription()); 688 | } else { 689 | DJILog.e(TAG, "Move to video position successfully."); 690 | } 691 | } 692 | }); 693 | }) 694 | .setNegativeButton("Cancel", (dialog, id) -> dialog.cancel()); 695 | AlertDialog alertDialog = alertDialogBuilder.create(); 696 | alertDialog.show(); 697 | 698 | } 699 | 700 | @Override 701 | public void onClick(View v) { 702 | switch (v.getId()) { 703 | case R.id.back_btn: { 704 | this.finish(); 705 | break; 706 | } 707 | case R.id.delete_btn:{ 708 | deleteFileByIndex(lastClickViewIndex); 709 | break; 710 | } 711 | case R.id.reload_btn: { 712 | getFileList(); 713 | break; 714 | } 715 | case R.id.download_btn: { 716 | downloadFileByIndex(lastClickViewIndex); 717 | break; 718 | } 719 | case R.id.status_btn: { 720 | if (mPushDrawerSd.isOpened()) { 721 | mPushDrawerSd.animateClose(); 722 | } else { 723 | mPushDrawerSd.animateOpen(); 724 | } 725 | break; 726 | } 727 | case R.id.play_btn: { 728 | playVideo(); 729 | break; 730 | } 731 | case R.id.resume_btn: { 732 | mMediaManager.resume(error -> { 733 | if (null != error) { 734 | setResultToToast("Resume Video Failed" + error.getDescription()); 735 | } else { 736 | DJILog.e(TAG, "Resume Video Success"); 737 | } 738 | }); 739 | break; 740 | } 741 | case R.id.pause_btn: { 742 | mMediaManager.pause(error -> { 743 | if (null != error) { 744 | setResultToToast("Pause Video Failed" + error.getDescription()); 745 | } else { 746 | DJILog.e(TAG, "Pause Video Success"); 747 | } 748 | }); 749 | break; 750 | } 751 | case R.id.stop_btn: { 752 | mMediaManager.stop(error -> { 753 | if (null != error) { 754 | setResultToToast("Stop Video Failed" + error.getDescription()); 755 | } else { 756 | DJILog.e(TAG, "Stop Video Success"); 757 | } 758 | }); 759 | break; 760 | } 761 | case R.id.moveTo_btn: { 762 | moveToPosition(); 763 | break; 764 | } 765 | default: 766 | break; 767 | } 768 | } 769 | 770 | private boolean isMavicAir2() { 771 | BaseProduct baseProduct = DemoApplication.getProductInstance(); 772 | if (baseProduct != null) { 773 | return baseProduct.getModel() == Model.MAVIC_AIR_2; 774 | } 775 | return false; 776 | } 777 | 778 | private boolean isAir2S() { 779 | BaseProduct baseProduct = DemoApplication.getProductInstance(); 780 | if (baseProduct != null) { 781 | return baseProduct.getModel() == Model.DJI_AIR_2S; 782 | } 783 | return false; 784 | } 785 | 786 | private boolean isM300() { 787 | BaseProduct baseProduct = DemoApplication.getProductInstance(); 788 | if (baseProduct != null) { 789 | return baseProduct.getModel() == Model.MATRICE_300_RTK; 790 | } 791 | return false; 792 | } 793 | } 794 | -------------------------------------------------------------------------------- /MediaManagerDemo/app/src/main/res/drawable/back_button_disable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DJI-Mobile-SDK-Tutorials/Android-MediaManagerDemo/5e27271e620145987002c843cdc77806b18f4570/MediaManagerDemo/app/src/main/res/drawable/back_button_disable.png -------------------------------------------------------------------------------- /MediaManagerDemo/app/src/main/res/drawable/back_button_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DJI-Mobile-SDK-Tutorials/Android-MediaManagerDemo/5e27271e620145987002c843cdc77806b18f4570/MediaManagerDemo/app/src/main/res/drawable/back_button_normal.png -------------------------------------------------------------------------------- /MediaManagerDemo/app/src/main/res/drawable/back_button_press.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DJI-Mobile-SDK-Tutorials/Android-MediaManagerDemo/5e27271e620145987002c843cdc77806b18f4570/MediaManagerDemo/app/src/main/res/drawable/back_button_press.png -------------------------------------------------------------------------------- /MediaManagerDemo/app/src/main/res/drawable/background_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /MediaManagerDemo/app/src/main/res/drawable/round_btn.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /MediaManagerDemo/app/src/main/res/drawable/round_btn_disable.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | -------------------------------------------------------------------------------- /MediaManagerDemo/app/src/main/res/drawable/round_btn_normal.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | -------------------------------------------------------------------------------- /MediaManagerDemo/app/src/main/res/drawable/round_btn_pressed.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | -------------------------------------------------------------------------------- /MediaManagerDemo/app/src/main/res/drawable/selector_back_button.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /MediaManagerDemo/app/src/main/res/layout/activity_connection.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 20 | 21 | 33 | 34 | 43 | 44 |