This commit is contained in:
zyp
2026-07-09 10:11:25 +08:00
commit f632730452
2156 changed files with 764537 additions and 0 deletions

View File

@@ -0,0 +1,173 @@
package dji.sampleV5.aircraft.keyvalue
import dji.sdk.keyvalue.key.ComponentType
import dji.v5.manager.capability.CapabilityManager
import dji.v5.manager.capability.CapabilityParser
import dji.v5.utils.common.*
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.core.Completable
import io.reactivex.rxjava3.core.CompletableObserver
import io.reactivex.rxjava3.disposables.Disposable
import java.lang.Exception
import java.lang.StringBuilder
import dji.v5.ux.core.util.ToastUtils
/**
* @author feel.feng
* @time 2022/09/13 5:43 下午
* @description: 能力集key 测试,结果中只展示未通过的key
* 结果存储在包目录/keycheck/result.txt
*/
object CapabilityKeyChecker {
private val TAG = LogUtils.getTag(this)
/**
* 根据key名称从能力集获取测试用例json集合 每个对象可反序列化为key的参数对象
*/
fun getKeyParamList(keyName: String): MutableList<String> {
return CapabilityParser.getInstance().getValueParamList(keyName)
}
fun getKeyItem(keyName: String): KeyItem<*, *>? {
val allList: MutableList<KeyItem<*, *>> = ArrayList()
var item: KeyItem<*, *>? = null
KeyItemDataUtil.getAllKeyList(allList)
allList.forEach {
if (it.toString() == keyName) {
item = it;
}
}
return item
}
data class ItemDecoder(
var componetIndex: Int = 0, // LEFT_OR_MAIN
var subComponetType: Int = 65534, // DEFAULT
var subComponetIndex: Int = 0,
var jsonString: String
)
/**
* 获取枚举对应的Json list字符串
* eg:CameraMode 对象 obj cameramodeMsg field[obj] CameraMode
*/
fun getDJIValueBeanStr(item: KeyItem<*, *>): String {
var tagBegin = "{\"valueParamList\": ["
var tagEnd = "]}"
try {
val pFields = item.param?.javaClass?.declaredFields
if (pFields != null) {
for (field in pFields) {
field.isAccessible = true
val clazz = field.type
if (clazz.isEnum) {
val itemList =
(item.subItemMap as Map<String?, List<EnumItem>>)[clazz.canonicalName]!!
var jsonList = StringBuilder(tagBegin)
itemList.forEach {
field[item.param] = KeyItemHelper.getEnumData(
clazz as Class<Enum<*>>,
it.getName().toString()
)
var jsonString =
"\"" + item.param.toString().replace("\"", "\\\"") + "\""
var result = jsonString + ","
if (!result.contains("65535")) {//过滤unknown
jsonList.append(result)
}
}
jsonList.deleteAt(jsonList.lastIndex)
jsonList.append(tagEnd)
return jsonList.toString()
}
}
}
} catch (e: Exception) {
LogUtils.e(KeyItemHelper.TAG, e.message)
}
return ""
}
/**
* 根据item生成 枚举类型的json文件
*/
fun generateAllEnumList(productType: String) {
val allList: MutableList<KeyItem<*, *>> = ArrayList()
DJIExecutor.getExecutorFor(DJIExecutor.Purpose.IO).execute {
KeyItemDataUtil.getAllKeyList(allList)
allList
.filter {
val keyName = "Key$it"
it.canSet()
&& CapabilityManager.getInstance().isKeySupported(
productType,
"",
ComponentType.find(it.getKeyInfo().componentType),
keyName
)
}
.forEach() { item ->
val jsonStr = getDJIValueBeanStr(item)
if (jsonStr.isNotEmpty()) {
var filePath = DiskUtil.getExternalCacheDirPath(
ContextUtil.getContext(),
"keycheck/$item.json"
)
FileUtils.writeFile(filePath, jsonStr, false)
}
}
}
}
private fun checkOneType(
productType: String,
componentTypeName: String,
keyCheckType: KeyCheckType,
componentIndex: Int
): Completable {
var keyOperatorCommand = when (keyCheckType) {
KeyCheckType.SET -> KeySetCommand(productType, componentTypeName, componentIndex)
KeyCheckType.ACTION -> KeyActionCommand(productType, componentTypeName, componentIndex)
KeyCheckType.GET -> KeyGetCommand(productType, componentTypeName, componentIndex)
}
return keyOperatorCommand.execute()
}
fun check(
productType: String,
componentTypeName: String,
componentIndex: Int
) {
checkOneType(productType, componentTypeName, KeyCheckType.SET, componentIndex)
.andThen(checkOneType(productType, componentTypeName, KeyCheckType.SET, componentIndex))
.andThen(checkOneType(productType, componentTypeName, KeyCheckType.ACTION, componentIndex))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : CompletableObserver {
override fun onSubscribe(d: Disposable) {
LogUtils.e(TAG, "begin check")
ToastUtils.showToast("begin check")
}
override fun onComplete() {
LogUtils.e(TAG, "-check finish-")
}
override fun onError(e: Throwable) {
LogUtils.e(TAG, "check error${e.message}")
}
})
}
}

View File

@@ -0,0 +1,111 @@
package dji.sampleV5.aircraft.keyvalue
/**
* @author feel.feng
* @time 2022/03/11 9:42 上午
* @description:
*/
enum class ChannelType(name: String) {
/**
* 电池
*/
CHANNEL_TYPE_BATTERY("BATTERY"),
/**
* 云台
*/
CHANNEL_TYPE_GIMBAL("GIMBAL"),
/**
* 相机
*/
CHANNEL_TYPE_CAMERA("CAMERA"),
/**
* Airlink
*/
CHANNEL_TYPE_AIRLINK("AIRLINK"),
/**
* Flight Assistant
*/
CHANNEL_TYPE_FLIGHT_ASSISTANT("ASSISTANT"),
/**
* Flight Control
*/
CHANNEL_TYPE_FLIGHT_CONTROL("FLIGHT CONTROL"),
/**
* Remote Controller
*/
CHANNEL_TYPE_REMOTE_CONTROLLER("REMOTE CONTROLLER"),
/**
* BLE
*/
CHANNEL_TYPE_BLE("BLE"),
/**
* RTK
*/
CHANNEL_TYPE_RTK_BASE_STATION("RTK BASE STATION"),
/**
* RTK
*/
CHANNEL_TYPE_RTK_MOBILE_STATION("RTK MOBILE STATION"),
/**
* Product
*/
CHANNEL_TYPE_PRODUCT("PRODUCT"),
/**
* OcuSync
*/
CHANNEL_TYPE_OCU_SYNC("OCU SYNC"),
/**
* Radar
*/
CHANNEL_TYPE_RADAR("RADAR"),
/**
* Mobile Network
*/
CHANNEL_TYPE_MOBILE_NETWORK("MOBILE NETWORK"),
/**
* on board
*/
CHANNEL_TYPE_ON_BOARD("BOARD"),
/**
* Payload
*/
CHANNEL_TYPE_ON_PAYLOAD("PAYLOAD"),
/**
* lidar
*/
CHANNEL_TYPE_LIDAR("LIDAR"),
/**
* IntelligentBox
*/
INTELLIGENT_BOX("INTELLIGENT BOX");
private val value: String
override fun toString(): String {
return value
}
init {
value = name
}
}

View File

@@ -0,0 +1,52 @@
package dji.sampleV5.aircraft.keyvalue
import java.io.Serializable
/**
* @author feel.feng
* @time 2022/03/10 9:58 上午
* @description:
*/
class EnumItem : Serializable {
/**
* 需要显示的条目描述
*/
private var description: String? = null
/**
* SDK对应的枚举
*/
private var name: String? = null
/**
* 是否选中
*/
private var selected = false
fun getName(): String? {
return name
}
fun setName(name: String?) {
this.name = name
}
fun getDescription(): String? {
return description
}
fun setDescription(description: String?) {
this.description = description
}
fun isSelected(): Boolean {
return selected
}
fun setSelected(selected: Boolean) {
this.selected = selected
}
companion object {
private const val serialVersionUID = 876323262645176354L
}
}

View File

@@ -0,0 +1,34 @@
package dji.sampleV5.aircraft.keyvalue
/**
* @author feel.feng
* @time 2022/10/26 2:25 下午
* @description:
*/
class KeyActionCommand(
private val productType: String,
private val componentTypeName: String,
private val componentIndex: Int
) : KeyOperatorCommand(productType, componentTypeName, componentIndex) {
private val TAG_GET = "【ACTION】"
private val TAG_ERROR = "ActionErrorMsg"
override fun filter(item: KeyItem<*, *>): Boolean {
return item.canAction()
}
override fun run(item: KeyItem<*, *>) {
super.doKeyParam(item, KeyCheckType.ACTION)
}
override fun getTAG(): String {
return TAG_GET
}
override fun getErrorTAG(): String {
return TAG_ERROR
}
}

