最近几天我已经将 DJI SDK 从 4.3.2 更新到 4.4.1。我试图让它工作,但不知何故,当我连接无人机的 Remote 时,它无法识别。
使用的硬件/软件:
智能手机:Google Pixel XL
智能手机操作系统 Android 8.1.0
无人机: DJI Phantom 4 Pro
DJI SDK:4.4.1
请注意,由于其他依赖项,我需要使其在 API 级别 >=26 下工作,我不能低于该级别。
build.gradle
apply plugin: 'com.android.application'
repositories {
maven { url 'https://maven.google.com' }
}
android {
compileSdkVersion 26
buildToolsVersion '26.0.2'
defaultConfig {
applicationId "com.test.droneapp"
minSdkVersion 19
targetSdkVersion 26
versionCode 9
versionName "1.0"
//Enabling multidex support
multiDexEnabled true
vectorDrawables.useSupportLibrary = true
useLibrary 'org.apache.http.legacy'
ndk {
abiFilters 'armeabi-v7a', 'x86', 'arm64-v8a'
}
}
lintOptions {
disable 'MissingTranslation'
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
dexOptions {
preDexLibraries = false
javaMaxHeapSize "4g"
}
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"
}
}
repositories {
flatDir {
dirs 'libs'
}
google()
}
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
// Support Library
compile "com.android.support:support-v4:26.1.0"
compile "com.android.support:recyclerview-v7:26.1.0"
compile "com.android.support:appcompat-v7:26.1.0"
compile "com.android.support:design:26.1.0"
// Google Play Services
compile "com.google.android.gms:play-services-maps:11.6.2"
compile "com.google.android.gms:play-services-auth:11.6.2"
compile "com.google.android.gms:play-services-gcm:11.6.2"
compile "com.google.android.gms:play-services-location:11.6.2"
// MultiDex
compile 'com.android.support:multidex:1.0.1'
// DJI SDK
// compile ('com.dji:dji-sdk:4.3.2')
compile 'com.dji:dji-sdk:4.4.1'
provided 'com.dji:dji-sdk-provided:4.4.1'
}
LauncherApplication.java
package com.test.droneapp;
import android.app.Application;
import android.content.Context;
import android.os.Environment;
import android.support.multidex.MultiDex;
import com.crashlytics.android.Crashlytics;
import com.secneo.sdk.Helper;
import java.io.File;
import io.fabric.sdk.android.Fabric;
public class LauncherApplication extends Application {
private DJIApplication mDJIApplication;
@Override
protected void attachBaseContext(Context paramContext) {
super.attachBaseContext(paramContext);
MultiDex.install(this);
Helper.install(this);
if (mDJIApplication == null) {
mDJIApplication = new DJIApplication();
mDJIApplication.setContext(this);
}
}
@Override
public void onCreate() {
super.onCreate();
mDJIApplication.onCreate();
// Initialize Fabric
Fabric.with(this, new Crashlytics());
}
}
DJIApplication.java
package com.test.droneapp;
import android.app.Application;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.support.v4.content.ContextCompat;
import android.util.Log;
import android.widget.Toast;
import dji.common.error.DJIError;
import dji.common.error.DJISDKError;
import dji.sdk.base.BaseComponent;
import dji.sdk.base.BaseProduct;
import dji.sdk.camera.Camera;
import dji.sdk.products.Aircraft;
import dji.sdk.products.HandHeld;
import dji.sdk.sdkmanager.DJISDKManager;
public class DJIApplication extends Application {
private static final String TAG = "DroneApp";
public static final String FLAG_CONNECTION_CHANGE = "dji_sdk_connection_change";
private DJISDKManager.SDKManagerCallback mDJISDKManagerCallback;
private BaseProduct.BaseProductListener mDJIBaseProductListener;
private BaseComponent.ComponentListener mDJIComponentListener;
private static BaseProduct mProduct;
public Handler mHandler;
private Application instance;
public void setContext(Application application) {
instance = application;
}
@Override
public Context getApplicationContext() {
return instance;
}
public DJIApplication() {
}
public static synchronized BaseProduct getProductInstance() {
if (null == mProduct) {
mProduct = DJISDKManager.getInstance().getProduct();
}
return mProduct;
}
public static synchronized Aircraft getAircraftInstance() {
if (!isAircraftConnected()) return null;
return (Aircraft) getProductInstance();
}
public static synchronized Camera getCameraInstance() {
if (getProductInstance() == null) return null;
Camera camera = null;
if (getProductInstance() instanceof Aircraft) {
camera = ((Aircraft) getProductInstance()).getCamera();
} else if (getProductInstance() instanceof HandHeld) {
camera = ((HandHeld) getProductInstance()).getCamera();
}
return camera;
}
public static boolean isAircraftConnected() {
return getProductInstance() != null && getProductInstance() instanceof Aircraft;
}
public static boolean isProductModuleAvailable() {
return (null != DJIApplication.getProductInstance());
}
public static boolean isCameraModuleAvailable() {
return isProductModuleAvailable() &&
(null != DJIApplication.getProductInstance().getCamera());
}
public static boolean isPlaybackAvailable() {
return isCameraModuleAvailable() &&
(null != DJIApplication.getProductInstance().getCamera().getPlaybackManager());
}
@Override
public void onCreate() {
super.onCreate();
mHandler = new Handler(Looper.getMainLooper());
mDJIComponentListener = new BaseComponent.ComponentListener() {
@Override
public void onConnectivityChange(boolean isConnected) {
notifyStatusChange();
}
};
mDJIBaseProductListener = new BaseProduct.BaseProductListener() {
@Override
public void onComponentChange(BaseProduct.ComponentKey key, BaseComponent oldComponent, BaseComponent newComponent) {
if (newComponent != null) {
newComponent.setComponentListener(mDJIComponentListener);
}
notifyStatusChange();
}
@Override
public void onConnectivityChange(boolean isConnected) {
notifyStatusChange();
}
};
/**
* When starting SDK services, an instance of interface DJISDKManager.DJISDKManagerCallback will be used to listen to
* the SDK Registration result and the product changing.
*/
mDJISDKManagerCallback = new DJISDKManager.SDKManagerCallback() {
//Listens to the SDK registration result
@Override
public void onRegister(DJIError error) {
if (error == DJISDKError.REGISTRATION_SUCCESS) {
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(), "Register Success", Toast.LENGTH_LONG).show();
}
});
DJISDKManager.getInstance().startConnectionToProduct();
} else {
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(), "Register sdk fails, check network is available", Toast.LENGTH_LONG).show();
}
});
}
Log.e("TAG", error.toString());
}
//Listens to the connected product changing, including two parts, component changing or product connection changing.
@Override
public void onProductChange(BaseProduct oldProduct, BaseProduct newProduct) {
mProduct = newProduct;
if (mProduct != null) {
mProduct.setBaseProductListener(mDJIBaseProductListener);
}
notifyStatusChange();
}
};
//Check the permissions before registering the application for android system 6.0 above.
int permissionCheck = ContextCompat.checkSelfPermission(getApplicationContext(), android.Manifest.permission.WRITE_EXTERNAL_STORAGE);
int permissionCheck2 = ContextCompat.checkSelfPermission(getApplicationContext(), android.Manifest.permission.READ_PHONE_STATE);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M || (permissionCheck == 0 && permissionCheck2 == 0)) {
//This is used to start SDK services and initiate SDK.
DJISDKManager.getInstance().registerApp(getApplicationContext(), mDJISDKManagerCallback);
Toast.makeText(getApplicationContext(), "registering, pls wait...", Toast.LENGTH_LONG).show();
} else {
Toast.makeText(getApplicationContext(), "Please check if the permission is granted.", Toast.LENGTH_LONG).show();
}
}
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);
getApplicationContext().sendBroadcast(intent);
}
};
}
ConnectionActivity.java (Just the important parts...)
package com.test.droneapp;
import android.Manifest;
import android.app.Dialog;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.graphics.Color;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
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.util.Log;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.TextView;
import com.airbnb.lottie.LottieAnimationView;
import com.airbnb.lottie.SimpleColorFilter;
import com.geniusforapp.fancydialog.FancyAlertDialog;
import com.securepreferences.SecurePreferences;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import com.test.droneapp.utils.NetworkUtils;
import com.test.droneapp.utils.SystemUtils;
import com.test.droneapp.utils.UIUtils;
import dji.common.error.DJIError;
import dji.common.error.DJISDKError;
import dji.log.DJILog;
import dji.sdk.base.BaseProduct;
import dji.sdk.sdkmanager.DJISDKManager;
public class ConnectionActivity extends AppCompatActivity implements View.OnClickListener {
private static final String TAG = "droneapp";
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);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
checkAndRequestPermissions();
setContentView(R.layout.activity_connection);
initUI();
// Register the broadcast receiver for receiving the device connection's changes.
IntentFilter filter = new IntentFilter();
filter.addAction(DJIApplication.FLAG_CONNECTION_CHANGE);
registerReceiver(mReceiver, filter);
}
@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();
}
@Override
protected void onDestroy() {
Log.e(TAG, "onDestroy");
unregisterReceiver(mReceiver);
super.onDestroy();
}`
/**
* 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() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
ActivityCompat.requestPermissions(this,
missingPermission.toArray(new String[missingPermission.size()]),
REQUEST_PERMISSION_CODE);
} else {
startSDKRegistration();
}
}
/**
* 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 {
UIUtils.errorSnacky("Missing permissions!!!");
}
}
protected BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
refreshSDKRelativeUI();
}
};
private void startSDKRegistration() {
if (isRegistrationInProgress.compareAndSet(false, true)) {
AsyncTask.execute(new Runnable() {
@Override
public void run() {
showToast( "registering, pls wait...");
DJISDKManager.getInstance().registerApp(getApplicationContext(), new DJISDKManager.SDKManagerCallback() {
@Override
public void onRegister(DJIError djiError) {
if (djiError == DJISDKError.REGISTRATION_SUCCESS) {
DJILog.e("App registration", DJISDKError.REGISTRATION_SUCCESS.getDescription());
DJISDKManager.getInstance().startConnectionToProduct();
runOnUiThread(new Runnable() {
@Override
public void run() {
UIUtils.successSnacky("Register Success");
}
});
} else {
runOnUiThread(new Runnable() {
@Override
public void run() {
UIUtils.errorSnacky( "Register sdk fails, check network is available");
}
});
}
Log.v(TAG, djiError.getDescription());
}
@Override
public void onProductChange(BaseProduct oldProduct, BaseProduct newProduct) {
Log.d(TAG, String.format("onProductChanged oldProduct:%s, newProduct:%s", oldProduct, newProduct));
}
});
}
});
}
}
}
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.test.droneapp">
<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-permission android:name="android.permission.WRITE_SETTINGS" />
<uses-permission android:name="android.permission.CHANGE_CONFIGURATION" />
<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" />
<application
android:name=".LauncherApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme"
tools:replace="android:icon">
<!-- DJI SDK -->
<uses-library android:name="com.android.future.usb.accessory" />
<meta-data
android:name="com.dji.sdk.API_KEY"
android:value="XXXXXXXXXXXXXXXXXXXXXX" />
<activity
android:name="dji.sdk.sdkmanager.DJIAoaControllerActivity"
android:theme="@android:style/Theme.Translucent" >
<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>
<service android:name="dji.sdk.sdkmanager.DJIGlobalService" >
</service>
<!-- DJI SDK -->
<meta-data
android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version" />
<activity
android:name=".ConnectionActivity"
android:label="@string/app_name"
android:screenOrientation="landscape">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
在 4.3.2 上一切正常,更新后我按照 DJI 网站上的指南使其正常工作,但我无法弄清楚我做错了什么。
如有任何帮助,我们将不胜感激!
最佳答案
您可以通过在Application 类上使用以下代码来获取RemoteController 对象
/**
* This method is used for getting RemoteController instance.
*
* @return
*/
public static synchronized RemoteController getRemoteControllerInstance() {
if (!isAircraftConnected()) return null;
return getAircraftInstance().getRemoteController();
}
关于java - 更新到4.4.1后DJI SDK无法识别RC,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48704129/
我在从html页面生成PDF时遇到问题。我正在使用PDFkit。在安装它的过程中,我注意到我需要wkhtmltopdf。所以我也安装了它。我做了PDFkit的文档所说的一切......现在我在尝试加载PDF时遇到了这个错误。这里是错误:commandfailed:"/usr/local/bin/wkhtmltopdf""--margin-right""0.75in""--page-size""Letter""--margin-top""0.75in""--margin-bottom""0.75in""--encoding""UTF-8""--margin-left""0.75in""-
给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru
我对最新版本的Rails有疑问。我创建了一个新应用程序(railsnewMyProject),但我没有脚本/生成,只有脚本/rails,当我输入ruby./script/railsgeneratepluginmy_plugin"Couldnotfindgeneratorplugin.".你知道如何生成插件模板吗?没有这个命令可以创建插件吗?PS:我正在使用Rails3.2.1和ruby1.8.7[universal-darwin11.0] 最佳答案 随着Rails3.2.0的发布,插件生成器已经被移除。查看变更日志here.现在
我尝试运行2.x应用程序。我使用rvm并为此应用程序设置其他版本的ruby:$rvmuseree-1.8.7-head我尝试运行服务器,然后出现很多错误:$script/serverNOTE:Gem.source_indexisdeprecated,useSpecification.Itwillberemovedonorafter2011-11-01.Gem.source_indexcalledfrom/Users/serg/rails_projects_terminal/work_proj/spohelp/config/../vendor/rails/railties/lib/r
我正在尝试在我的centos服务器上安装therubyracer,但遇到了麻烦。$geminstalltherubyracerBuildingnativeextensions.Thiscouldtakeawhile...ERROR:Errorinstallingtherubyracer:ERROR:Failedtobuildgemnativeextension./usr/local/rvm/rubies/ruby-1.9.3-p125/bin/rubyextconf.rbcheckingformain()in-lpthread...yescheckingforv8.h...no***e
我花了三天的时间用头撞墙,试图弄清楚为什么简单的“rake”不能通过我的规范文件。如果您遇到这种情况:任何文件夹路径中都不要有空格!。严重地。事实上,从现在开始,您命名的任何内容都没有空格。这是我的控制台输出:(在/Users/*****/Desktop/LearningRuby/learn_ruby)$rake/Users/*******/Desktop/LearningRuby/learn_ruby/00_hello/hello_spec.rb:116:in`require':cannotloadsuchfile--hello(LoadError) 最佳
我将应用程序升级到Rails4,一切正常。我可以登录并转到我的编辑页面。也更新了观点。使用标准View时,用户会更新。但是当我添加例如字段:name时,它不会在表单中更新。使用devise3.1.1和gem'protected_attributes'我需要在设备或数据库上运行某种更新命令吗?我也搜索过这个地方,找到了许多不同的解决方案,但没有一个会更新我的用户字段。我没有添加任何自定义字段。 最佳答案 如果您想允许额外的参数,您可以在ApplicationController中使用beforefilter,因为Rails4将参数
我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/
我在pry中定义了一个函数:to_s,但我无法调用它。这个方法去哪里了,怎么调用?pry(main)>defto_spry(main)*'hello'pry(main)*endpry(main)>to_s=>"main"我的ruby版本是2.1.2看了一些答案和搜索后,我认为我得到了正确的答案:这个方法用在什么地方?在irb或pry中定义方法时,会转到Object.instance_methods[1]pry(main)>defto_s[1]pry(main)*'hello'[1]pry(main)*end=>:to_s[2]pry(main)>defhello[2]pry(main)
我使用的是Firefox版本36.0.1和Selenium-Webdrivergem版本2.45.0。我能够创建Firefox实例,但无法使用脚本继续进行进一步的操作无法在60秒内获得稳定的Firefox连接(127.0.0.1:7055)错误。有人能帮帮我吗? 最佳答案 我遇到了同样的问题。降级到firefoxv33后一切正常。您可以找到旧版本here 关于ruby-无法在60秒内获得稳定的Firefox连接(127.0.0.1:7055),我们在StackOverflow上找到一个类