# APP 操作

在设备开发过程中,你可能想要通过第三方APP来为用户提供服务。我们提供了app_action来支持这样的场景。

消息类型 名称 必须实现
response execute 否,设备需要支持app行为才需实现
check 否,设备需要支持app行为才需实现
request check_result 否,设备需要支持app行为才需实现
execute_succeed 否,设备需要支持app行为才需实现
execute_failed 否,设备需要支持app行为才需实现

# context

"iflyos_context": {
  ...
  "app_action": {    
    "version": "1.2",  //升级一个版本
    "supported_execute": ["activity","broadcast","service","exit"],//支持的操作,数组。如果该字段不出现则代表全部支持。
    "foreground_app": "com.qiyi.video.speaker",
    "activity": "com.qiyi.video.speaker.activitiy.SearchActivityNew"
  },
  ...
}
参数 类型 说明 必填
version String 模块版本,现在是1.2
supported_exetute Array 设备支持的操作,如果该项不出现,代表支持全部类型的操作。可取值: activitybroadcastserviceexit
foreground_app String 当前前景运行的app的包名。如果前景未运行第三方app(运行的是launcher),该字段取值为DEFAULT。若该字段不出现,默认取值为DEFAULT
activity String 当前正在前景运行的页面名称。Android设备请传输当前运行的类名。如果没有第三方app在运行,该项可不出现。

版本说明

版本 说明 更新日期
1.0 基础实现 2019-08-13
1.1 增加execute的类型 2019-09-12
1.2 增加exit相关,增加supported_execute 2019-12-12

# response

# 执行操作

在用户请求命中语料后,云端会返回一类(针对不同版本的APP的)APP的操作数据。该执行列表中,有可能部分执行不成功,部分执行成功。设备需要按照返回顺序执行,若一项执行失败则执行下一项,只要有一项操作执行成功,就无需执行剩下的操作。

标准协议如下

{    
  "iflyos_responses": [      
    ...,      
    {        
      "header": {          
        "name": "app_action.execute"        
      },        
      "payload": {         
        "execution_id": "xxx",         
        "actions": [           
          {             
            "action_id": "",             
            "data": {               
              "type": "activity",
              "uri": "",               
              "package_name": "", 
              "category_name": "",               
              "friendly_name": "",               
              "class_name": "",               
              "action_name": "",               
              "extras": {                 
                "key": "value",               
              }                
            },             
            "version": {                  
              "start": 0,                  
              "end": 11304,          
            }           
          },           
          {             
            "action_id": "",             
            "data": {               
              "type": "activity",
              "uri": "",               
              "package_name": "", 
              "category_name": "",               
              "friendly_name": "",               
              "class_name": "",               
              "action_name": "",               
              "extras": {                 
                "key": "value",               
              }             
            },             
            "version": {                 
              "start": 0,               
              "end": 11304,              
            }           
          }         
        ]        
      }     
    }   
  } 
}
参数 类型 说明 必有
execution_id String 代表这一组执行的 id
actions List action 列表,按顺序执行。执行成功一个 action 后,其他的 action 无需执行。
actions.[i].action_id String 代表这个 action 的唯一标识
actions.[i].data Object 执行 app_action 时需要的 data。
actions.[i].data.package_name String 应用包名,,只有 Android 设备的执行返回才会出现这个字段
actions.[i].data.category_name String category 数据,只有 Android 设备的执行返回才会出现这个字段
actions.[i].data.type String action 的类型,取值:activity,service,broadcast,exit。当不出现的时候,默认取值 activity。只有 Android 设备的执行返回才会出现这个字段
actions.[i].data.friendly_name String 应用显示的名称,一般为中文
actions.[i].data.uri String uri 数据,iOS 设备执行必填,Android 设备非必填。
actions.[i].data.class_name String class 数据,只有 Android 设备的执行返回才会出现这个字段
actions.[i].data.action_name String action 数据,只有 Android 设备的执行返回才会出现这个字段
actions.[i].data.extras Object 该 app_action 的附带信息,只有 Android 设备的执行返回才会出现这个字段
actions.[i].version Object 该 action 支持的版本范围,用户设备对比本地的 APP 版本。若本地 APP 版本不在这个范围内,可以选择不执行。如果是 iOS 设备,这个字段不会出现。
actions.[i].version.start Long 支持该 action 的最小版本,若未填写,默认为 0
actions.[i].version.end Long 支持该 action 的最大版本,若未填写,代表支持版本无限大。

# 收到指令后设备的执行逻辑

注意

收到的response中,package_name不一定与当前设备前景运行的app一致。

不同操作系统收到的 app_action 有所不同,下面是一些简单的示例,但不包含对应用版本的判断执行。

Android

若设备系统为 Android,则可能收到形如以下格式的 app_action 数据。

{
  // 其他属性
  "data": {               
    "type": "activity",
    "uri": "URI_SAMPLE",               
    "package_name": "PACKAGE_NAME_SAMPLE", 
    "category_name": "CATEGORY_NAME_SAMPLE",               
    "friendly_name": "FRIENDLY_NAME_SAMPLE",               
    "class_name": "CLASS_NAME_SAMPLE",               
    "action_name": "ACTION_NAME_SAMPLE",               
    "extras": {                 
      "EXTRA_KEY_SAMPLE_1": "EXTRA_VALUE_SAMPLE_1", 
      "EXTRA_KEY_SAMPLE_2": "EXTRA_VALUE_SAMPLE_2", 
      // 其他的 extra              
    }             
  }
  // 其他属性
}

那么对于这些数据,设备端应该执行类似下方的处理