View File

@@ -0,0 +1,219 @@
package dji.sampleV5.aircraft.keyvalue;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import dji.sdk.keyvalue.key.DJIActionKeyInfo;
import dji.sdk.keyvalue.key.DJIKey;
import dji.sdk.keyvalue.key.DJIKeyInfo;
import dji.sdk.keyvalue.key.KeyTools;
import dji.v5.common.callback.CommonCallbacks;
import dji.v5.manager.KeyManager;
import dji.v5.utils.common.LogUtils;
public class KeyBaseStructure<P, R> {
private static final String TAG = KeyBaseStructure.class.getSimpleName();
/**
* 设置参数
*/
protected P param;
/**
* 返回结果
*/
protected R result;
/**
* 推送数据记录
*/
protected String listenRecord = "";
/**
* 推送Listener宿主
*/
protected Object listenHolder;
public int getComponetIndex() {
return componetIndex;
}
public void setComponetIndex(int componetIndex) {
this.componetIndex = componetIndex;
}
public int getSubComponetType() {
return subComponetType;
}
public void setSubComponetType(int subComponetType) {
this.subComponetType = subComponetType;
}
public int getSubComponetIndex() {
return subComponetIndex;
}
public void setSubComponetIndex(int subComponetIndex) {
this.subComponetIndex = subComponetIndex;
}
protected int componetIndex = -1;
protected int subComponetType = -1;
protected int subComponetIndex = -1;
/**
* 枚举列表
*/
protected Map<String, List<EnumItem>> subItemMap = new HashMap<>();
/**
* 通过反射获取泛型类型数据并实例化
*/
protected void initGenericInstance() {
try {
KeyItemHelper.INSTANCE.initClassData(param);
KeyItemHelper.INSTANCE.initClassData(result);
initSubItemData();
} catch (Exception e) {
LogUtils.e(TAG ,e.getMessage());
}
}
/**
* 如果为枚举或者枚举嵌套列表,则初始化该列表数据
*/
protected void initSubItemData() {
if (param == null) {
return;
}
subItemMap.clear();
subItemMap.putAll(KeyItemHelper.INSTANCE.initSubItemData(param));
}
/**
* 获取需要设置的参数实例
*
* @return
*/
public P getParam() {
return param;
}
/**
* 获取参数映射列表
*
* @return
*/
public Map<String, List<EnumItem>> getSubItemMap() {
return subItemMap;
}
/**
* 获取推送数据记录
*
* @return
*/
public String getListenRecord() {
return listenRecord;
}
/**
* 异步get
*
* @param keyInfo
* @param getCallback
*/
protected void get(DJIKeyInfo<R> keyInfo, CommonCallbacks.CompletionCallbackWithParam<R> getCallback) {
DJIKey<R> key = createKey(keyInfo);
KeyManager.getInstance().getValue(key, getCallback);
}
/**
* 同步get
*
* @param keyInfo
* @return
*/
protected R get(DJIKeyInfo<R> keyInfo) {
DJIKey<R> key = createKey(keyInfo);
return KeyManager.getInstance().getValue(key);
}
/**
* 设置属性
*
* @param keyInfo
* @param setCallback
*/
protected void set(DJIKeyInfo<P> keyInfo, P param, CommonCallbacks.CompletionCallback setCallback) {
DJIKey<P> key = createKey(keyInfo);
KeyManager.getInstance().setValue(key, param, setCallback);
}
/**
* 设置Listener
*
* @param keyInfo
* @param listenHolder
* @param listenCallback
*/
protected void listen(DJIKeyInfo<R> keyInfo, Object listenHolder, CommonCallbacks.KeyListener<R> listenCallback) {
this.listenHolder = listenHolder;
DJIKey<R> key = createKey(keyInfo);
KeyManager.getInstance().listen(key, listenHolder, listenCallback);
}
/**
* 取消Listener
*
* @param keyInfo
* @param listenHolder
*/
protected void cancelListen(DJIKeyInfo<R> keyInfo, Object listenHolder) {
KeyManager.getInstance().cancelListen(createKey(keyInfo), listenHolder);
}
/**
* 带参action
*
* @param keyInfo
* @param param
* @param actonCallback
*/
protected void action(DJIActionKeyInfo<P, R> keyInfo, P param, CommonCallbacks.CompletionCallbackWithParam<R> actonCallback) {
DJIKey.ActionKey<P,R> key = createActionKey(keyInfo);
KeyManager.getInstance().performAction(key, param, actonCallback);
}
protected DJIKey.ActionKey<P,R> createActionKey(DJIActionKeyInfo<P,R> keyInfo) {
DJIKey.ActionKey<P,R> key = null;
key = KeyTools.createKey(keyInfo, 0, getComponetIndex(),getSubComponetType(), getSubComponetIndex());
return key;
}
protected<Parame> DJIKey<Parame> createKey(DJIKeyInfo<Parame> keyInfo ) {
DJIKey<Parame> key = null;
key = KeyTools.createKey(keyInfo, 0 , getComponetIndex(),getSubComponetType(), getSubComponetIndex());
return key;
}
}

View File

@@ -0,0 +1,12 @@
package dji.sampleV5.aircraft.keyvalue
/**
* @author feel.feng
* @time 2022/10/26 2:09 下午
* @description:
*/
enum class KeyCheckType {
GET,
SET,
ACTION
}

View File

@@ -0,0 +1,34 @@
package dji.sampleV5.aircraft.keyvalue
/**
* @author feel.feng
* @time 2022/10/26 2:25 下午
* @description:
*/
class KeyGetCommand(
private val productType: String,
private val componentTypeName: String,
private val componentIndex: Int
) : KeyOperatorCommand(productType, componentTypeName, componentIndex) {
private val TAG_GET = "【GET】"
private val TAG_ERROR = "GetErrorMsg"
override fun filter(item: KeyItem<*, *>): Boolean {
return item.canGet()
}
override fun run(item: KeyItem<*, *>) {
super.doKeyParam(item, KeyCheckType.GET)
}
override fun getTAG(): String {
return TAG_GET
}
override fun getErrorTAG(): String {
return TAG_ERROR
}
}

View File

