大疆文档(4)-Android教程-应用程序激活和飞机绑定

本节全篇为大疆 Mobile SDK 安卓教程 部分,ios教程参见 IOS教程 .

应用程序激活和飞机绑定

Note:本教程仅适用于在 中国 使用的应用程序。相同的步骤可用于在现有应用中激活应用和绑定飞机。

你可以在这里下载本教程的最终示例项目 Github Page.

介绍

如果该应用程序在中国使用,DJI飞机固件要求控制DJI飞机的移动应用程序使用用户的DJI账户激活。这将确保操作人员根据其地理位置和用户档案为其飞机使用正确的地理空间信息和飞行功能。激活系统概述如下:

  • 中国用户必须每三个月至少登录一次自己的DJI账号来激活自己的应用程序。
  • 激活将在应用程序中持久存在,直到用户注销。
  • 登录DJI帐户需要网络连接。
  • 在中国之外,SDK将自动激活应用程序,而无需用户登录。
  • 此外,中国用户需要在 DJI Go / DJI Go 4 中绑定他们的飞机到用户账户。这只需要一次。如果没有激活应用程序,没有绑定飞机(如果需要的话),或使用旧版本的SDK(< 4.1), 则所有 相机实时流禁用 ,并且飞行将限制为直径100米的区域和30米高度,以确保飞机保持在视线范围内。

应用程序激活

现在,让我们在Android Studio中创建一个新项目,打开Android Studio并选择 File -> New -> New Project 来创建一个名为 ActivationDemo 的新项目。

  1. 创建一个名为 ActivationDemo 的新项目
  2. 输入域名和包名(这里我们使用“com.dji.activationDemo”)
  3. Phone and Tablet 下设置最低SDK版本为 API 18: Android 4.3 (Jelly Bean)
  4. 选择 "Empty Activity"
  5. 将Activity Name保留为“MainActivity”,将 Layout Name保留为“activity_main”,完成

注册应用程序

这个demo基于 ImportSDKDemo Github Sample 构建,你可以查看 Integrate SDK into Application 教程,了解如何导入Android SDK Maven依赖项,和使用DJI Mobile SDK注册应用程序。

实现应用程序的UI

使用 MApplication,DemoApplication和ConnectionActivity

有关详细实现,请查看 Creating an Camera Application 教程和本教程的 sample project

使用MainActivity类

MainActivity.java 文件默认由Android Studio创建。让我们用以下内容替换它的代码:

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private static final String TAG = MainActivity.class.getName();
    protected Button loginBtn;
    protected Button logoutBtn;
    protected TextView bindingStateTV;
    protected TextView appActivationStateTV;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // When the compile and target version is higher than 22, please request the
        // following permissions at runtime to ensure the
        // SDK work well.
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            ActivityCompat.requestPermissions(this,
                    new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, 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.MOUNT_UNMOUNT_FILESYSTEMS,
                            Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.SYSTEM_ALERT_WINDOW,
                            Manifest.permission.READ_PHONE_STATE,
                    }
                    , 1);
        }

        setContentView(R.layout.activity_main);
        initUI();
    }

    @Override
    public void onResume() {
        Log.e(TAG, "onResume");
        super.onResume();
    }

    @Override
    public void onPause() {
        Log.e(TAG, "onPause");
        super.onPause();
    }

    @Override
    public void onStop() {
        Log.e(TAG, "onStop");
        super.onStop();
    }

    public void onReturn(View view){
        Log.e(TAG, "onReturn");
        this.finish();
    }

    @Override
    protected void onDestroy() {
        Log.e(TAG, "onDestroy");
        super.onDestroy();
    }

    private void initUI(){

        bindingStateTV = (TextView) findViewById(R.id.tv_binding_state_info);
        appActivationStateTV = (TextView) findViewById(R.id.tv_activation_state_info);
        loginBtn = (Button) findViewById(R.id.btn_login);
        logoutBtn = (Button) findViewById(R.id.btn_logout);
        loginBtn.setOnClickListener(this);
        logoutBtn.setOnClickListener(this);

    }

    @Override
    public void onClick(View v) {

        switch (v.getId()) {
            case R.id.btn_login:{
                break;
            }
            case R.id.btn_logout:{
                break;
            }
            default:
                break;
        }
    }
}

在以上代码中,我们实现了以下功能:

  1. 创建布局UI元素变量,包括两个文本布局 bindingStateTV and appActivationStateTV,两个按钮loginBtnlogoutBtn
  2. 然后调用 initUI() 方法初始化UI变量。并为所有按钮实现按钮的setOnClickListener()方法。
  3. 重写 onClick() 方法以实现3个按钮的单击事件。
