DJI Integrate SDK into Application freezes and does nothing

368 views Asked by At

I'm new to Android programming and I'm trying to follow the DJI "Integrate SDK into Application" tutorial for Android and have been struggling to get it going. I've been following the tutorial here: https://developer.dji.com/mobile-sdk/documentation/application-development-workflow/workflow-integrate.html#android-studio-project-integration

After making some tweaks to the code (the tutorial appears out of date), I can compile the project without errors and run it on my phone, but after I get the prompts for permissions, the app just freezes and does nothing instead of showing "Register Success" as per the tutorial. I've generated an app key and made sure the package name is the same as in my project. The key is pasted between the quotes at android:value="app key" in AndroidManifest.xml. I've tried a number of different sdk versions and build configurations but the app only occasionally says "registering, pls wait..." and never says "Register Success" or "Register sdk fails..." I'm at a loss and not sure what else to do. Any suggestions?

MainActivity.java


    package com.dji.importSDKDemo;
    
    import android.Manifest;
    import android.content.pm.PackageManager;
    import android.os.AsyncTask;
    import android.os.Build;
    import android.os.Bundle;
    import android.content.Intent;
    import android.os.Handler;
    import android.os.Looper;
    import android.util.Log;
    import android.widget.Toast;
    
    import androidx.annotation.NonNull;
    import androidx.appcompat.app.AppCompatActivity;
    import androidx.core.app.ActivityCompat;
    import androidx.core.content.ContextCompat;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.concurrent.atomic.AtomicBoolean;
    
    import dji.common.error.DJIError;
    import dji.common.error.DJISDKError;
    import dji.sdk.base.BaseComponent;
    import dji.sdk.base.BaseProduct;
    import dji.sdk.sdkmanager.DJISDKInitEvent;
    import dji.sdk.sdkmanager.DJISDKManager;
    
    public class MainActivity extends AppCompatActivity {
    
        private static final String TAG = MainActivity.class.getName();
        public static final String FLAG_CONNECTION_CHANGE = "dji_sdk_connection_change";
        private static BaseProduct mProduct;
        private Handler mHandler;
    
        private static final String[] REQUIRED_PERMISSION_LIST = new String[]{
                Manifest.permission.VIBRATE,
                Manifest.permission.INTERNET,
                Manifest.permission.ACCESS_WIFI_STATE,
                Manifest.permission.WAKE_LOCK,
                Manifest.permission.ACCESS_COARSE_LOCATION,
                Manifest.permission.ACCESS_NETWORK_STATE,
                Manifest.permission.ACCESS_FINE_LOCATION,
                Manifest.permission.CHANGE_WIFI_STATE,
                Manifest.permission.WRITE_EXTERNAL_STORAGE,
                Manifest.permission.BLUETOOTH,
                Manifest.permission.BLUETOOTH_ADMIN,
                Manifest.permission.READ_EXTERNAL_STORAGE,
                Manifest.permission.READ_PHONE_STATE,
        };
        private List<String> missingPermission = new ArrayList<>();
        private AtomicBoolean isRegistrationInProgress = new AtomicBoolean(false);
        private static final int REQUEST_PERMISSION_CODE = 12345;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    
            // When the compile and target version is higher than 22, please request the following permission at runtime to ensure the SDK works well.
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                checkAndRequestPermissions();
            }
    
            setContentView(R.layout.activity_main);
    
            //Initialize DJI SDK Manager
            mHandler = new Handler(Looper.getMainLooper());
    
        }
    
        /**
         * Checks if there is any missing permissions, and
         * requests runtime permission if needed.
         */
        private void checkAndRequestPermissions() {
            // Check for permissions
            for (String eachPermission : REQUIRED_PERMISSION_LIST) {
                if (ContextCompat.checkSelfPermission(this, eachPermission) != PackageManager.PERMISSION_GRANTED) {
                    missingPermission.add(eachPermission);
                }
            }
            // Request for missing permissions
            if (missingPermission.isEmpty()) {
                startSDKRegistration();
            } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                showToast("Need to grant the permissions!");
                ActivityCompat.requestPermissions(this,
                        missingPermission.toArray(new String[missingPermission.size()]),
                        REQUEST_PERMISSION_CODE);
            }
    
        }
    
        /**
         * Result of runtime permission request
         */
        @Override
        public void onRequestPermissionsResult(int requestCode,
                                               @NonNull String[] permissions,
                                               @NonNull int[] grantResults) {
            super.onRequestPermissionsResult(requestCode, permissions, grantResults);
            // Check for granted permission and remove from missing list
            if (requestCode == REQUEST_PERMISSION_CODE) {
                for (int i = grantResults.length - 1; i >= 0; i--) {
                    if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
                        missingPermission.remove(permissions[i]);
                    }
                }
            }
            // If there is enough permission, we will start the registration
            if (missingPermission.isEmpty()) {
                startSDKRegistration();
            } else {
                showToast("Missing permissions!!!");
            }
        }
    
        private void startSDKRegistration() {
            if (isRegistrationInProgress.compareAndSet(false, true)) {
                AsyncTask.execute(new Runnable() {
                    @Override
                    public void run() {
                        showToast("registering, pls wait...");
    
                        DJISDKManager.getInstance().registerApp(MainActivity.this.getApplicationContext(), new DJISDKManager.SDKManagerCallback() {
                            @Override
                            public void onRegister(DJIError djiError) {
                                if (djiError == DJISDKError.REGISTRATION_SUCCESS) {
                                    showToast("Register Success");
                                    DJISDKManager.getInstance().startConnectionToProduct();
                                } else {
                                    showToast("Register sdk fails, please check the bundle id and network connection!");
                                }
                                Log.v(TAG, djiError.getDescription());
                            }
    
                            @Override
                            public void onProductDisconnect() {
                                Log.d(TAG, "onProductDisconnect");
                                showToast("Product Disconnected");
                                notifyStatusChange();
    
                            }
                            @Override
                            public void onProductConnect(BaseProduct baseProduct) {
                                Log.d(TAG, String.format("onProductConnect newProduct:%s", baseProduct));
                                showToast("Product Connected");
                                notifyStatusChange();
    
                            }
    
    
                            /*
                            @Override
                            public void onProductChanged(BaseProduct baseProduct) {
    
                            }
                            */
    
    
    
                            @Override
                            public void onComponentChange(BaseProduct.ComponentKey componentKey, BaseComponent oldComponent,
                                                          BaseComponent newComponent) {
    
                                if (newComponent != null) {
                                    newComponent.setComponentListener(new BaseComponent.ComponentListener() {
    
                                        @Override
                                        public void onConnectivityChange(boolean isConnected) {
                                            Log.d(TAG, "onComponentConnectivityChanged: " + isConnected);
                                            notifyStatusChange();
                                        }
                                    });
                                }
                                Log.d(TAG,
                                        String.format("onComponentChange key:%s, oldComponent:%s, newComponent:%s",
                                                componentKey,
                                                oldComponent,
                                                newComponent));
    
                            }
    
                            @Override
                            public void onInitProcess(DJISDKInitEvent djisdkInitEvent, int i) {
    
                            }
    
                            @Override
                            public void onDatabaseDownloadProgress(long l, long l1) {
    
                            }
    
                        });
                    }
                });
            }
        }
    
        private void notifyStatusChange() {
            mHandler.removeCallbacks(updateRunnable);
            mHandler.postDelayed(updateRunnable, 500);
        }
    
        private Runnable updateRunnable = new Runnable() {
    
            @Override
            public void run() {
                Intent intent = new Intent(FLAG_CONNECTION_CHANGE);
                sendBroadcast(intent);
            }
        };
    
        private void showToast(final String toastMsg) {
    
            Handler handler = new Handler(Looper.getMainLooper());
            handler.post(new Runnable() {
                @Override
                public void run() {
                    Toast.makeText(getApplicationContext(), toastMsg, Toast.LENGTH_LONG).show();
                }
            });
    
        }
    
    }