@@ -0,0 +1,415 @@
package dji.sampleV5.aircraft.keyvalue;
import org.json.JSONObject;
import androidx.annotation.NonNull;
import dji.v5.ux.core.util.ToastUtils;
import dji.sampleV5.aircraft.util.Util;
import dji.sdk.keyvalue.converter.DJIValueConverter;
import dji.sdk.keyvalue.converter.EmptyValueConverter;
import dji.sdk.keyvalue.converter.SingleValueConverter;
import dji.sdk.keyvalue.key.DJIActionKeyInfo;
import dji.sdk.keyvalue.key.DJIKeyInfo;
import dji.sdk.keyvalue.value.base.DJIValue;
import dji.sdk.keyvalue.value.common.EmptyMsg;
import dji.v5.common.callback.CommonCallbacks;
import dji.v5.common.error.IDJIError;
import dji.v5.utils.common.LogUtils;
/**
*
* KeyItem作为key能力和动作的载体来进行封装
*/
public class KeyItem<P, R> extends KeyBaseStructure<P , R> implements Comparable<KeyItem<?,?>>{
private static final String TAG = KeyItem.class.getSimpleName();
public KeyItem(DJIKeyInfo<?> keyInfo) {
super();
this.keyInfo = (DJIKeyInfo<R>)keyInfo;
this.keyInfoSet = (DJIKeyInfo<P>)keyInfo;
}
/**
* 属性展示名
*/
protected String name;
public long getCount() {
return count;
}
public void setCount(long count) {
this.count = count;
}
/**
* 使用次数,用户排序
*/
private long count;
public boolean isSingleDJIValue;
/**
* 参数key的能力携带实体
*/
protected DJIKeyInfo<R> keyInfo;
protected DJIKeyInfo<P> keyInfoSet;
/**
* 需要调用者注入的回调接口,用于结果通知
*/
protected KeyItemActionListener<Object> keyOperateCallBack;
private boolean isItemSelected ;
public boolean isItemSelected() {
return isItemSelected;
}
public void setItemSelected(boolean itemSelected) {
isItemSelected = itemSelected;
}
/**
* 推送数据回调(需要调用者注入)
*/
protected KeyItemActionListener<String> pushCallBack;
public String getName() {
return Util.isBlank(name) ? keyInfo.getIdentifier() : name;
}
public void setName(String name){
this.name = name;
}
public DJIKeyInfo<P> getKeyInfo() {
return keyInfoSet;
}
/**
* 获取listen宿主
*
* @return
*/
public Object getListenHolder() {
return listenHolder;
}
/**
* 是否可以Get
*
* @return
*/
public boolean canGet() {
return keyInfo.isCanGet();
}
/**
* 是否可以Set
*
* @return
*/
public boolean canSet() {
return keyInfo.isCanSet();
}
/**
* 是否可以Listen
*
* @return
*/
public boolean canListen() {
return keyInfo.isCanListen();
}
/**
* 是否为action
*
* @return
*/
public boolean canAction() {
return keyInfo.isCanPerformAction();
}
/**
* 需要调用者注入的回调接口,用于结果通知
*
* @param keyOperateCallBack
*/
public void setKeyOperateCallBack(KeyItemActionListener<Object> keyOperateCallBack) {
this.keyOperateCallBack = keyOperateCallBack;
}
/**
* 推送回调
*
* @param pushCallBack
*/
public void setPushCallBack(KeyItemActionListener<String> pushCallBack) {
this.pushCallBack = pushCallBack;
}
/**
* Get请求
*/
public void doGet() {
try {
get(keyInfo, new CommonCallbacks.CompletionCallbackWithParam<R>() {
@Override
public void onSuccess(R data) {
if (keyOperateCallBack != null && data != null) {
keyOperateCallBack.actionChange(getName()+"【GET】 == success " + data.toString());
}
}
@Override
public void onFailure(@NonNull IDJIError error) {
if (keyOperateCallBack != null) {
keyOperateCallBack.actionChange(getName() + "【GET】 GetErrorMsg ==" + error.toString());
}
}
});
} catch (Exception e) {
LogUtils.e(TAG ,e.getMessage());
}
}
/**
* SyncGet请求
*/
public void doSyncGet() {
try {
DJIValue getResult = (DJIValue) get(keyInfo);
if(keyOperateCallBack != null){
keyOperateCallBack.actionChange(null == getResult ? "" : getResult.toJson());
}
} catch (Exception e) {
LogUtils.e(TAG ,e.getMessage());
}
}
/**
* Set请求
*/
public void doSet(String jsonStr) {
try {
final P p = validPrams(jsonStr);
if (p == null) {
keyOperateCallBack.actionChange(getName() + "【SET】SetErrorMsg== json error");
return;
}
set(keyInfoSet, p, new CommonCallbacks.CompletionCallback() {
@Override
public void onSuccess() {
LogUtils.e(TAG, "set success : " + getName());
if (keyOperateCallBack != null) {
// 保存上一次设置成功的对象下次set时可使用保存过的对象 序列化json
if (getKeyInfo().getTypeConverter() instanceof DJIValueConverter) {
param = p;
}
keyOperateCallBack.actionChange(getName() + "【SET】==" + p.toString() + " | " + " success");
}
ToastUtils.INSTANCE.showToast("set " + p.getClass().getSimpleName() + " success");
}
@Override
public void onFailure(@NonNull IDJIError error) {
LogUtils.e(TAG, "set error : " + error);
if (keyOperateCallBack != null) {
keyOperateCallBack.actionChange(getName() + "【SET】SetErrorMsg== " + p.toString() + "|" + error.toString());
}
}
});
} catch (Exception e) {
ToastUtils.INSTANCE.showToast("输入参数出错");
}
}
/**
* action请求
*/
public void doAction(String jsonStr) {
DJIActionKeyInfo<P,R> actionKeyInfo = (DJIActionKeyInfo<P,R>) keyInfo;
P p = null;
if(jsonStr != null && !jsonStr.isEmpty()){
p = validPrams(jsonStr);
}
if (p == null && actionKeyInfo.getTypeConverter()!= EmptyValueConverter.converter) {
return;
}
P pRes = p;
action(actionKeyInfo, p, new CommonCallbacks.CompletionCallbackWithParam<R>() {
@Override
public void onSuccess(Object data) {
if (keyOperateCallBack != null) {
if (data != null && !(data instanceof EmptyMsg)) {
keyOperateCallBack.actionChange(getName() + "【ACTION】== " + getActionTipsStr(pRes) + " success: " + data.toString());
} else {
keyOperateCallBack.actionChange(getName() + "【ACTION】== " + getActionTipsStr(pRes) + " result: success");
}
}
}
@Override
public void onFailure(@NonNull IDJIError error) {
if (keyOperateCallBack != null) {
keyOperateCallBack.actionChange(getName() +"【ACTION】 ActionErrorMsg==" + error.toString());
}
}
});
}
private String getActionTipsStr(P pRes){
return pRes == null ? "" : pRes.toString();
}
/**
* 推送回调
*/
private CommonCallbacks.KeyListener<R> listenSDKCallback =
( oldValue ,newValue) -> {
StringBuffer sb = new StringBuffer("【LISTEN】");
sb.append(getName());
sb.append(" result:");
sb.append("oldValue:").append(oldValue);
sb.append(" newValue:").append(newValue);
if (pushCallBack != null) {
pushCallBack.actionChange(sb.toString());
}
};
/**
* 注册Listen新接口
*
* @param listenHolder
*/
public void listen(Object listenHolder) {
listen(keyInfo, listenHolder, listenSDKCallback);
}
/**
* 取消Listen新接口
*
* @param listenHolder
*/
public void cancelListen(Object listenHolder) {
if (this.listenHolder == listenHolder) {
this.listenHolder = null;
cancelListen(keyInfo, listenHolder);
pushCallBack = null;
listenRecord = "";
}
//listenSDKCallback = null;
}
/**
* 验证参数
*
* @param jsonStr
* @return
*/
private P validPrams(String jsonStr) {
if (Util.isBlank(jsonStr)) {
ToastUtils.INSTANCE.showToast("请先设置参数");
return null;
}
final P p = buildParamFromJsonStr(jsonStr);
if (p == null) {
ToastUtils.INSTANCE.showToast("请先设置" + jsonStr + " 参数");
return null;
}
return p;
}
/**
* 反序列化根据JSON串获取对象
*
* @param jsonStr
* @return
*/
public P buildParamFromJsonStr(String jsonStr) {
P p;
if (keyInfo.getTypeConverter() instanceof SingleValueConverter && !isSingleDJIValue) {
p = (P) keyInfo.getTypeConverter().fromStr(getSingleJsonValue(jsonStr));
} else {
p = (P) keyInfo.getTypeConverter().fromStr(jsonStr);
}
return p;
}
/**
* 获取SingleValue 中原始类型包装类的value值
* @param jsonStr
* @return
*/
private String getSingleJsonValue(String jsonStr) {
String value = "";
try {
JSONObject jsonObj = new JSONObject(jsonStr);
value = jsonObj.getString("value");
}catch (Exception e) {
LogUtils.e(TAG ,e.getMessage());
}
return value;
}
/**
* 序列化获取默认JSON串
*
* @return
*/
public String getParamJsonStr() {
String jsonStr = null;
try {
jsonStr = param.toString();
} catch (Exception e) {
LogUtils.e(TAG ,e.getMessage());
}
return jsonStr;
}
/**
* 移除回调
*/
public void removeCallBack() {
cancelListen(listenHolder);
keyOperateCallBack = null;
}
@Override
public int compareTo(KeyItem keyItem) {
if (keyItem.count - this.count > 0) {
return 1;
} else if (keyItem.count - this.count < 0) {
return -1;
} else {
return 0;
}
}
public boolean isSingleDJIValue() {
return isSingleDJIValue;
}
public void setSingleDJIValue(boolean singleDJIValue) {
isSingleDJIValue = singleDJIValue;
}
@Override
public String toString(){
return Util.isBlank(name) ? keyInfo.getIdentifier() : name;
}
}

View File

@@ -0,0 +1,18 @@
package dji.sampleV5.aircraft.keyvalue;
/**
* @author feel.feng
* @time 2022/03/11 9:55 上午
* @description:
*/
import org.jetbrains.annotations.Nullable;
/**
* @author feel.feng
*/
public interface KeyItemActionListener<T> {
void actionChange(@Nullable T t);
}

View File

