APP自定义按键的demo
📎 原始文档:https://www.kdocs.cn/l/ccQQJb6UcJUv APP自定义按键的demo
demo代码
【金山文档】 app_define_key (普通SDK)
https://kdocs.cn/l/cvG7yfD5uZpA
【金山文档】 app_define_key_1t2(可视化SDK)
https://kdocs.cn/l/ccJmcq29RE9i
使用方法详见以上demo内的注释
支持的功能:
- 支持多种场景下的按键自定义 例如通话场景、播歌场景,通过APP_KEY_SENCE枚举定义场景,在key_2_func函数中识别当前场景。
/**
* @brief APP定义的按键场景,根据APP定义修改
*
*/
typedef enum {
KEY_SENCE_CALL_IN, //来电场景
KEY_SENCE_HANGUP, //非通话场景
KEY_SENCE_ALL, //任何场景
KEY_SENCE_CALLING, //通话中场景
KEY_SENCE_MAX,
}APP_KEY_SENCE;
static u8 key_2_func(APP_KEY_CHANNEL channel, APP_KEY_BUTTON button, APP_KEY_ACTION action)
{
APP_KEY_SENCE sence = KEY_SENCE_MAX;
switch (get_call_status()) //这个switch要根据项目APP的场景改,完成耳机状态到APP场景的转换
{
case BT_CALL_HANGUP:
sence = KEY_SENCE_HANGUP;//非通话场景
break;
case BT_CALL_ACTIVE:
// sence = KEY_SENCE_CALLING;
// break;
case BT_CALL_INCOMING:
sence = KEY_SENCE_CALL_IN;//通话场景
break;
default:
break;
}
//......下面的代码省略,具体看demo
}
- 支持添加多种按键动作 通过APP_KEY_ACTION枚举定义按键动作,在app_key_deal函数中识别当前按键动作
/**
* @brief APP定义的按键动作,根据APP定义修改
*
*/
typedef enum {
KEY_ACTION_1_CLICK, //单击
KEY_ACTION_2_CLICK, //双击
KEY_ACTION_LONG, //长按
KEY_ACTION_3_CLICK, //三击
KEY_ACTION_MAX,
}APP_KEY_ACTION;
//app过来的按键事件跟耳机本地的按键事件值可能不一样,用这个数组转换一下
const APP_KEY_ACTION local_action_to_ext_action[KEY_EVENT_MAX] = {
[KEY_EVENT_CLICK] = KEY_ACTION_1_CLICK,
[KEY_EVENT_DOUBLE_CLICK] = KEY_ACTION_2_CLICK,
[KEY_EVENT_TRIPLE_CLICK] = KEY_ACTION_3_CLICK,
};
u8 app_key_deal(u8 from_tws, u8 key_value, u8 key_status)
{
//......上面的代码省略,具体看demo
case KEY_EVENT_DOUBLE_CLICK:
case KEY_EVENT_TRIPLE_CLICK:
key_action = local_action_to_ext_action[key_status];
//......中间的代码省略,具体看demo
case KEY_EVENT_LONG:
key_action = KEY_ACTION_LONG;
//......下面的代码省略,具体看demo
}
- 支持添加多个按键值 通过APP_KEY_BUTTON枚举定义按键值,在app_key_deal函数中识别当前按键值
/**
* @brief APP定义的按键值,根据APP定义修改
*
*/
typedef enum {
KEY_BUTTON_1,
// KEY_BUTTON_2,
// KEY_BUTTON_3,
// KEY_BUTTON_4,
KEY_BUTTON_MAX,
}APP_KEY_BUTTON;
u8 app_key_deal(u8 from_tws, u8 key_value, u8 key_status)
{
//......上面的代码省略,具体看demo
APP_KEY_BUTTON button = key_value;//本地按键值转APP按键值(根据APP修改,APP没有按键值定义的可以不改)
//......下面的代码省略,具体看demo
}
- 支持添加多种操作类型 比如左边操作按键、右边操作按键、左右同时操作按键等,通过APP_KEY_CHANNEL枚举定义操作类型,在app_key_deal函数中识别当前操作类型
/**
* @brief APP定义的按键按下类别,根据APP定义修改
*
*/
typedef enum {
KEY_CHANNEL_L, //左耳按下
KEY_CHANNEL_R, //右耳按下
KEY_CHANNEL_MAX,//把MAX放在这里,表示下面的功能关闭,要打开或者新增功能,要放在MAX上面
KEY_CHANNEL_B, //盒子按下,此项目不用
KEY_CHANNEL_LR, //左右耳一起按下,此项目不用
}APP_KEY_CHANNEL;
u8 app_key_deal(u8 from_tws, u8 key_value, u8 key_status)
{
u8 channel = KEY_CHANNEL_LR;
//......上面的代码省略,具体看demo
if (get_bt_tws_connect_status()) {
channel = tws_api_get_local_channel();
if ('L' == channel) {
channel = from_tws ? KEY_CHANNEL_R : KEY_CHANNEL_L;
} else {
channel = from_tws ? KEY_CHANNEL_L : KEY_CHANNEL_R;
}
}
else{
channel = tws_api_get_local_channel() == 'L' ? KEY_CHANNEL_L : KEY_CHANNEL_R;
}
//......下面的代码省略,具体看demo
}
- 支持添加自定义按键功能 在Function枚举中定义APP端的按键功能,在app_key_func_deal函数中实现功能
/**
* @brief APP定义的按键功能,根据APP定义修改
*
*/
typedef enum {
CANCLE_OPTION, //关闭操作
CALL_ANSWER, //接听电话
CALL_REJECT_HANGUP, //拒接或挂断电话
MUSIC_PP, //暂停播放
MUSIC_PREV, //上一曲
MUSIC_NEXT, //下一曲
START_SIRI, //唤醒语音助手
SWITCH_ANC, //降噪模式切换
VOLUME_UP, //增大音量
VOLUME_DOWN, //减小音量
CALL_ANSWER_HANGUP, //接听或挂断电话
CALL_REJECT, //拒接电话
CALL_HANGUP, //挂断电话
GAME_MODE, //游戏模式
/******************以下暂时不用*********************/
RANDOM_MUSIC, //随心听歌
START_GOOGLE, //Google语音助手
SET_VOLUME, //调节音量
PLAY_COLLECTION, //播放收藏歌曲
PREV_OR_NEXT, //上一曲/下一曲
FORCE_CONN, //抢连
SECOND_CALL_HOLD_ON, //保持三方通话
}Function;
- 支持设置默认按键功能 首次开机,没有连接APP修改按键时,使用默认的按键功能,通过以下数组设定默认的按键功能,通过app_key_reset函数重置按键功能到默认。
不需要默认功能时,将对应操作下的功能改为CANCLE_OPTION。
//对应默认的功能UI,第一次开机或者恢复出厂设置时使用
const u8 default_key_info_call_in[KEY_CHANNEL_MAX * KEY_ACTION_MAX][4] = { //对应来电场景的默认按键功能
{KEY_CHANNEL_L, KEY_BUTTON_1, KEY_ACTION_1_CLICK, (VOLUME_DOWN)}, //左耳按键单击音量减
{KEY_CHANNEL_R, KEY_BUTTON_1, KEY_ACTION_1_CLICK, (VOLUME_UP)}, //右耳按键单击音量加
{KEY_CHANNEL_L, KEY_BUTTON_1, KEY_ACTION_2_CLICK, (CALL_ANSWER_HANGUP)},//左耳按键双击接挂电话
{KEY_CHANNEL_R, KEY_BUTTON_1, KEY_ACTION_2_CLICK, (CALL_ANSWER_HANGUP)},//右耳按键双击接挂电话
{KEY_CHANNEL_L, KEY_BUTTON_1, KEY_ACTION_3_CLICK, (CANCLE_OPTION)}, //左耳按键三击无功能
{KEY_CHANNEL_R, KEY_BUTTON_1, KEY_ACTION_3_CLICK, (CANCLE_OPTION)}, //右耳按键三击无功能
{KEY_CHANNEL_L, KEY_BUTTON_1, KEY_ACTION_LONG, (CALL_REJECT)}, //左耳按键长按拒接
{KEY_CHANNEL_R, KEY_BUTTON_1, KEY_ACTION_LONG, (CALL_REJECT)}, //右耳按键长按拒接
// {KEY_CHANNEL_LR, KEY_BUTTON_1, KEY_ACTION_LONG, (GAME_MODE)},
};
const u8 default_key_info_hangup[KEY_CHANNEL_MAX * KEY_ACTION_MAX][4] = { //对应非通话场景的默认按键功能
{KEY_CHANNEL_L, KEY_BUTTON_1, KEY_ACTION_1_CLICK, (VOLUME_DOWN)},
{KEY_CHANNEL_R, KEY_BUTTON_1, KEY_ACTION_1_CLICK, (VOLUME_UP)},
{KEY_CHANNEL_L, KEY_BUTTON_1, KEY_ACTION_2_CLICK, (MUSIC_PP)},
{KEY_CHANNEL_R, KEY_BUTTON_1, KEY_ACTION_2_CLICK, (MUSIC_PP)},
{KEY_CHANNEL_L, KEY_BUTTON_1, KEY_ACTION_3_CLICK, (GAME_MODE)},
{KEY_CHANNEL_R, KEY_BUTTON_1, KEY_ACTION_3_CLICK, (START_SIRI)},
{KEY_CHANNEL_L, KEY_BUTTON_1, KEY_ACTION_LONG, (MUSIC_PREV)},
{KEY_CHANNEL_R, KEY_BUTTON_1, KEY_ACTION_LONG, (MUSIC_NEXT)},
// {KEY_CHANNEL_LR, KEY_BUTTON_1, KEY_ACTION_LONG, (GAME_MODE)},
};
const u8 default_key_info_all[KEY_CHANNEL_MAX * KEY_ACTION_MAX][4] = { //对应任何场景的默认按键功能
{KEY_CHANNEL_L, KEY_BUTTON_1, KEY_ACTION_1_CLICK, (CANCLE_OPTION)},
{KEY_CHANNEL_R, KEY_BUTTON_1, KEY_ACTION_1_CLICK, (CANCLE_OPTION)},
{KEY_CHANNEL_L, KEY_BUTTON_1, KEY_ACTION_2_CLICK, (CANCLE_OPTION)},
{KEY_CHANNEL_R, KEY_BUTTON_1, KEY_ACTION_2_CLICK, (CANCLE_OPTION)},
{KEY_CHANNEL_L, KEY_BUTTON_1, KEY_ACTION_3_CLICK, (CANCLE_OPTION)},
{KEY_CHANNEL_R, KEY_BUTTON_1, KEY_ACTION_3_CLICK, (CANCLE_OPTION)},
{KEY_CHANNEL_L, KEY_BUTTON_1, KEY_ACTION_LONG, (CANCLE_OPTION)},
{KEY_CHANNEL_R, KEY_BUTTON_1, KEY_ACTION_LONG, (CANCLE_OPTION)},
// {KEY_CHANNEL_LR, KEY_BUTTON_1, KEY_ACTION_LONG, (GAME_MODE)},
};
const u8 default_key_info_calling[KEY_CHANNEL_MAX * KEY_ACTION_MAX][4] = { //对应通话中场景的默认按键功能
{KEY_CHANNEL_L, KEY_BUTTON_1, KEY_ACTION_1_CLICK, (VOLUME_DOWN)},
{KEY_CHANNEL_R, KEY_BUTTON_1, KEY_ACTION_1_CLICK, (VOLUME_UP)},
{KEY_CHANNEL_L, KEY_BUTTON_1, KEY_ACTION_2_CLICK, (CALL_ANSWER_HANGUP)},
{KEY_CHANNEL_R, KEY_BUTTON_1, KEY_ACTION_2_CLICK, (CALL_ANSWER_HANGUP)},
{KEY_CHANNEL_L, KEY_BUTTON_1, KEY_ACTION_3_CLICK, (CANCLE_OPTION)},
{KEY_CHANNEL_R, KEY_BUTTON_1, KEY_ACTION_3_CLICK, (CANCLE_OPTION)},
{KEY_CHANNEL_L, KEY_BUTTON_1, KEY_ACTION_LONG, (CALL_REJECT)},
{KEY_CHANNEL_R, KEY_BUTTON_1, KEY_ACTION_LONG, (CALL_REJECT)},
// {KEY_CHANNEL_LR, KEY_BUTTON_1, KEY_ACTION_LONG, (GAME_MODE)},
};
//对应APP_KEY_SENCE的APP场景
const u8 **default_key_info[KEY_SENCE_MAX] = {
default_key_info_call_in,
default_key_info_hangup,
default_key_info_all,
default_key_info_calling,
};
接口使用方法
- 运行自定义按键的流程
普通SDK:
extern u8 app_key_deal(u8 from_tws, u8 key_value, u8 key_status);
if(app_key_deal((u32)event->arg == KEY_EVENT_FROM_TWS, key->value, key->event)){
printf("app define, break sdk default func\n");
return ret;
}