使用MainActivity布局

打开 activity_main.xml 布局文件,并使用以下代码替换:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
        android:id="@+id/binding_state_label"
        android:layout_width="wrap_content"
        android:layout_height="60dp"
        android:layout_below="@+id/textView"
        android:layout_centerHorizontal="true"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="50dp"
        android:gravity="center"
        android:text="Binding State:"
        android:textColor="@color/black"
        android:textSize="17sp"
        android:textStyle="bold" />

    <TextView
        android:id="@+id/tv_binding_state_info"
        android:layout_width="220dp"
        android:layout_height="60dp"
        android:layout_below="@+id/binding_state_label"
        android:layout_centerHorizontal="true"
        android:layout_gravity="center_horizontal"
        android:gravity="center"
        android:text="Unknown"
        android:textColor="@color/black"
        android:textSize="17sp" />

    <Button
        android:id="@+id/btn_login"
        style="@style/common_button"
        android:layout_alignParentBottom="true"
        android:layout_alignStart="@+id/tv_activation_state_info"
        android:layout_gravity="center_horizontal"
        android:layout_marginBottom="79dp"
        android:text="Login" />

    <Button
        android:id="@+id/btn_logout"
        style="@style/common_button"
        android:layout_alignParentBottom="true"
        android:layout_alignEnd="@+id/tv_activation_state_info"
        android:layout_gravity="center_horizontal"
        android:layout_marginBottom="79dp"
        android:text="Logout" />

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="30dp"
        android:text="Application Activation Demo"
        android:textSize="17sp"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true" />

    <TextView
        android:id="@+id/activation_state_label"
        android:layout_width="wrap_content"
        android:layout_height="60dp"
        android:layout_below="@+id/tv_binding_state_info"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="11dp"
        android:text="App Activation State:"
        android:textColor="@color/black"
        android:textSize="17sp"
        android:textStyle="bold" />

    <TextView
        android:id="@+id/tv_activation_state_info"
        android:layout_width="220dp"
        android:layout_height="60dp"
        android:layout_alignEnd="@+id/tv_binding_state_info"
        android:layout_below="@+id/activation_state_label"
        android:text="Unknown"
        android:textAlignment="center"
        android:textColor="@color/black"
        android:textSize="17sp" />

</RelativeLayout>

在xml文件中,我们实现以下内容:

  1. 我们实现该 RelativeLayout 元素,然后声明两个 TextView 元素:(id:tv_binding_state_info) 和 (id:tv_activation_state_info) 以显示激活和绑定状态信息。对于其他三个 TextView 元素,我们将它们声明为labels, 用以显示标题。
  2. 然后,我们创建 "Login" 按钮(id: btn_login) 和 "Logout" 按钮(id: btn_logout)。

有关布局的更多详细配置,请查看教程的Github示例项目中的 activity_main.xml 文件。

配置Resources

完成上述步骤后,让我们在Android Studio左侧导航器将一些资源文件添加到 res 文件夹中。

将以下图像和xml文件从教程Github Sample项目的drawable文件夹复制到项目中,它们用于按钮的UI:

从教程Github Sample项目的 drawable 文件夹复制以下图片和xml文件到你的项目中,它们用于按钮的UI:

img

接下来,打开 "colors.xml" 文件并在底部添加以下代码以声明黑色:

<color name="colorBlack">#ff000000</color>
<color name="colorWhite">#FFFFFF</color>

此外,打开 "strings.xml" 文件并替换为以下内容:

<resources>
    <string name="app_name">ActivationDemo</string>
    <string name="title_activity_connection">ConnectionActivity</string>
    <string name="action_settings">Settings</string>
    <string name="disconnected">Disconnected</string>
    <string name="product_information">Product Information</string>
    <string name="connection_loose">Status: No Product Connected</string>
    <string name="sdk_version">DJI SDK Version: %1$s</string>
</resources>

这里,我们定义了字符串元素用于在 ConnectionActivity 布局中显示文本。

此外,打开 "styles.xml" 文件并替换为以下内容:

<resources>
    <!-- Base application theme. -->
    <style name="AppTheme" parent="@style/Theme.AppCompat.Light.NoActionBar">
    </style>

    <!-- Common button style -->
    <style name="common_button">
        <item name="android:layout_width">100dp</item>
        <item name="android:layout_height">45dp</item>
        <item name="android:layout_marginTop">10dp</item>
        <item name="android:background">@drawable/round_btn</item>
        <item name="android:paddingLeft">5dp</item>
        <item name="android:paddingRight">5dp</item>
        <item name="android:textAllCaps">false</item>
        <item name="android:textColor">@android:color/white</item>
        <item name="android:textSize">14sp</item>
    </style>