@@ -0,0 +1,143 @@
package dji.sampleV5.aircraft.keyvalue;
import android.content.Context;
import android.graphics.Color;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Filter;
import android.widget.Filterable;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
import androidx.annotation.NonNull;
import androidx.collection.SparseArrayCompat;
import androidx.recyclerview.widget.RecyclerView;
import dji.sampleV5.aircraft.R;
public class KeyItemAdapter extends RecyclerView.Adapter<KeyItemAdapter.ComViewHolder> implements Filterable {
private KeyItemActionListener<KeyItem<?, ?>> callback;
protected List<KeyItem<?, ?>> dataList;
protected List<KeyItem<?, ?>> mFilterList;
protected Context context;
public KeyItemAdapter(Context context, List<KeyItem<?, ?>> dataList, KeyItemActionListener<KeyItem<?, ?>> callback) {
this.context = context;
this.dataList = dataList;
this.mFilterList = dataList;
this.callback = callback;
}
@Override
public ComViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new ComViewHolder(LayoutInflater.from(context).inflate(R.layout.item_camera_key_list, parent, false));
}
@Override
public void onBindViewHolder(@NonNull ComViewHolder holder, int position) {
if (mFilterList.size() <= position) {
return;
}
convert(holder, mFilterList.get(position));
}
@Override
public int getItemCount() {
return mFilterList == null ? 0 : mFilterList.size();
}
public void convert(ComViewHolder viewHolder, final KeyItem<?, ?> keyItem) {
if (viewHolder == null || keyItem == null) {
return;
}
TextView textView = viewHolder.getView(R.id.tv_item_name);
textView.setText(keyItem.getName());
if (keyItem.isItemSelected()) {
textView.setBackgroundColor(Color.GRAY);
} else {
textView.setBackgroundColor(Color.TRANSPARENT);
}
textView.setOnClickListener(v -> {
if (callback != null) {
callback.actionChange(keyItem);
}
});
}
@Override
public Filter getFilter() {
return new Filter() {
@Override
protected FilterResults performFiltering(CharSequence charSequence) {
String charString = charSequence.toString();
if (charString.isEmpty()) {
mFilterList = dataList;
} else {
List<KeyItem<?, ?>> filteredList = new ArrayList<>();
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0, length = charSequence.length(); i < length; i++) {
if (stringBuilder.length() > 0) {
stringBuilder.append("+.*");
}
stringBuilder.append(charSequence.charAt(i));
}
Pattern pattern = Pattern.compile(stringBuilder.toString(), Pattern.CASE_INSENSITIVE);
for (KeyItem<?, ?> item : dataList) {
if (pattern.matcher(item.keyInfo.getIdentifier()).find()) {
filteredList.add(item);
}
}
mFilterList = filteredList;
}
FilterResults filterResults = new FilterResults();
filterResults.values = mFilterList;
return filterResults;
}
@Override
protected void publishResults(CharSequence charSequence, FilterResults filterResults) {
mFilterList = (ArrayList<KeyItem<?, ?>>) filterResults.values;
notifyDataSetChanged();
}
};
}
/**
* 缓存容器
*/
public class ComViewHolder extends RecyclerView.ViewHolder {
private View convertView;
private SparseArrayCompat<View> views;
public ComViewHolder(View itemView) {
super(itemView);
this.convertView = itemView;
this.views = new SparseArrayCompat<>();
}
public <T extends View> T getView(int layoutId) {
View view = views.get(layoutId);
if (view == null) {
view = convertView.findViewById(layoutId);
views.put(layoutId, view);
}
return (T) view;
}
}
}

View File

@@ -0,0 +1,255 @@
package dji.sampleV5.aircraft.keyvalue;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import dji.sdk.keyvalue.converter.DJIValueConverter;
import dji.sdk.keyvalue.converter.IDJIValueConverter;
import dji.sdk.keyvalue.converter.SingleValueConverter;
import dji.sdk.keyvalue.key.AIBoxKey;
import dji.sdk.keyvalue.key.AirLinkKey;
import dji.sdk.keyvalue.key.BatteryKey;
import dji.sdk.keyvalue.key.BleKey;
import dji.sdk.keyvalue.key.CameraKey;
import dji.sdk.keyvalue.key.DJIKeyInfo;
import dji.sdk.keyvalue.key.FlightAssistantKey;
import dji.sdk.keyvalue.key.FlightControllerKey;
import dji.sdk.keyvalue.key.GimbalKey;
import dji.sdk.keyvalue.key.LidarKey;
import dji.sdk.keyvalue.key.MobileNetworkKey;
import dji.sdk.keyvalue.key.MobileNetworkLinkRCKey;
import dji.sdk.keyvalue.key.OcuSyncKey;
import dji.sdk.keyvalue.key.OnboardKey;
import dji.sdk.keyvalue.key.PayloadKey;
import dji.sdk.keyvalue.key.ProductKey;
import dji.sdk.keyvalue.key.RadarKey;
import dji.sdk.keyvalue.key.RemoteControllerKey;
import dji.sdk.keyvalue.key.RtkBaseStationKey;
import dji.sdk.keyvalue.key.RtkMobileStationKey;
import dji.sdk.keyvalue.value.base.DJIValue;
import dji.v5.utils.common.LogUtils;
/**
* @author feel.feng
* @time 2022/03/16 3:36 下午
* @description:
*/
public class KeyItemDataUtil {
private static final String TAG = KeyItemDataUtil.class.getSimpleName();
private static final List<KeyItem<?, ?>> allKeyList = new ArrayList<>();
private KeyItemDataUtil() {
//do something
}
public static void initBatteryKeyList(List<KeyItem<?, ?>> keyList) {
initList(keyList, BatteryKey.getKeyList());
}
public static void initAirlinkKeyList(List<KeyItem<?, ?>> keylist) {
initList(keylist, AirLinkKey.getKeyList());
}
public static void initGimbalKeyList(List<KeyItem<?, ?>> keyList) {
initList(keyList, GimbalKey.getKeyList());
}
public static void initCameraKeyList(List<KeyItem<?, ?>> keyList) {
initList(keyList, CameraKey.getKeyList());
}
public static void initWiFiKeyList(List<KeyItem<?, ?>> keyList) {
// initList(keyList , WiFiKey.getKeyList());
}
public static void initFlightAssistantKeyList(List<KeyItem<?, ?>> keyList) {
initList(keyList, FlightAssistantKey.getKeyList());
}
public static void initFlightControllerKeyList(List<KeyItem<?, ?>> keyList) {
initList(keyList, FlightControllerKey.getKeyList());
}
public static void initRemoteControllerKeyList(List<KeyItem<?, ?>> keyList) {
initList(keyList, RemoteControllerKey.getKeyList());
}
public static void initBleKeyList(List<KeyItem<?, ?>> keyList) {
initList(keyList, BleKey.getKeyList());
}
public static void initProductKeyList(List<KeyItem<?, ?>> keyList) {
initList(keyList, ProductKey.getKeyList());
}
public static void initRtkBaseStationKeyList(List<KeyItem<?, ?>> keyList) {
initList(keyList, RtkBaseStationKey.getKeyList());
}
public static void initRtkMobileStationKeyList(List<KeyItem<?, ?>> keyList) {
initList(keyList, RtkMobileStationKey.getKeyList());
}
public static void initOcuSyncKeyList(List<KeyItem<?, ?>> keyList) {
initList(keyList, OcuSyncKey.getKeyList());
}
public static void initRadarKeyList(List<KeyItem<?, ?>> keyList) {
initList(keyList, RadarKey.getKeyList());
}
public static void initIntelligentBoxList(List<KeyItem<?, ?>> keyList) {
initList(keyList, AIBoxKey.getKeyList());
}
public static void initMobileNetworkKeyList(List<KeyItem<?, ?>> keyList) {
initList(keyList, MobileNetworkKey.getKeyList());
}
public static void initMobileNetworkLinkRCKeyList(List<KeyItem<?, ?>> keyList) {
initList(keyList, MobileNetworkLinkRCKey.getKeyList());
}
public static void initOnboardKeyList(List<KeyItem<?, ?>> keyList) {
initList(keyList, OnboardKey.getKeyList());
}
public static void initPayloadKeyList(List<KeyItem<?, ?>> keyList) {
initList(keyList, PayloadKey.getKeyList());
}
public static void initLidarKeyList(List<KeyItem<?, ?>> keyList) {
initList(keyList, LidarKey.getKeyList());
}
private static void initList(List<KeyItem<?, ?>> keyList, List<DJIKeyInfo<?>> keyInfoList) {
if (keyList == null || !keyList.isEmpty()){
return;
}
for (DJIKeyInfo<?> info : keyInfoList) {
KeyItem<DJIValue, DJIValue> item = new KeyItem<>(info);
genericItem(item, info);
keyList.add(item);
}
}
public static <P extends DJIValue, R extends DJIValue> void genericItem(KeyItem<P, R> item, DJIKeyInfo<?> keyInfo) {
Class<?> tdClazz;
Field field = null;
boolean isDjiValue = false;
try {
IDJIValueConverter<P, R> clazzConvert = keyInfo.getTypeConverter();
if (clazzConvert instanceof SingleValueConverter) {
field = clazzConvert.getClass().getDeclaredField("dClass");
Field tmp = clazzConvert.getClass().getDeclaredField("isDJIValue");
tmp.setAccessible(true);
isDjiValue = tmp.getBoolean(clazzConvert);
} else if (clazzConvert instanceof DJIValueConverter) {
field = clazzConvert.getClass().getDeclaredField("tClass");
}
if (field != null) {
field.setAccessible(true);
tdClazz = (Class<?>) field.get(clazzConvert);
item.param = (P) tdClazz.newInstance();
item.result = (R) tdClazz.newInstance();
item.setSingleDJIValue(isDjiValue);
item.initGenericInstance();
}
} catch (Exception e) {
LogUtils.e(TAG, e.getMessage());
}
}
public static int getAllKeyListCount() {
List<KeyItem<?, ?>> allKeyList = new ArrayList<>();
getAllKeyList(allKeyList);
return allKeyList.size();
}
public static void getAllKeyList(List<KeyItem<?, ?>> keylist) {
if (!allKeyList.isEmpty()) {
keylist.addAll(allKeyList);
return;
}
List<KeyItem<?, ?>> keyList = new ArrayList<>();
initBatteryKeyList(keyList);
allKeyList.addAll(keyList);
keyList.clear();
initAirlinkKeyList(keyList);
allKeyList.addAll(keyList);
keyList.clear();
initGimbalKeyList(keyList);
allKeyList.addAll(keyList);
keyList.clear();
initCameraKeyList(keyList);
allKeyList.addAll(keyList);
keyList.clear();
initFlightAssistantKeyList(keyList);
allKeyList.addAll(keyList);
keyList.clear();
initFlightControllerKeyList(keyList);
allKeyList.addAll(keyList);
keyList.clear();
initRemoteControllerKeyList(keyList);
allKeyList.addAll(keyList);
keyList.clear();
initBleKeyList(keyList);
allKeyList.addAll(keyList);
keyList.clear();
initProductKeyList(keyList);
allKeyList.addAll(keyList);
keyList.clear();
initRtkBaseStationKeyList(keyList);
allKeyList.addAll(keyList);
keyList.clear();
initRtkMobileStationKeyList(keyList);
allKeyList.addAll(keyList);
keyList.clear();
initOcuSyncKeyList(keyList);
allKeyList.addAll(keyList);
keyList.clear();
initRadarKeyList(keyList);
allKeyList.addAll(keyList);
keyList.clear();
initMobileNetworkKeyList(keyList);
allKeyList.addAll(keyList);
keyList.clear();
initMobileNetworkLinkRCKeyList(keyList);
allKeyList.addAll(keyList);
keyList.clear();
initOnboardKeyList(keyList);
allKeyList.addAll(keyList);
keyList.clear();
initPayloadKeyList(keyList);
allKeyList.addAll(keyList);
keyList.clear();
initLidarKeyList(keyList);
allKeyList.addAll(keyList);
keyList.clear();
initIntelligentBoxList(keyList);
allKeyList.addAll(keyList);
keyList.clear();
}
}

