本节全篇为大疆 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
的新项目。
- 创建一个名为
ActivationDemo
的新项目 - 输入域名和包名(这里我们使用“com.dji.activationDemo”)
- 在
Phone and Tablet
下设置最低SDK版本为API 18: Android 4.3 (Jelly Bean)
- 选择 "Empty Activity"
- 将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;
}
}
}
在以上代码中,我们实现了以下功能:
- 创建布局UI元素变量,包括两个文本布局
bindingStateTV
andappActivationStateTV
,两个按钮loginBtn
,logoutBtn
。 - 然后调用
initUI()
方法初始化UI变量。并为所有按钮实现按钮的setOnClickListener()
方法。 - 重写
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文件中,我们实现以下内容:
- 我们实现该
RelativeLayout
元素,然后声明两个TextView
元素:(id:tv_binding_state_info) 和 (id:tv_activation_state_info) 以显示激活和绑定状态信息。对于其他三个TextView
元素,我们将它们声明为labels, 用以显示标题。 - 然后,我们创建 "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:
接下来,打开 "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的预览屏幕截图,:
使用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());
}
});
}
}
在以上代码中,我们实现了以下功能:
- 我们通过调用
DJISDKManager
的getAppActivationManager
方法来初始化appActivationManager
变量。 - 然后我们检查
appActivationManager
变量是否存在并调用addAppActivationStateListener()
方法并将activationStateListener
监听器作为参数传递,以便为 应用程序激活状态更新 添加监听器。同样,我们调用该addAircraftBindingStateListener()
方法并将bindingStateListener
监听器作为参数,以便为 飞机绑定状态更新 添加监听器。 - 最后,我们调用
appActivationManager
的getAppActivationState()
方法来获取当前的应用程序激活状态,并更新appActivationStateTV
的文本值。同样,我们调用appActivationManager
的getAircraftBindingState()
方法来获取当前飞机绑定状态并更新bindingStateTV
的文本值。
完成上述步骤后,让我们再添加另外两个方法: setUpListener
和 tearDownListener
来设置监听器,并在 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();
}
在这里,我们实现了以下功能:
- 在
setUpListener()
方法中,我们初始化activationStateListener
和bindingStateListener
监听器并重写它们的onUpdate()
方法以获取更新的AppActivationState
和AircraftBindingState
枚举值。然后用它们去更新appActivationStateTV
和bindingStateTV
文本视图中的文本值。 - 接下来,由于我们在
appActivationManager
中为应用程序激活状态和飞机绑定状态更新添加了监听器,我们还需要删除它们。因此,在tearDownListener()
方法中,我们调用了appActivationManager
中的removeAppActivationStateListener
和removeAircraftBindingStateListener
方法来删除监听器。此外,更新appActivationStateTV
和bindingStateTV
文本视图中的的文本值更新为 "Unknown"。 - 最后,重写
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;
}
}
在上面的代码中,我们实现了以下内容:
- 在
loginAccount()
方法中,我们调用了UserAccountManager
中的logIntoDJIUserAccount()
方法来显示登录对话框。并重写了onSuccess()
andonFailure()
方法,通过调用showToast()
方法用来展示结果消息。 - 接下来,在
logoutAccount()
方法中,我们调用UserAccountManager
的logoutOfDJIUserAccount()
方法来注销DJI用户帐户。此外,重写onResult()
方法以显示结果消息。
有关更多实现细节,请查看本教程的Github示例项目。
现在,让我们构建并运行项目,将demo程序连接到你的飞机(详情请见 Run Application )并检查我们到目前为止已实现的功能。
以下是激活应用程序的步骤:
-
如果未激活应用程序,您可能会看到以下截图:
-
现在,让我们按 Login 按钮并填写你的email和密码以登录并激活该应用程序:
如果一切ok,你会看到
appActivationStateTV
的文本值变更为 "Activated":
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 按钮,您可能会看到警告框,如下所示:
现在,按 Link 按钮完成手机链接和验证过程。之后,将飞机连接回应用程序,你将会看到 bindingStateTV
的文本值已更改为 "Bound" ,如下所示:
恭喜!现在,你的移动应用程序和飞机可以毫无问题地在中国使用。换句话说,你的应用程序现在可以看到飞机的相机直播流,并且飞行不会局限于直径100米和高30米的圆柱体范围内。
概要
在本教程中,你已经学习如何使用DJI Mobile SDK激活SDK移动应用程序,并使用DJI Go app将飞机绑定到你的DJI 账户。同样的步骤可用于激活应用程序和绑定应用程序中的飞机。希望您喜欢本教程,并继续关注我们的下一个教程!