</resources>

在上面的代码中,我们改变了 AppTheme 样式并定义了一个通用按钮样式。

现在,如果你检查 activity_main.xml 文件,可以看到如下所示的MainActivity的预览屏幕截图,:

img

使用AppActivationManager

为了获取更新的应用程序激活状态和飞机绑定状态,我们可以使用 AppActivationManager 来添加监听器来获取这个信息。

现在,让我们打开 "MainActivity.java" 文件并创建一个 AppActivationManager 变量appActivationManager,一个 AppActivationStateListener 监听器activationStateListener 和一个 AircraftBindingStateListener 监听器bindingStateListener

private AppActivationManager appActivationManager;
private AppActivationState.AppActivationStateListener activationStateListener;
private AircraftBindingState.AircraftBindingStateListener bindingStateListener;

接下来,创建一个如下所示的 initData() 方法,并在 onCreate() 方法的底部调用它:

private void initData(){
    setUpListener();

    appActivationManager = DJISDKManager.getInstance().getAppActivationManager();

    if (appActivationManager != null) {
        appActivationManager.addAppActivationStateListener(activationStateListener);
        appActivationManager.addAircraftBindingStateListener(bindingStateListener);
        MainActivity.this.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                appActivationStateTV.setText("" + appActivationManager.getAppActivationState());
                bindingStateTV.setText("" + appActivationManager.getAircraftBindingState());
            }
        });
    }
}

在以上代码中,我们实现了以下功能:

  1. 我们通过调用 DJISDKManagergetAppActivationManager 方法来初始化 appActivationManager 变量。
  2. 然后我们检查 appActivationManager 变量是否存在并调用 addAppActivationStateListener() 方法并将 activationStateListener 监听器作为参数传递,以便为 应用程序激活状态更新 添加监听器。同样,我们调用该 addAircraftBindingStateListener() 方法并将 bindingStateListener 监听器作为参数,以便为 飞机绑定状态更新 添加监听器。
  3. 最后,我们调用 appActivationManagergetAppActivationState() 方法来获取当前的应用程序激活状态,并更新 appActivationStateTV 的文本值。同样,我们调用 appActivationManagergetAircraftBindingState() 方法来获取当前飞机绑定状态并更新 bindingStateTV的文本值。

完成上述步骤后,让我们再添加另外两个方法: setUpListenertearDownListener 来设置监听器,并在 onResume()onDestroy() 方法中调用它们,如下所示:

private void setUpListener() {
    // Example of Listener
    activationStateListener = new AppActivationState.AppActivationStateListener() {
        @Override
        public void onUpdate(final AppActivationState appActivationState) {
            MainActivity.this.runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    appActivationStateTV.setText("" + appActivationState);
                }
            });
        }
    };

    bindingStateListener = new AircraftBindingState.AircraftBindingStateListener() {

        @Override
        public void onUpdate(final AircraftBindingState bindingState) {
            MainActivity.this.runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    bindingStateTV.setText("" + bindingState);
                }
            });
        }
    };
}

private void tearDownListener() {
    if (activationStateListener != null) {
        appActivationManager.removeAppActivationStateListener(activationStateListener);
        MainActivity.this.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                appActivationStateTV.setText("Unknown");
            }
        });
    }
    if (bindingStateListener !=null) {
        appActivationManager.removeAircraftBindingStateListener(bindingStateListener);
        MainActivity.this.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                bindingStateTV.setText("Unknown");
            }
        });
    }
}

@Override
public void onResume() {
    Log.e(TAG, "onResume");
    setUpListener();
    super.onResume();
}

@Override
protected void onDestroy() {
    Log.e(TAG, "onDestroy");
    tearDownListener();
    super.onDestroy();
}

在这里,我们实现了以下功能:

  1. setUpListener() 方法中,我们初始化 activationStateListenerbindingStateListener 监听器并重写它们的 onUpdate() 方法以获取更新的 AppActivationStateAircraftBindingState 枚举值。然后用它们去更新 appActivationStateTVbindingStateTV 文本视图中的文本值。
  2. 接下来,由于我们在 appActivationManager 中为应用程序激活状态和飞机绑定状态更新添加了监听器,我们还需要删除它们。因此,在 tearDownListener() 方法中,我们调用了 appActivationManager 中的 removeAppActivationStateListenerremoveAircraftBindingStateListener 方法来删除监听器。此外,更新 appActivationStateTVbindingStateTV 文本视图中的的文本值更新为 "Unknown"。
  3. 最后,重写 onResume() 方法并调用 setUpListener() 以设置监听器。然后重写 onDestroy() 方法并调用 tearDownListener() 方法以删除 appActivationManager 中的监听器。