View File

@@ -0,0 +1,453 @@
package dji.sampleV5.aircraft.keyvalue
import android.content.Context
import android.view.View
import dji.sampleV5.aircraft.util.Util
import dji.v5.utils.common.LogUtils
import java.lang.Exception
import java.lang.StringBuilder
import java.lang.reflect.Field
import java.lang.reflect.ParameterizedType
import java.util.*
/**
* @author feel.feng
* @time 2022/03/10 11:23 上午
* @description:
*/
object KeyItemHelper {
val TAG = LogUtils.getTag(this)
val LISTEN_RECORD_MAX_LENGTH = 2000
val FILED_CHANGE = "\$change"
/**
* 通过反射给字段赋值
*
* @param obj
*/
fun initSubItemData(obj: Any): Map<String?, List<EnumItem>> {
val dataMap: MutableMap<String?, List<EnumItem>> = HashMap()
try {
val fields = obj.javaClass.declaredFields
for (field in fields) {
if (field.name == FILED_CHANGE || field.name == "serialVersionUID") {
continue
}
val clazz = field.type
if (clazz.isEnum) {
dataMap[clazz.canonicalName] =
buildParamsSubItemListWithEnum(field.type as Class<Enum<*>>)
} else if (isEnumList(field)) {
val type = field.genericType
if (type is ParameterizedType) {
val subObject: Class<out Enum<*>> =
type.actualTypeArguments[0] as Class<Enum<*>>
dataMap[clazz.canonicalName] = buildParamsSubItemListWithEnum(subObject)
}
} else {
dataMap.clear()
break
}
}
} catch (e: Exception) {
LogUtils.e(TAG,e.message)
}
return dataMap
}
/**
* 处理界面参数选择和设置
*
* @param anchor
* @param dataMap
*/
fun <P> processSubListLogic(
anchor: View,
param: P,
dataMap: Map<String?, List<EnumItem>>,
callBack: KeyItemActionListener<String?>
) {
try {
val nameList = Util.getMapKeyList(dataMap)
val subItemList = Util.getMapValueList(dataMap)
if (dataMap.size == 1) {
//简单列表
val list = subItemList[0]
val clazz = Class.forName(nameList[0]!!) as Class<Enum<*>>
showSimpleSubItemList(anchor.context, list, clazz, object :
KeyItemActionListener<List<String>?> {
override fun actionChange(t: List<String>?) {
updateClassData(param, dataMap)
callBack.actionChange(param.toString())
}
})
} else {
//复合列表
KeyValueDialogUtil.showListConfirmWindow(
anchor,
getSimpleNameList(nameList),
"select item for setting",
object :
KeyItemActionListener<String?> {
override fun actionChange(msg: String?) {
if ("confirm" == msg) {
updateClassData(param, dataMap)
callBack.actionChange(param.toString())
} else {
val clazz = getClassWithName(msg, nameList) as Class<Enum<*>>?
val list = dataMap[clazz!!.canonicalName]!!
showSimpleSubItemList(
anchor.context,
list,
clazz
) { updateClassData(param, dataMap) }
}
}
})
}
} catch (e: ClassNotFoundException) {
LogUtils.e(TAG , e.message)
}
}
/**
* 类名列表获取通过全名list获取简名list
*
* @param nameList
* @return
*/
fun getSimpleNameList(nameList: List<String?>): List<String> {
val simpleNameList: MutableList<String> = ArrayList()
for (str in nameList) {
simpleNameList.add(str!!.substring(str!!.lastIndexOf(".") + 1))
}
return simpleNameList
}
/**
* 通过简名获取类字节码
*
* @param simpleName
* @param nameList
* @return
*/
fun getClassWithName(simpleName: String?, nameList: List<String?>): Class<*>? {
var clazz: Class<*>? = null
try {
for (str in nameList) {
if (str!!.endsWith(simpleName!!)) {
clazz = Class.forName(str!!)
break
}
}
} catch (e: ClassNotFoundException) {
LogUtils.e(TAG , e.message)
}
return clazz
}
/**
* 构建参数条目的选项子列表
*
* @param <E>
* @return
</E> */
fun <E : Enum<*>?> buildParamsSubItemListWithEnum(clazz: Class<E>): List<EnumItem> {
val list: MutableList<EnumItem> = ArrayList()
try {
var item: EnumItem
val objs: Array<out E>? = clazz.getEnumConstants()
for (obj in objs!!) {
item = EnumItem()
item.setName(obj.toString())
list.add(item)
}
} catch (e: Exception) {
LogUtils.e(TAG,e.message)
}
return list
}
/**
* 获取属性子列表数据
*
* @param data
* @return
*/
fun getSubItemNameList(data: List<EnumItem>): List<String> {
val result: MutableList<String> = ArrayList()
for (item in data) {
result.add(item.getName().toString())
}
return result
}
/**
* 获取选中属性的顺序值
*
* @param data
* @return
*/
fun getSelectedIndex(data: List<EnumItem>): Int {
var index = 0
for (i in data.indices) {
if (data[i].isSelected()) {
index = i
break
}
}
return index
}
/**
* 获取选中属性的value
*
* @param data
* @return
*/
fun getSelectedValue(data: List<EnumItem>): String {
var value = ""
for (i in data.indices) {
if (data[i].isSelected()) {
value = data[i].getName().toString()
break
}
}
return value
}
/**
* 更新选择状态
*
* @param data
* @param name
*/
fun updatedSelectedInfo(data: List<EnumItem>, names: List<String?>) {
for (item in data) {
item.setSelected(false)
for (name in names) {
if (item.getName().equals(name)) {
item.setSelected(true)
}
}
}
}
/**
* 简单参数列表,逻辑处理
*/
fun <E : Enum<*>?> showSimpleSubItemList(
context: Context?,
simpleItemList: List<EnumItem>,
clazz: Class<E>,
callBack: KeyItemActionListener<List<String>?>
) {
val StrList = getSubItemNameList(simpleItemList)
if (StrList.size == 0) {
return
}
val selectedIndex = getSelectedIndex(simpleItemList)
if (clazz.isEnum) {
KeyValueDialogUtil.showSingleChoiceDialog(
context,
StrList,
selectedIndex,
object : KeyItemActionListener<List<String>?> {
override fun actionChange(values: List<String>?) {
updatedSelectedInfo(simpleItemList, values!!)
callBack.actionChange(values)
}
})
} else {
KeyValueDialogUtil.showMultiChoiceDialog(
context,
StrList,
) { values ->
updatedSelectedInfo(simpleItemList, values!!)
callBack.actionChange(values)
}
}
}
/**
* 通过反射给字段赋值
*
* @param obj
*/
fun initClassData(obj: Any?) {
if (obj == null || Util.isBlank(obj.toString())) {
return
}
try {
val pFields = obj.javaClass.declaredFields
for (field in pFields) {
if (field.name == FILED_CHANGE || field.name == "serialVersionUID") {
continue
}
field.isAccessible = true
val clazz = field.type
if (setFieldPro(field , obj)){
continue
}
val subObj = clazz.newInstance()
field[obj] = subObj
initClassData(subObj)
}
} catch (e: Exception) {
LogUtils.e(TAG, e.message)
}
}
fun setFieldPro(field:Field , obj: Any?):Boolean{
val clazz = field.type
if (clazz.isEnum) {
field[obj] = clazz.enumConstants!![0]
return true;
}
if (Util.isWrapClass(clazz)) {
if (clazz == Boolean::class.javaObjectType) {
field[obj] = false
}
//如果需要 可在else 中可给Integer Double 等设置初始值
return true
}
if (clazz == MutableList::class.java) {
field[obj] = ArrayList<Any>()// todo
return true
} else if (clazz == String::class.java) {
field[obj] = ""
return true
}
return false
}
private fun isEnumList(field: Field): Boolean {
if (field.type == MutableList::class.java) {
val type = field.genericType
if (type is ParameterizedType) {
val subType = type.actualTypeArguments[0]
val clazz = subType as Class<*>
if (clazz.isEnum) {
return true
}
}
}
return false
}
/**
* 通过反射给字段赋值
*
* @param obj
*/
fun updateClassData(obj: Any?, subItemMap: Map<String?, List<EnumItem>>) {
try {
val pFields = obj?.javaClass?.declaredFields
if (pFields != null) {
for (field in pFields) {
if (field.name == FILED_CHANGE || field.name == "serialVersionUID") {
continue
}
field.isAccessible = true
val clazz = field.type
if (clazz.isEnum) {
val itemList = subItemMap[clazz.canonicalName]!!
field[obj] = getEnumData(clazz as Class<Enum<*>>, getSelectedValue(itemList))
} else if (isEnumList(field)) {
setEnumListProperty(field , obj , subItemMap)
}
}
}
} catch (e: Exception) {
LogUtils.e(TAG , e.message)
}
}
fun setEnumListProperty(field: Field , obj: Any? , subItemMap: Map<String?, List<EnumItem>>) {
val type = field.genericType
val clazz = field.type
if (type is ParameterizedType) {
val subObject = type.actualTypeArguments[0] as Class<Enum<*>>
val itemList = subItemMap[clazz.canonicalName]
val list: MutableList<Any> = ArrayList()
val values: List<String> = getSelectedValues(itemList!!)
for (value in values) {
val test: Any = getEnumData(subObject, value)!!
list.add(test)
}
field[obj] = list
}
}
fun getSelectedValues(data: List<EnumItem>): List<String> {
var value: String = ""
val values: MutableList<String> = ArrayList()
for (i in data.indices) {
if (data[i].isSelected()) {
value = data[i].getName()!!
values.add(value)
continue
}
}
return values
}
/**
* 追加携带日期的推送字符串
*
* @param targetStr
* @param appendStr
* @return
*/
fun appendListenRecord(targetStr: String, appendStr: String?): String {
if (Util.isBlank(appendStr)) {
return targetStr
}
val sb = StringBuilder(targetStr)
sb.append("\n")
sb.append(Util.getDateStr(Date()) + ":")
sb.append("\n")
sb.append(appendStr)
//长度限制
var result = sb.toString()
if (result.length > LISTEN_RECORD_MAX_LENGTH) {
result = result.substring(result.length - LISTEN_RECORD_MAX_LENGTH)
}
val title = "push info:"
if (!result.startsWith(title)) {
result = """
$title
$result
""".trimIndent()
}
return result
}
/**
* 通过字节码和值来获取实例
*
* @param clazz
* @param value
* @param <E>
* @return
</E> */
fun <E : Enum<*>?> getEnumData(clazz: Class<E>, value: String): E? {
var data: E? = null
try {
val objs: Array<out E>? = clazz.getEnumConstants()
for (obj in objs!!) {
if (obj.toString() == value) {
data = obj as E
break
}
}
} catch (e: Exception) {
LogUtils.e(TAG , e.message)
}
return data
}
}