AndroidManifest.xml '''

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.dji.importSDKDemo">

    <!-- Permissions and features -->
    <uses-permission android:name="android.permission.BLUETOOTH" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
    <uses-permission android:name="android.permission.VIBRATE" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />

    <uses-feature android:name="android.hardware.camera" />
    <uses-feature android:name="android.hardware.camera.autofocus" />
    <uses-feature
        android:name="android.hardware.usb.host"
        android:required="false" />
    <uses-feature
        android:name="android.hardware.usb.accessory"
        android:required="true" />

    <!-- Permissions and features -->

    <application
        android:name=".MApplication"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme" >

        <!-- DJI SDK -->
        <uses-library android:name="com.android.future.usb.accessory" />
        <uses-library android:name="org.apache.http.legacy" android:required="false" />
        <meta-data
            android:name="com.dji.sdk.API_KEY"
            android:value="f7d...my app code is pasted here..." />

        <!-- DJI SDK -->

        <activity android:name=".MainActivity"
            android:configChanges="orientation"
            android:screenOrientation="portrait">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>

            <intent-filter>
                <action android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" />
            </intent-filter>
            <meta-data
                android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED"
                android:resource="@xml/accessory_filter" />
        </activity>
    </application>

</manifest>

activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.dji.importSDKDemo.MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>
build.gradle
apply plugin: 'com.android.application'

android {
    compileSdkVersion 29
    buildToolsVersion '30.0.2'

    defaultConfig {
        applicationId "com.dji.importSDKDemo"
        minSdkVersion 29
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }

    packagingOptions{
        doNotStrip "*/*/libdjivideo.so"
        doNotStrip "*/*/libSDKRelativeJNI.so"
        doNotStrip "*/*/libFlyForbid.so"
        doNotStrip "*/*/libduml_vision_bokeh.so"
        doNotStrip "*/*/libyuv2.so"
        doNotStrip "*/*/libGroudStation.so"
        doNotStrip "*/*/libFRCorkscrew.so"
        doNotStrip "*/*/libUpgradeVerify.so"
        doNotStrip "*/*/libFR.so"
        doNotStrip "*/*/libDJIFlySafeCore.so"
        doNotStrip "*/*/libdjifs_jni.so"
        doNotStrip "*/*/libsfjni.so"
        exclude 'META-INF/rxjava.properties'
    }
}

dependencies {
    implementation fileTree(dir: "libs", include: ["*.jar"])
    implementation 'androidx.appcompat:appcompat:1.2.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.0.1'
    testImplementation 'junit:junit:4.13'
    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
    implementation ('com.dji:dji-sdk:4.12')
    compileOnly ('com.dji:dji-sdk-provided:4.12')

}



2

There are 2 answers

0
Geir Opdahl On

I seem to have solved it. I hadn't used the proguard-rules.pro file supplied in the sample code and once I used that, it started working. I don't know anything about proguard and there was nothing in the tutorials about it but I guess it was necessary.

0
IncorrectMethod On

DJI has a strict requirement to use minSdkVersion 19. Without this, the SDK packages will not download and the SDK registration will hang. Additionally the IDE won't even report errors. I suggest using the code examples from the dji dev GitHub as the tutorial is outdated.