val intent = Intent()
intent.data = Uri.parse(URI_SAMPLE)
intent.setPackage(PACKAGE_NAME_SAMPLE)
intent.setClassName(PACKAGE_NAME_SAMPLE, CLASS_NAME_SAMPLE)
// 上述两步也可换为 intent.component = ComponentName(PACKAGE_NAME_SAMPLE, CLASS_NAME_SAMPLE)
intent.addCategory(CATEGORY_NAME_SAMPLE)
intent.putExtra(EXTRA_KEY_SAMPLE_1, EXTRA_VALUE_SAMPLE_1)
intent.putExtra(EXTRA_KEY_SAMPLE_2, EXTRA_VALUE_SAMPLE_2)
when (data.type) {
  "activity" -> {
    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
    context.startActivity(intent)
  }
  "service" -> {
    context.startService(intent)
  }
  "broadcast" -> {
    context.sendBroadcast(intent)
  }
}

responsedata 不包含某些字段,则相当于上述代码对应的那一行不执行(例如若不存在 category_name 属性,则 intent.addCategory(...) 不被调用)。

iOS

若设备为 iOS 系统,那么对于收到 app_actionresponsedata 中只存在 uri 字段。设备端对于 uri 字段做如下处理。

if (@available(iOS 10.0, *)) {
    // ios 10 及以后
    if ([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:uri]]) {
        [[UIApplication sharedApplication] openURL:[NSURL URLWithString:uri] options:@{} completionHandler:nil];
    } else {
        // 执行无法打开应用的逻辑
    }
} else {
    // ios 10 之前
    if ([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:uri]]) {
        [[UIApplication sharedApplication] openURL:[NSURL URLWithString:uri]];
    } else {
        // 执行无法打开应用的逻辑
    }
}

# 通用的应用退出方式

若你的设备不支持exit操作,但用户请求退出某一个APP,你可能会收到以下response


{
  "iflyos_responses":[
    {
      "header":{
          "name":"app_action.execute"
      },
      "payload":{
        "execution_id":"xxx",
        "actions":[
          {
            "action_id":"",
            "data":{
              "type":"activity",
              "action_name":"android.intent.action.MAIN",
              "category_name":"android.intent.category.HOME"
            }
          }
        ]
      }
    }
  ]
}

# 判断是否能执行

为了更好的用户体验,云端可能会发送这个 response 来确定设备是否能够支持某些 execute,设备需要正确处理这个 response,并返回正确的结果。如果设备告知云端支持该 execute,云端会再下发 executeresponse

在收到这个 response 时,你需要检查每一项是否能正确执行,并返回结果。

返回示例

{   
  "iflyos_responses": [     
    ...,     
    {       
      "header": {         
        "name": "app_action.check"      
      },       
      "payload": {         
        "check_id": "xxx",         
        "actions": [           
          {             
            "action_id": "xxxxx",              
            "data": {               
              "package_name": "",               
              "uri": ""             
            }           
          },           
          {             
            "action_id": "xxxxx",              
            "data": {               
              "package_name": "",              
              "uri": ""             
            }           
          },         
        ],       
      }     
    }   
  ] 
}

# request

# 执行成功

返回的一系列actions中,只要有一个action执行成功,即执行成功,发送执行成功的请求,带上执行成功的action的id。

请求示例

{    
  "iflyos_header": {...},    
  "iflyos_context": {...},    
  "iflyos_request": {      
    "header": {        
      "name": "app_action.execute_succeed",        
      "request_id": "xxxxxxxx"      
    },      
    "payload": {        
      "action_id": "xxxxx",        
      "feedback_text": "xxxxx"      
    }    
  }  
}
参数 类型 说明 必填
iflyos_header Object 构建的通用 iflyos_header
iflyos_context Object 构建的通用 iflyos_context
action_id String 执行成功的action的唯一标识
feedback_text String 执行失败反馈给用户的语音提示,iFLYOS收到后将进行语音合成并下发至设备,最多100个字符。若该字段不出现,云端将反馈默认回复。字段出现但取值为空,云端将不会回复。

# 执行失败

云端返回的一系列action中,没有一个action执行成功,则返回执行失败。

请求示例

{    
  "iflyos_header": {...},    
  "iflyos_context": {...},    
  "iflyos_request": {      
    "header": {        
      "name": "app_action.execute_failed",        
      "request_id": "xxxxxxxx"      
    },      
    "payload": {        
      "execution_id": "xxxxx",        
      "failure_code": "xxxx",        
      "feedback_text": "xxxxx"      
    }    
  }  
}
参数 类型 说明 必填
iflyos_header Object 构建的通用 iflyos_header
iflyos_context Object 构建的通用 iflyos_context
action_id String 执行失败的action的唯一标识
failure_code String 失败类型代码,详见下方
feedback_text String 执行失败反馈给用户的语音提示,iFLYOS收到后将进行语音合成并下发至设备,最多100个字符。若该字段不出现,云端将反馈默认回复。字段出现但取值为空,云端将不会回复。

错误类型

提示

若多个执行失败的原因不同,则按照错误类型的优先级返回优先级高的失败原因。

说明 描述
ACTION_UNSUPPORTED 不支持这个action
APP_NOT_FOUND 客户端没有这个app
INTERNAL_ERROR 内部错误

# 检查结果

在检查设备是否可以执行该操作之后,需要把检查结果发送至云端。

{    
  "iflyos_header": {...},    
  "iflyos_context": {...},    
  "iflyos_request": {      
    "header": {        
      "name": "app_action.check_result",        
      "request_id": "xxxxxxxx"      
    },      
    "payload": {        
      "check_id": "xxxxx", 
      "actions": [
        {
          "action_id": "xxxxx",
          "result": true
        },
        {
          "action_id": "xxxxx",
          "result": false
        }
      ]         
    }    
  }  
}