View File

@@ -0,0 +1,288 @@
package dji.sampleV5.aircraft.keyvalue
import dji.sdk.keyvalue.key.ComponentType
import dji.sdk.keyvalue.key.ProductKey
import dji.sdk.keyvalue.utils.MultiComponentManager
import dji.sdk.keyvalue.value.common.CameraLensType
import dji.sdk.keyvalue.value.product.ProductType
import dji.v5.common.callback.CommonCallbacks
import dji.v5.common.error.DJICommonError
import dji.v5.common.error.IDJIError
import dji.v5.et.create
import dji.v5.et.get
import dji.v5.manager.capability.CapabilityManager
import dji.v5.manager.capability.CapabilityParser
import dji.v5.utils.common.DateUtils
import dji.v5.utils.common.FileUtils
import dji.v5.utils.common.LogUtils
import io.reactivex.rxjava3.core.Completable
import io.reactivex.rxjava3.core.CompletableEmitter
import io.reactivex.rxjava3.schedulers.Schedulers
import java.util.concurrent.CountDownLatch
/**
* @author feel.feng
* @time 2022/10/26 11:18 上午
* @description: key的操作基类
*/
abstract class KeyOperatorCommand(
private val productType: String,
private val componentTypeName: String,
private val componentIndex: Int
) {
private val INTERVAL_TIME = 500L // 连续快速调用key时 可能会导致失败
private val TAG_EQUAL = "=="
private val TAG = LogUtils.getTag(this)
private lateinit var competableEmitter: CompletableEmitter
private var unPassedCount = 0;
private lateinit var curCheckType: KeyCheckType
private var keyCount = 0
private var whiteList = mutableListOf<String>(
"GimbalCalibrationStatus",
"IsShootingPhotoPanorama",
"PhotoPanoramaMode",
"PhotoPanoramaProgress",
"ThermalContrast",
"ThermalDDE",
"ThermalRegionMetersureTemperature",
"ThermalBrightness",
"ThermalGainModeTemperatureRange",
"AircraftLocation3D",
"PhotoRatio"
)
/**
* 过滤Key类型条件
*/
abstract fun filter(item: KeyItem<*, *>): Boolean
/**
* 指定指定动作类型
*/
abstract fun run(item: KeyItem<*, *>)
/**
* 写文件需要
*/
abstract fun getTAG(): String
/**
* Key执行结果回调TAG用来改key执行错误或者失败
*/
abstract fun getErrorTAG(): String
open fun getIntervalTime(): Long {
return INTERVAL_TIME
}
fun execute(): Completable {
return Completable.create { emitter ->
competableEmitter = emitter;
unPassedCount = 0
val allList: MutableList<KeyItem<*, *>> = ArrayList()
KeyItemDataUtil.getAllKeyList(allList)
val capabilityKeyCount =
CapabilityManager.getInstance().getCapabilityKeyCount(productType)
LogUtils.i(TAG, "begin check $capabilityKeyCount")
saveResult(" ----- begin ${getTAG()} check -----\n\n", false, false)
saveResult(" ----- begin ${getTAG()} check -----\n\n", true, false)
allList.filter { item ->
filter(item) && (item.toString() !in whiteList) && CapabilityManager.getInstance().isKeySupported(
productType, componentTypeName, ComponentType.find(item.getKeyInfo().componentType), "Key$item"
)
}.forEach { item ->
LogUtils.e(TAG, "${++keyCount} doCheck $item ")
LogUtils.e(TAG, "Thread name is 1 " + Thread.currentThread().name)
val lock = CountDownLatch(1)
item.componetIndex = if (MultiComponentManager.isMultiKey(item.keyInfo.componentType)) {
componentIndex
} else {
0
}
dependKeySet(item, object : CommonCallbacks.CompletionCallback {
override fun onSuccess() {
//从主线程再切回io线程
LogUtils.e(TAG, "Thread name is 2 " + Thread.currentThread().name)
lock.countDown()
}
override fun onFailure(error: IDJIError) {
LogUtils.e(TAG, "Set $item depend key failed!")
lock.countDown()
}
})
lock.await()
Thread.sleep(getIntervalTime()) // 设置完后立即设置可能会异常如FrequencyBand
run(item)
}
Thread.sleep(getIntervalTime())
saveResult(" --------finish ${getTAG()}---------\n", true, true)
saveResult(" --------finish ${getTAG()}---------\n", false, true)
//遍历完成即完成
competableEmitter.onComplete()
}.subscribeOn(Schedulers.io())
}
/**
* 执行依赖key的 Set方法
*/
private fun dependKeySet(item: KeyItem<*, *>, callback: CommonCallbacks.CompletionCallback) {
// 首先获取具体的依赖的keyitem
val keyName = item.toString()
val valueBean = CapabilityParser.getInstance().getValueBean(keyName)
val dependKeyItem = valueBean?.dependKeyName?.let { CapabilityKeyChecker.getKeyItem(it) }
LogUtils.e(TAG, "dependKeyItem key : " + (valueBean?.dependKeyName ?: "null"))
if (dependKeyItem != null) {
dependKeyItem.setKeyOperateCallBack { res ->
if (res.toString().contains("SetErrorMsg")) {
callback.onFailure(DJICommonError.FACTORY.build(res.toString()))
} else {
callback.onSuccess()
}
}
dependKeyItem.setComponetIndex(item.getComponetIndex())
dependKeyItem.setSubComponetType(item.getSubComponetType())
dependKeyItem.setSubComponetIndex(item.getSubComponetIndex())
dependKeyItem.doSet(valueBean.dependKeyValue.replace("\\", ""))
} else {
// 没有找到前置条件,返回成功。
callback.onSuccess()
}
}
fun doKeyParam(item: KeyItem<*, *>, type: KeyCheckType) {
curCheckType = type
getItemDecoderList(item).forEach {
val lock = CountDownLatch(1)
item.setKeyOperateCallBack {
var result = StringBuilder()
val resStr = it.toString()
val keyNameIndex = resStr.indexOf(getTAG())
if (keyNameIndex <= -1 ) return@setKeyOperateCallBack
val keyName = resStr.substring(0, keyNameIndex)
val isPassed: Boolean
val failedReson = if (resStr.contains(getErrorTAG())) {
isPassed = false
resStr.substring(resStr.indexOf(TAG_EQUAL))
} else {
isPassed = true
resStr.substring(resStr.indexOf(TAG_EQUAL))
}
var componentTYpe = ComponentType.find(item.getKeyInfo().componentType)
result.append("${++unPassedCount} KeyName :${keyName} - ${componentTYpe}\n")
.append("SubType:${getLensName(item)}\n")
.append("Details:${failedReson}\n")
.append("\n ----------------------- \n")
saveResult(result.toString(), isPassed, true)
LogUtils.e(TAG, "SubType:${getLensName(item)} KeyName :${keyName}} ComponentType : $componentTYpe " + resStr)
lock.countDown()
}
item.setComponetIndex(it.componetIndex)
item.setSubComponetType(it.subComponetType)
item.setSubComponetIndex(it.subComponetIndex)
when (type) {
KeyCheckType.ACTION -> item.doAction(it.jsonString)
KeyCheckType.SET -> item.doSet(it.jsonString)
KeyCheckType.GET -> item.doGet()
}
lock.await()
Thread.sleep(getIntervalTime())
}
LogUtils.e(TAG, "check finish!")
}
private fun getLensName(keyItem: KeyItem<*, *>): String {
return if (keyItem.keyInfo.componentType == ComponentType.CAMERA.value()) {
CameraLensType.find(keyItem.getSubComponetType()).name
} else {
"DEFAULT"
}
}
private fun saveResult(content: String, saveType: Boolean, append: Boolean) {
val product = ProductKey.KeyProductType.create().get(ProductType.UNRECOGNIZED)
var filePath = LogUtils.getLogPath() + getTAG() + product.name + "${DateUtils.getSystemTimeOnlyYMD()}"
filePath += if (saveType) {
"Success.txt"
} else {
"Failed.txt"
}
FileUtils.writeFile(filePath, content, append)
}
/**
* 通过key名称从能力集中获取keyItem需要的参数,包括lenstype ,用例json
* 如果在能力集中没有找到用例 需要返回一个默认的(无参的action 返回空)
*/
private fun getItemDecoderList(keyItem: KeyItem<*, *>): MutableList<CapabilityKeyChecker.ItemDecoder> {
val resList: MutableList<CapabilityKeyChecker.ItemDecoder> = ArrayList()
//通过item获取 支持的lensType 列表
var lensTypeList = if (keyItem.keyInfo.componentType == ComponentType.CAMERA.value()) {
CapabilityManager.getInstance().getSupportLens("Key$keyItem", productType, componentTypeName)
} else {
arrayListOf("DEFAULT")
}
//获取javaBean 字符列表 用例中没有文件则返回空集合
var keyParamList = CapabilityKeyChecker.getKeyParamList(keyItem.toString())
// 用例文件存在(set类型 都会有) ;action 不在用例文件中的则不自动测试需要人为测试 支持set get
if (keyParamList.isNotEmpty() || keyItem.canGet()) {
lensTypeList.map {
transCameraLensTypeStr(it)
}.forEach { subComponentType ->
val index = if (MultiComponentManager.isMultiKey(keyItem.keyInfo.componentType)) {
componentIndex
} else {
0
}
if (curCheckType == KeyCheckType.SET || curCheckType == KeyCheckType.ACTION) {
keyParamList
.forEach {
resList.add(
CapabilityKeyChecker.ItemDecoder(
componetIndex = index,
subComponetType = subComponentType,
jsonString = it
)
)
}
} else if (curCheckType == KeyCheckType.GET) {
resList.add(
CapabilityKeyChecker.ItemDecoder(
componetIndex = index,
subComponetType = subComponentType,
jsonString = ""
)
)
}
}
} else {
//添加默认 确保每个key都可以执行到set方法 action无参数不执行(在用例中未找到)
}
return resList
}
/**
* 将能力集中CameraLensType 字符串转为对应的value
*/
fun transCameraLensTypeStr(lensName: String): Int {
CameraLensType.values().forEach {
if (it.name.contains(lensName)) {
return it.value()
}
}
return CameraLensType.UNKNOWN.value()
}
}