可视化SDK:
见demo
2. app修改耳机的按键功能 使用函数:u8 extern_set_key_func(u8 ch, u8 sence, u8 key_value, u8 key_status, u8 func)
APP需要提供的信息:
1. 此次修改的是左耳还是右耳,对应得到ch,非TWS传KEY_CHANNEL_R 2. 此次修改的是什么场景下的按键功能,对应得到sence,不涉及可传KEY_SENCE_ALL 3. 此次修改的是哪个按键,对应得到key_value,只有一个按键一般填0 4. 此次修改的是哪个按键动作,对应得到key_status 5. 此次修改需要按键实现什么功能,对应得到func
/**
* @brief 给APP修改按键的接口
*
* @param ch APP_KEY_CHANNEL
* @param sence APP_KEY_SENCE
* @param key_value APP_KEY_BUTTON
* @param key_status APP_KEY_ACTION
* @param func Function
* @return u8 1:按键冲突;0:正常设置
*/
u8 extern_set_key_func(u8 ch, u8 sence, u8 key_value, u8 key_status, u8 func)
{
if(ch >= KEY_CHANNEL_MAX || sence >= KEY_SENCE_MAX || key_value >= KEY_BUTTON_MAX || key_status >= KEY_ACTION_MAX){
return 1;
}
return extern_define_key_func(ch, sence, key_value, key_status, func);
}
- app获取(耳机上报)自定义按键功能 使用函数:u8 extern_get_key_func(u8 ch, u8 sence, u8 key_value, u8 key_status)
获取某个自定义按键实现的功能,需要提供信息:- 左耳还是右耳,对应得到ch
- 什么场景下的按键功能,对应得到sence
- 哪个按键,对应得到key_value
- 哪个按键动作,对应得到key_status 以上信息对应的按键功能通过函数的返回值返回
/**
* @brief 给APP读取按键的接口
*
* @param ch APP_KEY_CHANNEL
* @param sence APP_KEY_SENCE
* @param key_value APP_KEY_BUTTON
* @param key_status APP_KEY_ACTION
* @return u8 Function
*/
u8 extern_get_key_func(u8 ch, u8 sence, u8 key_value, u8 key_status)
{
if(ch >= KEY_CHANNEL_MAX || sence >= KEY_SENCE_MAX || key_value >= KEY_BUTTON_MAX || key_status >= KEY_ACTION_MAX){
return 0;
}
#if APP_DEFINE_KEY_EN
struct extern_define_key *define_key_p = &define_key[ch];
u8 func = define_key_p->func[sence][key_value][key_status].func;
printf("sence %s, channel %c, key %d, action %d, func %d\n", SENCE_NAME[sence], ch ? 'R' : 'L', key_value, key_status, func);
return func;
#endif
return 0;
}
//demo:可通过轮询定义的各个项目(操作类别、场景、按键值、按键动作)获得自定义按键的具体信息
/**
* @brief 上报耳机本地保存的自定义按键,此为demo,需根据APP协议修改
*
* @param cmd
*/
void key_data_rsp(u8 cmd)
{
extern u8 extern_get_key_func(u8 ch, u8 sence, u8 key_value, u8 key_status);
u8 func_array[16];
u8 func_num = 0;
u8 function;
for (u8 sence = 0; sence < 4; sence++) //按场景轮询
{
for (u8 ch = 0; ch < 2; ch++) //按左右耳轮询
{
for (u8 key_value = 0; key_value < 1; key_value++) //按按键值轮询
{
for (u8 key_status = 0; key_status < 4; key_status++) //按按键事件轮询
{
function = extern_get_key_func(ch, sence, key_value, key_status);
if(function){//拿到功能
u8 app_key = 2 * key_status + ch;//转换为APP按键,比如左耳,长按
func_array[func_num++] = (app_key << 4) | function;//按照键值对的形式组合[按键:功能值],比如左耳长按:音量加,缓存到数组中
}
}
}
}
//将当前场景下的按键上报给APP,此为demo,需根据APP协议修改
struct sound_peats_parse data;
data.sof = SOF;
data.length = 5 + func_num;
data.cmd = (u8)cmd;
data.flag = 0x00;
data.pkg_num = sence;
u8 rsp_buffer[sizeof(data) - 1 + 5 + func_num];
u8 rsp_len = sizeof(data) - 1;
memcpy(rsp_buffer, (u8 *)&data, sizeof(data) - 1);
rsp_buffer[rsp_len++] = BIT(sence);
rsp_buffer[rsp_len++] = 0xff;
rsp_buffer[rsp_len++] = 0x1f;
rsp_buffer[rsp_len++] = 0xff;
rsp_buffer[rsp_len++] = 0x00;
if(func_num){
memcpy(rsp_buffer + rsp_len, func_array, func_num);
rsp_len += func_num;
}
puts("send:");
put_buf(rsp_buffer, rsp_len);
multi_spp_send_data(0x0A, 0, rsp_buffer, rsp_len);//发数据
func_num = 0;
}
}
- 保存自定义按键 需要在include_lib\system\syscfg_id.h中定义一个新的VM索引(不要跟SDK已有的重复)
例如:#define CFG_USER_CY_KEY 2
关机时调用一下app_key_write_vm保存到VM。
/**
* @brief 自定义按键保存到VM
*
*/
void app_key_write_vm(void)
{
#if APP_DEFINE_KEY_EN
syscfg_write(CFG_USER_CY_KEY, define_key, sizeof(define_key));
#endif
}