使用登录和注销DJI用户帐户

为了激活应用程序,我们需要登录DJI用户帐户。现在让我们创建以下方法并在 onClick() 方法中调用它们:

private void loginAccount(){

    UserAccountManager.getInstance().logIntoDJIUserAccount(this,
            new CommonCallbacks.CompletionCallbackWith<UserAccountState>() {
                @Override
                public void onSuccess(final UserAccountState userAccountState) {
                    showToast("Login Success");
                }
                @Override
                public void onFailure(DJIError error) {
                    showToast("Login Error:"
                            + error.getDescription());
                }
            });

}

private void logoutAccount(){
    UserAccountManager.getInstance().logoutOfDJIUserAccount(new CommonCallbacks.CompletionCallback() {
        @Override
        public void onResult(DJIError error) {
            if (null == error) {
                showToast("Logout Success");
            } else {
                showToast("Logout Error:"
                        + error.getDescription());
            }
        }
    });
}

@Override
public void onClick(View v) {

    switch (v.getId()) {
        case R.id.btn_login:{
            loginAccount();
            break;
        }
        case R.id.btn_logout:{
            logoutAccount();
            break;
        }
        default:
            break;
    }
}

在上面的代码中,我们实现了以下内容:

  1. loginAccount() 方法中,我们调用了 UserAccountManager 中的 logIntoDJIUserAccount() 方法来显示登录对话框。并重写了 onSuccess() and onFailure() 方法,通过调用 showToast() 方法用来展示结果消息。
  2. 接下来,在 logoutAccount() 方法中,我们调用 UserAccountManagerlogoutOfDJIUserAccount() 方法来注销DJI用户帐户。此外,重写 onResult() 方法以显示结果消息。

有关更多实现细节,请查看本教程的Github示例项目。

现在,让我们构建并运行项目,将demo程序连接到你的飞机(详情请见 Run Application )并检查我们到目前为止已实现的功能。

以下是激活应用程序的步骤:

  1. 如果未激活应用程序,您可能会看到以下截图:

    img
  2. 现在,让我们按 Login 按钮并填写你的email和密码以登录并激活该应用程序:

    img
  3. 如果一切ok,你会看到 appActivationStateTV 的文本值变更为 "Activated":

img

Note: 这里的教程有点逻辑问题,应避免完全按照教程,需要在上一篇教程的代码基础上,注册成功后再调用 initData() 方法进行appActivationManager的初始化操作,否则管理类为NULL,其他监听类都会失效。以下为MainActivity类的完整代码:

package com.dji.activationDemo;

import android.Manifest;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

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.common.realname.AircraftBindingState;
import dji.common.realname.AppActivationState;
import dji.common.useraccount.UserAccountState;
import dji.common.util.CommonCallbacks;
import dji.sdk.base.BaseComponent;
import dji.sdk.base.BaseProduct;
import dji.sdk.realname.AppActivationManager;
import dji.sdk.sdkmanager.DJISDKManager;
import dji.sdk.useraccount.UserAccountManager;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private static final String TAG = MainActivity.class.getName();
    public static final String FLAG_CONNECTION_CHANGE = "dji_sdk_connection_change";
    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;


    protected Button loginBtn;
    protected Button logoutBtn;
    protected TextView bindingStateTV;
    protected TextView appActivationStateTV;


    private AppActivationManager appActivationManager;
    private AppActivationState.AppActivationStateListener activationStateListener;
    private AircraftBindingState.AircraftBindingStateListener bindingStateListener;

    @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) {// more than Android 6.0
            checkAndRequestPermissions();
        }

        setContentView(R.layout.activity_main);
        initUI();
        //Initialize DJI SDK Manager
        mHandler = new Handler(Looper.getMainLooper());