View File

@@ -0,0 +1,40 @@
package dji.sampleV5.aircraft.keyvalue
/**
* @author feel.feng
* @time 2022/10/26 2:25 下午
* @description:
*/
class KeySetCommand(
private val productType: String,
private val componentTypeName: String,
private val componentIndex: Int
) : KeyOperatorCommand(productType, componentTypeName, componentIndex) {
private val TAG_GET = "【SET】"
private val TAG_ERROR = "SetErrorMsg"
private val INTERVAL_TIME = 3000L
//&& ("CameraMode" ==item.toString() || "RegionMeteringArea" == item.toString())
override fun filter(item: KeyItem<*, *>): Boolean {
return item.canSet()
}
override fun run(item: KeyItem<*, *>) {
super.doKeyParam(item, KeyCheckType.SET)
}
override fun getTAG(): String {
return TAG_GET
}
override fun getErrorTAG(): String {
return TAG_ERROR
}
override fun getIntervalTime(): Long {
return INTERVAL_TIME
}
}

View File

@@ -0,0 +1,287 @@
package dji.sampleV5.aircraft.keyvalue;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.graphics.drawable.ColorDrawable;
import android.text.Editable;
import android.text.TextWatcher;
import android.text.method.ScrollingMovementMethod;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.PopupWindow;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import dji.sampleV5.aircraft.R;
import dji.sampleV5.aircraft.util.Util;
import dji.v5.utils.common.ContextUtil;
import dji.v5.utils.common.DisplayUtil;
public class KeyValueDialogUtil {
private static final int LIST_Y_OFF_SET = 3;
private KeyValueDialogUtil(){
// init something
}
/**
* 显示单选对话框
*/
public static void showSingleChoiceDialog(Context context, List<String> data, int selectedIndex, final KeyItemActionListener<List<String>> callBack) {
AlertDialog dialog;
final AlertDialog.Builder builder = new AlertDialog.Builder(context);
final String[] items = data.toArray(new String[data.size()]);
builder.setSingleChoiceItems(items, selectedIndex, (dialog1, which) -> {
if (callBack != null) {
callBack.actionChange(Arrays.asList(items[which]));
dialog1.dismiss();
}
});
builder.setCancelable(true);
dialog = builder.create();
dialog.show();
}
public static void showMultiChoiceDialog(Context context, List<String> data, final KeyItemActionListener<List<String>> callBack){
AlertDialog dialog;
List<String> values = new ArrayList<>();
final AlertDialog.Builder builder = new AlertDialog.Builder(context);
final String[] items = data.toArray(new String[data.size()]);
builder.setMultiChoiceItems(items, null, (dialog1, which, isChecked) -> {
if (isChecked) {
values.add(items[which]);
} else {
values.remove(items[which]);
}
});
builder.setPositiveButton(R.string.confirm, (dialog12, which) -> callBack.actionChange(values));
builder.setCancelable(true);
dialog = builder.create();
dialog.show();
}
/**
* 显示简单列表弹窗
*
* @param anchor
* @param data
* @param callback
*/
public static void showListConfirmWindow(View anchor, final List<String> data, String title, final KeyItemActionListener<String> callback) {
if (anchor == null || anchor.getContext() == null) {
return;
}
Context context = anchor.getContext();
View rootView = View.inflate(context, R.layout.dialog_list_confirm, null);
final PopupWindow window = new PopupWindow(context);
window.setWidth(Util.getHeight(ContextUtil.getContext()) / 2);
window.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
window.setOutsideTouchable(false);
window.setTouchable(true);
window.setFocusable(true);
window.setBackgroundDrawable(new ColorDrawable(0xffffff));
window.setContentView(rootView);
window.showAsDropDown(anchor, 0, LIST_Y_OFF_SET, Gravity.CENTER | Gravity.BOTTOM);
ListView listView = rootView.findViewById(R.id.list_view);
TextView titleView = rootView.findViewById(R.id.title);
if (Util.isNotBlank(title)) {
titleView.setText(title);
titleView.setVisibility(View.VISIBLE);
} else {
titleView.setVisibility(View.GONE);
}
ArrayAdapter<String> adapter = new ArrayAdapter<String>(context, R.layout.item_textview, data);
listView.setAdapter(adapter);
listView.setOnItemClickListener((parent, view, position, id) -> callback.actionChange(data.get(position)));
rootView.findViewById(R.id.button).setOnClickListener(v -> {
window.dismiss();
callback.actionChange("confirm");
});
}
/**
* 显示输入确认弹窗
*
* @param context
* @param item
*/
public static void showInputDialog(Activity context, KeyItem<? ,?> item, final KeyItemActionListener<String> callback) {
showInputDialog(context, context.getResources().getString(R.string.key_value_set) + item.getName() + "", item.getParamJsonStr(), "", false, callback);
}
/**
* 显示输入确认弹窗
*
* @param context
* @param title
* @param msg
*/
public static void showInputDialog(Activity context, String title, final String msg, String hint, boolean singleLine, final KeyItemActionListener<String> callback) {
View dialogView = context.getLayoutInflater().inflate(R.layout.dialog_param_input, null);
dialogView.setBackgroundColor(context.getResources().getColor(R.color.gray));
final AlertDialog dialog = new AlertDialog.Builder(context).setView(dialogView).create();
dialog.setCanceledOnTouchOutside(false);
dialog.setCancelable(false);
TextView tvTitle = dialogView.findViewById(R.id.title);
tvTitle.setText(title);
final EditText input = dialogView.findViewById(R.id.input);
input.setSingleLine(singleLine);
if (Util.isNotBlank(msg)) {
input.setText(msg);
}
if (Util.isNotBlank(hint)) {
input.setHint(hint);
}
input.setMovementMethod(ScrollingMovementMethod.getInstance());
dialogView.findViewById(R.id.confirm).setOnClickListener(v -> {
if (callback != null) {
callback.actionChange(input.getText().toString().trim());
}
if (dialog != null) {
dialog.dismiss();
}
});
dialogView.findViewById(R.id.cancel).setOnClickListener(v -> {
if (callback != null) {
callback.actionChange(null);
}
if (dialog != null) {
dialog.dismiss();
}
});
dialog.show();
}
/**
* 显示通用弹窗
*
* @param anchor
* @param data
* @param callback
*/
public static void showFilterListWindow(View anchor, final List<KeyItem<?,?>> data, final KeyItemActionListener<KeyItem<?,?>> callback) {
if (anchor == null || anchor.getContext() == null) {
return;
}
Context context = anchor.getContext();
View rootView = View.inflate(context, R.layout.window_simple_listview, null);
final PopupWindow window = new PopupWindow(context);
window.setWidth(Util.getHeight(ContextUtil.getContext()) / 2);
window.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
window.setOutsideTouchable(true);
window.setTouchable(true);
window.setFocusable(true);
window.setBackgroundDrawable(new ColorDrawable(0xffffff));
window.setContentView(rootView);
window.showAsDropDown(anchor, 0, - DisplayUtil.dip2px(anchor.getContext(), 47), Gravity.LEFT | Gravity.TOP );
ListView listView = rootView.findViewById(R.id.list_view);
TextView titleView = rootView.findViewById(R.id.tv_title);
titleView.setText(R.string.commonlyused_key);
titleView.setVisibility(View.VISIBLE);
ArrayAdapter<KeyItem<?,?>> adapter = new ArrayAdapter<KeyItem<?,?>> (context, R.layout.item_textview, data);
listView.setAdapter(adapter);
listView.setOnItemClickListener((parent, view, position, id) -> {
if (callback != null ) {
callback.actionChange(data.get(position));
}
if (window != null) {
window.dismiss();
}
});
}
/**
* 显示通用弹窗
*
* @param anchor
* @param data
* @param callback
*/
public static void showChannelFilterListWindow(View anchor, final List<ChannelType> data , final KeyItemActionListener<ChannelType> callback) {
if (anchor == null || anchor.getContext() == null) {
return;
}
Context context = anchor.getContext();
View rootView = View.inflate(context, R.layout.window_simple_listview, null);
final PopupWindow window = new PopupWindow(context);
window.setWidth(Util.getHeight(ContextUtil.getContext()) / 2);
window.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
window.setOutsideTouchable(true);
window.setTouchable(true);
window.setFocusable(true);
window.setBackgroundDrawable(new ColorDrawable(0xffffff));
window.setContentView(rootView);
window.showAsDropDown(anchor, (int) (anchor.getWidth() * 1.5), - DisplayUtil.dip2px(anchor.getContext(), 37), Gravity.LEFT | Gravity.BOTTOM);
ListView listView = rootView.findViewById(R.id.list_view);
ArrayAdapter<ChannelType> adapter = new ArrayAdapter<ChannelType>(context, R.layout.item_textview, data);
listView.setAdapter(adapter);
listView.setOnItemClickListener((parent, view, position, id) -> {
if (callback != null ) {
callback.actionChange(data.get(position));
}
if (window != null) {
window.dismiss();
}
});
EditText filter = rootView.findViewById(R.id.et_filter);
filter.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
// dosomething
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// dosomething
}
@Override
public void afterTextChanged(Editable s) {
adapter.getFilter().filter(s.toString());
}
});
}
public static void showNormalDialog(Activity context, String title) {
View dialogView = context.getLayoutInflater().inflate(R.layout.dialog_tips, null);
final AlertDialog dialog = new AlertDialog.Builder(context).setView(dialogView).create();
dialog.setCanceledOnTouchOutside(false);
dialog.setCancelable(false);
TextView tvTitle = dialogView.findViewById(R.id.title);
tvTitle.setText(title);
dialogView.findViewById(R.id.confirm).setOnClickListener(v -> {
dialog.dismiss();
});
dialog.show();
}
}