//        initData();
    }


    /**
     * 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!");
            // start request permission if target version is higher than android 6.0 and missing permission not empty
            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!!!");
        }
    }

    // start registration if not missing permission
    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();

                                initData();

                            } 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 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));

                        }
                    });
                }
            });
        }
    }

    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);
        }
    };

    @Override
    public void onResume() {
        Log.e(TAG, "onResume");
        setUpListener();
        super.onResume();
    }

    @Override
    public void onPause() {
        Log.e(TAG, "onPause");
        super.onPause();
    }

    @Override
    public void onStop() {
        Log.e(TAG, "onStop");
        super.onStop();
    }

    public void onReturn(View view){
        Log.e(TAG, "onReturn");
        this.finish();
    }

    @Override
    protected void onDestroy() {
        Log.e(TAG, "onDestroy");
        tearDownListener();
        super.onDestroy();
    }

    private void initUI(){

        bindingStateTV = (TextView) findViewById(R.id.tv_binding_state_info);
        appActivationStateTV = (TextView) findViewById(R.id.tv_activation_state_info);
        loginBtn = (Button) findViewById(R.id.btn_login);
        logoutBtn = (Button) findViewById(R.id.btn_logout);
        loginBtn.setOnClickListener(this);
        logoutBtn.setOnClickListener(this);

    }

    private void initData(){

        setUpListener();

        appActivationManager = DJISDKManager.getInstance().getAppActivationManager();
        Log.e(TAG, "ManagerIsNULL: "+appActivationManager);

        if (appActivationManager != null) {
            appActivationManager.addAppActivationStateListener(activationStateListener);
            appActivationManager.addAircraftBindingStateListener(bindingStateListener);
            MainActivity.this.runOnUiThread(new Runnable() {
                @SuppressLint("SetTextI18n")
                @Override
                public void run() {
                    appActivationStateTV.setText("" + appActivationManager.getAppActivationState());
                    bindingStateTV.setText("" + appActivationManager.getAircraftBindingState());
                }
            });
        }
    }

    private void setUpListener() {
        // Example of Listener
        activationStateListener = new AppActivationState.AppActivationStateListener() {
            @Override
            public void onUpdate(final AppActivationState appActivationState) {
                MainActivity.this.runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        appActivationStateTV.setText("setUpListener:" + appActivationState);
                    }
                });
            }
        };

        bindingStateListener = new AircraftBindingState.AircraftBindingStateListener() {

            @Override
            public void onUpdate(final AircraftBindingState bindingState) {
                MainActivity.this.runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        bindingStateTV.setText("setUpListener:" + bindingState);
                    }
                });
            }
        };
    }

    private void tearDownListener() {
        if (activationStateListener != null) {
            appActivationManager.removeAppActivationStateListener(activationStateListener);
            MainActivity.this.runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    appActivationStateTV.setText("tearDownListener Unknown");
                }
            });
        }
        if (bindingStateListener !=null) {
            appActivationManager.removeAircraftBindingStateListener(bindingStateListener);
            MainActivity.this.runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    bindingStateTV.setText("tearDownListener Unknown");
                }
            });
        }
    }

    @Override
    public void onClick(View v) {

        switch (v.getId()) {
            case R.id.btn_login:{
                loginAccount();
                break;
            }
            case R.id.btn_logout:{
                logoutAccount();
                break;
            }
            default:
                break;
        }
    }

    private void loginAccount(){

        UserAccountManager.getInstance().logIntoDJIUserAccount(this,
                new CommonCallbacks.CompletionCallbackWith<UserAccountState>() {
                    @Override
                    public void onSuccess(final UserAccountState userAccountState) {
                        showToast("Login Success");
                    }
                    @Override
                    public void onFailure(DJIError error) {
                        showToast("Login Error:"
                                + error.getDescription());
                    }
                });

    }

    private void logoutAccount(){
        UserAccountManager.getInstance().logoutOfDJIUserAccount(new CommonCallbacks.CompletionCallback() {
            @Override
            public void onResult(DJIError error) {
                if (null == error) {
                    showToast("Logout Success");
                } else {
                    showToast("Logout Error:"
                            + error.getDescription());
                }
            }
        });
    }

    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();
            }
        });

        Log.e(TAG, toastMsg + "");

    }
}

飞机绑定

完成激活应用程序后,你现在可以将你的飞机连接到 DJI Go 4/DJI Go app并把你的飞机绑定到你的DJI帐户。这只需要一次。如果按 Enter Device 按钮,您可能会看到警告框,如下所示:

img

现在,按 Link 按钮完成手机链接和验证过程。之后,将飞机连接回应用程序,你将会看到 bindingStateTV的文本值已更改为 "Bound" ,如下所示:

img

恭喜!现在,你的移动应用程序和飞机可以毫无问题地在中国使用。换句话说,你的应用程序现在可以看到飞机的相机直播流,并且飞行不会局限于直径100米和高30米的圆柱体范围内。

概要

在本教程中,你已经学习如何使用DJI Mobile SDK激活SDK移动应用程序,并使用DJI Go app将飞机绑定到你的DJI 账户。同样的步骤可用于激活应用程序和绑定应用程序中的飞机。希望您喜欢本教程,并继续关注我们的下一个教程!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345

推荐阅读更多精彩内容