当前位置:首页 » APP上架 » 正文

小米MIUI 小部件系统能力说明

小米MIUI 小部件系统能力说明

1.调起 MIUI Widget 商店里的详情页,添加应用的 MIUI Widget

1.1.描述

启动 Widget 详情页,详情页会包含该 App 通过审核的Widget。用户可以左右滑动预览,并选择其中一个添加到桌面。

小米MIUI 小部件系统能力说明小米MIUI 小部件系统能力说明

1.2.调用方式

关键方法
appWidgetManager.requestPinAppWidget(myProvider, extras, null)
使用 extras 携带参数 
addType: appWidgetDetail
widgetName: 小部件name,可选参数,用来指定打开详情页后定位到的组件。如果不填,默认定位到第一个。
widgetExtraData: 用于携带自定义参数,携带自定义参数类型只能是String,最多携带5个,超过5个所有携带的自定义参数都将被抛弃。

注意:该方法仅支持 Android 8.0 及以上系统。不支持 MIUI Widget 的手机调用requestPinAppWidget 方法不会调起 Widget 商店里的详情页。

1.3.示例

public void startAppWidgetPage(Context context){
    AppWidgetManager appWidgetManager =  
    AppWidgetManager.getInstance(context);
    ComponentName myProvider = new ComponentName(this,  
    ExampleWidgetProvider.class);
    if (appWidgetManager.isRequestPinAppWidgetSupported()) {
        Bundle extras = new Bundle();
        extras.putString("addType","appWidgetDetail");
        // packageName 为应用真实包名
⁣        extras.putString("widgetName",
            "packageName/com.miui.ExampleWidgetProvider");
        Bundle dataBundle = new Bundle();
        dataBundle.putString("dataKey1","data1xxx");
        dataBundle.putString("dataKey2","data2xxx");
        dataBundle.putString("dataKey3","data3xxx");
        dataBundle.putString("dataKey4","data4xxx");
        dataBundle.putString("dataKey5","data5xxx");
        extras.putBundle("widgetExtraData", dataBundle);
        appWidgetManager.requestPinAppWidget(myProvider, extras, null);
    }
}

// 获取自定义参数
public class ExampleWidgetProvider extends AppWidgetProvider {
 
   @Override
   public void onAppWidgetOptionsChanged(Context context, AppWidgetManager
     appWidgetManager, int appWidgetId, Bundle newOptions) {
         super.onAppWidgetOptionsChanged(context, appWidgetManager, 
         appWidgetId, newOptions);
        if(extras != null){
            Bundle dataBundle = extras.getBundle("widgetExtraData");
            dataBundle.getString("xxxkey");
        }   
    }    
}

2.设置 MIUI Widget卡片状态

2.1.描述

MIUI Widget 也可以在智能助理(负一屏)进行展示。应用可以选择向系统发送卡片的优先级状态,MIUI智能助理(负一屏)会根据卡片状态进行动态的排序。

2.2.调用方式

key:    miuiWidgetEventCode     
类型:    String
描述:   事件code命名规则请参考code事件表
key:    miuiWidgetTimestamp
类型:   String
描述:   状态变化时的时间戳 
appWidgetManager.updateAppWidgetOptions(widgetId, options);
appWidgetManager.updateAppWidget(widgetId, views);

2.3.code 事件表

事件code命名规则请参考下表,具体事件与code对应关系以双方沟通结果为准
*新增或修改事件需与MIUI商务同学联系,上报未确认的事件code不会生效,恶意错报被系统识别后会降低推荐权重
核心场景 事件 事件code 备注
金融
股票证券
小部件内有股票/基金在交易 opening
小部件内无股票/基金在交易 closing
出行 通勤时间 commuting
非通勤时间 other
直播/电台 小部件内展示内容“直播中” live1 不同的事件可用live1、live2…区分
小部件内展示内容“重播中” replay
小部件内展示内容“未开播” other
连接状态 已连接 connected
未连接 disconnect
通知提醒 小部件内展示有通知提醒提醒 notice1 不同的事件可用notice1、notice2…区分
进度状态 如“打车进度”“下单进度”等 progress1 不同的事件可用progress1、progress2…区分
内容资讯 如“今日热门”“话题榜”“热搜榜”等 info1 不同的事件可用info1、info2…区分
功能跳转 小部件为纯工具类的功能跳转 state1 不同的事件可用state1、state2…区分
其他 小部件的兜底展示方案或其他状态 other

2.4.示例

  • 普通 Widget :在 AppWidgetProvider 的 onUpdate 方法,调用 updateAppWidgetOptions 方法。
public class ExampleWidgetProvider extends AppWidgetProvider {
    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        // todo 准备待更新数据
        ....
        // 更新数据
        for (int appWidgetId : appWidgetIds) {
            RemoteViews views = new RemoteViews(context.getPackageName(), 
            R.layout.widget_ui);
            Bundle options = new Bundle();
            options.putString("miuiWidgetEventCode", "notice1");
            long currentTimeMillis = System.currentTimeMillis();
            options.putString("miuiWidgetTimestamp",
            String.valueOf(currentTimeMillis));
            appWidgetManager.updateAppWidgetOptions(widgetId, options);
            appWidgetManager.updateAppWidget(widgetId, views);
        }
    }
}
  • 列表 Widget : notifyAppWidgetViewDataChanged 更新后调用 updateAppWidget 和updateAppWidgetOptions 方法
public class ExampleWidgetProvider extends AppWidgetProvider {
    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        for (int appWidgetId : appWidgetIds) {
            // todo 准备待更新数据
            ...
            appWidgetManager.notifyAppWidgetViewDataChanged (mAppWidgetId,R.id.content);
            // 更新数据后进行状态更新
            Bundle options = new Bundle();
            options.putString("miuiWidgetEventCode", "notice1");
            long currentTimeMillis = System.currentTimeMillis();
            options.putString("miuiWidgetTimestamp",
            String.valueOf(currentTimeMillis));
            appWidgetManager.updateAppWidgetOptions(mAppWidgetId, options);
            RemoteViews remoteViews = new RemoteViews(mContext.getPackageName(),             R.layout.widget_list_example);
            appWidgetManager.updateAppWidget(mAppWidgetId, remoteViews);
        }
    }
}

注意:updateAppWidgetOptions 需要在 updateAppWidget ()方法前,并且两者需要同一线程。

3.设置编辑页

3.1.描述

MIUI Widget 额外提供进入编辑页的入口。用户点击编辑按钮,可以进入编辑页进行相应的设置。

3.2.调用方法与参数

Bundle options = appWidgetManager.getAppWidgetOptions(appWidgetId);
String uriPath = "widgetdemo://com.miui.widgetdemo/widget/WidgetEditActivity";
options.putString("miuiEditUri", uriPath);
appWidgetManager.updateAppWidgetOptions(appWidgetId, options);
appWidgetManager.updateAppWidget(widgetId, views);

3.3.示例

public class ExampleWidgetProvider extends AppWidgetProvider {
    @Override
    public void onUpdate(Context context, AppWidgetManager 
        appWidgetManager, int[] appWidgetIds) {
        for (int appWidgetId : appWidgetIds) {          
            Bundle options =   
            appWidgetManager.getAppWidgetOptions(appWidgetId);
            String uriPath =            "widgetdemo://com.miui.widgetdemo/widget/WidgetEditActivity";
            options.putString("miuiEditUri", uriPath);
            appWidgetManager.updateAppWidgetOptions(appWidgetId, options);
            RemoteViews views =   
            new RemoteViews(context.getPackageName(), 
            R.layout.widget_ui);
            appWidgetManager.updateAppWidget(widgetId, views);
        }
        ....
    }
}
// 通过 intent 可以获取点击的appWidgetId
public class WidgetEditActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        Intent intent = getIntent();
        int appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
    }
}

4.同功能 MIUI Widget 聚合

4.1.描述

MIUI Widget 可以把不同尺寸相同功能的Widget 在详情页中聚合在一块显示,用户可以根据需求添加相应尺寸的Widget。

4.2.调用方式

在AndroidManifest.xml 文件中 AppWidgetProvider 对应的receiver 如果label 的名称相同的话将会被认为是同一功能的不同尺寸。

4.3.示例

<receiver
    android:name="com.miui.widgetdemo.provider.ExampleWidgetProvider1"
    android:label="@string/app_widget_group">
    ...
</receiver>

<receiver
    android:name="com.miui.widgetdemo.provider.ExampleWidgetProvider2"
    android:label="@string/app_widget_group">
   ...
</receiver>

5.点击  MIUI Widget 跳转应用页面

5.1.描述

在 AppWidgetProvider 的onUpdate方法中,使用 RemoteViews 的 setOnClickPendingIntent 设置启动的 Intent 。使用 AppWidgetManager 的 updateAppWidget方法关联RemoteViews 和Widget。

5.2.参数

// viewId Widget 布局 id
// PendingIntent Intent的封装
// appWidgetId Widget 的 id,可在 onUpdate 方法参数中获取
// appWidgetManager widget 的管理对象,可在 onUpdate 方法参数获取
remoteViews.setOnClickPendingIntent(int viewId, PendingIntent pendingIntent)
appWidgetManager.updateAppWidget(int appWidgetId, RemoteViews views)

5.3.示例

// step1: 构建跳转页面的 PendingIntent
Intent intent = new Intent(context, ExampleActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
// step2: 生成 RemoteViews 关联 PendingIntent
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.appwidget_provider_layout);
views.setOnClickPendingIntent(R.id.button, pendingIntent);
// step3: 关联 widget 和 RemoteViews
appWidgetManager.updateAppWidget(appWidgetId, views);

6.判断 MIUI Widget 是否已经添加

6.1.描述

使用系统 AppWidgetManager 的 getAppWidgetIds方法判断某个Widget是否已添加。

6.2.示例

ComponentName componentName = new ComponentName(getPackageName(), "com.miui.ExampleAppWidgetProvider");
int[] appWidgetIds = AppWidgetManager.getInstance(getApplicationContext()).getAppWidgetIds(componentName);
if(appWidgetIds.length > 0){
    // 已添加
} else {
    // 未添加
}

7.判断是否支持MIUI Widget

7.1.描述

开发者可以通过示例方式判断当前手机是否支持MIUI Widget。

7.2.示例

@WorkerThread
public boolean isMiuiWidgetSupported() {
    Uri uri = 
    Uri.parse("content://com.miui.personalassistant.widget.external");
    boolean isMiuiWidgetSupported = false;
    try {
        Bundle bundle = getContentResolver().call(uri, 
        "isMiuiWidgetSupported", null, null);
        if (bundle != null) {
            isMiuiWidgetSupported = bundle.getBoolean("isMiuiWidgetSupported");
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    return isMiuiWidgetSupported;
}

8.判断是否支持MIUI Widget 小部件详情页

8.1.描述

开发者可以通过示例方式判断当前手机是否支持MIUI Widget 详情页。为了提升用户体验,部分机型支持 MIUI Widget(包含MIUI Widget 特性,例如曝光刷新等), 但不支持调起MIUI Widget 详情页。这部分机型添加小部件的方式与旧版系统一致。

8.2.示例

@WorkerThread
public boolean isMiuiWidgetDetailPageSupported() {
    Uri uri = 
    Uri.parse("content://com.miui.personalassistant.widget.external");
    boolean isMiuiWidgetDetailPageSupported = false;
    try {
        Bundle bundle = getContentResolver().call(uri, 
        "isMiuiWidgetDetailPageSupported", null, null);
        if (bundle != null) {
            isMiuiWidgetDetailPageSupported = bundle.getBoolean("isMiuiWidgetDetailPageSupported");
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    return isMiuiWidgetDetailPageSupported;
}

9.MIUI Widget 与 Activity 切换动画

9.1.描述

MIUI 系统为MIUI Widget 和 Activity 切换时增加了过渡动画。开发者可根据自身业务场景决定是否使用过渡动画。动画默认开启,如果不使用,设置”miuiWidgetTransitionAnimation”为false。

9.2.示例

<receiver android:name="com.miui.ExampleWidgetProvider" >
    <meta-data
    android:name="miuiWidget"
    android:value="true" />
    // 关闭动画
    <meta-data
    android:name="miuiWidgetTransitionAnimation"
    android:value="false" />

    ...
</receiver>

10.App主动刷新小部件

10.1.描述

当应用在前台使用或在后台存活时,可调用AppWidgetManager. updateAppWidget()方法,主动刷新小部件。这样可以提高小部件内容的实时性和准确性。

10.2.示例

// 通过组件名更新
public void updateWidget(Context context) {
    // R.layout.demo_appwidget_normal_example 为小部件布局文件,这里仅示例
    RemoteViews remoteViews = new RemoteViews(context.getPackageName(),
            R.layout.demo_appwidget_normal_example);
    AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
    // NormalExampleWidgetProvider 为小部件组件名字,这里仅示例
    ComponentName componentName = new ComponentName(context, NormalExampleWidgetProvider.class);
    appWidgetManager.updateAppWidget(componentName, remoteViews);
}
// 通过widgetId更新
public void updateWidgetByWidgetId(Context context, int widgetId) {
    // R.layout.demo_appwidget_normal_example 为小部件布局文件,这里仅示例
    RemoteViews remoteViews = new RemoteViews(context.getPackageName(),
            R.layout.demo_appwidget_normal_example);
    AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
    appWidgetManager.updateAppWidget(widgetId, remoteViews);
}

11.Push透传刷新服务

11.1.描述

当MIUI Widget状态发生改变且Widget不可见、主应用未启动时,开发者可调用小部件服务端提供的更新API,经由MI PUSH发送透传消息给负一屏/桌面客户端,由负一屏/桌面拉起Widget独立进程(不唤醒应用主进程),以实现MIUI Widget实时刷新的目的。

流程图:

小米MIUI 小部件系统能力说明小米MIUI 小部件系统能力说明

11.2.接口规范

域名: https://developer.assistant.miui.com
路径: /openapi/widget/{cpCode}/refresh
     cpCode值由小部件开发人员提供
参数:需包含Header以及Body
Header参数表
头域名称 描述 是否必选 类型 取值范围
Content-Type 固定值,填application/json String application/json
app-id 在小米开放平台注册的程序编号 String
access-token 应用级token(校验请求权限,通过帐号服务获取) String 最大长度:259
sign 签名(验证请求完整性,下方有签名生成方法) String
timestamp 当前时间戳(防止请求重放,会根据该字段判断请求有效期) long 13位 毫秒时间戳
trace-id 请求的唯一标识(用来定位每次请求) String 只能包含数字和大小写字母,最大长度64
Body参数表
字段名 描述 必须 类型
oaid 设备oaid String
widgetId 设备 widgetId (安卓生成的id) String
widgetProviderName 小部件providerName String
extra 额外透传到小部件内容 String

注意:

  • 获取access-token:access-token的获取方式请联系相关技术支持。
  • 处理签名
签名生成: 
    假设: appId = "111111111" , 当前时间戳是 1606206667013 ,秘钥是 "testSecret"
    step 1: 先将body进行md5,再转化为16进制小写字符串
            公式:md5Body= Lowercase(hexEncode(md5(body)))
    step 2: 将appId、毫秒时间戳和body的md5Body值,按照参数名称进行字典排序,用‘&’连接获得strToDigest
            如:strToDigest="appId=111111111&body=md5Body&timestamp=1606206667013"          
    step 3: 将获得到的摘要字符串(strToDigest)进行HMAC_SHA_256,再转化为16进制小写字符串得到签名
            公式:Lowercase(hexEncode(HMAC_SHA_256("testSecret", strToDigest)))
// 签名java demo: 
// demo引用了apache的工具类进行处理,若不想引用可以参考其逻辑自行实现
<!-- https://mvnrepository.com/artifact/commons-codec/commons-codec -->
<dependency>
    <groupId>commons-codec</groupId>
    <artifactId>commons-codec</artifactId>
    <version>1.14</version>
</dependency>

@Slf4j
public class SignatureUtils {
    
    /**
     * 生成签名
     *
     * @param appId     应用id
     * @param secret    秘钥
     * @param timestamp 请求的时间戳
     * @param body      请求的body内容
     * @return 签名信息
     */
    public static String generateSignature(String appId, String secret, String timestamp, String body) {
        String sign = null;
        try {
            String md5Body = DigestUtils.md5Hex(body);
            Map<String, String> paramMap = new TreeMap<>();
            paramMap.put("appId", appId);
            paramMap.put("timestamp", timestamp);
            paramMap.put("body", md5Body);
            String strToDigest = paramMap.entrySet().stream().map(e -&gt手机app生成器; e.getKey() + "=" + e.getValue()).collect(Collectors.joining("&"));
            sign = new HmacUtils(HmacAlgorithms.HMAC_SHA_256, secret).hmacHex(strToDigest);
        } catch (Exception e) {
            log.error("generate signature exception !", e);
        }
        return sign;
    }
    
    /**
     * 验证签名
     *
     * @param appId     应用id
     * @param secret    秘钥
     * @param timestamp 请求的时间戳
     * @param body      请求的body内容
     * @param sign      请求的签名
     * @return 签名信息
     */
    public static boolean verifySignature(String appId, String secret, String timestamp, String body, String sign) {
        return sign.equals(generateSignature(appId, secret, timestamp, body));
    }
}
  • 返回状态码定义
状态码 描述
成功 0
服务异常 1000
无效参数 1001
缺失参数 1002
token验证失败 3000
签名验证失败 4000
流量限制 5000
  • 开发者MIUI Widget可从刷新广播的Intent中得到extra信息。

11.3.示例

// Curl示例:
curl --location --request POST 'http://developer.assistant.miui.com/openapi/widget/testCp/refresh' 
    --header 'app-id: 111111111' 
    --header 'access-token: A1_oKs19iiWArgij6qFYaEAooAMqG3bhXUgS0MZQf63KQTgWju-oj9YuccqR1EhbugrqnFmooNr6mKdkfEdKN740fUYpxk0o9ZHE5QpFvR1fhaoJ7xELYD1byNnYZb-tB-y5DPXRLIp8ikod5lUZGnayuLVPePa7uB1LlVzw-qPS-U' 
    --header 'sign: 664d528f398af20f23b6e4ec43d4e9662476ee8fc9c5cee42dd897b1af0152e7' 
    --h生成自己的appeader 'timestamp: 1636341480000' 
    --header 'trace-id: 123123' 
    --header 'Content-Type: application/json' 
    --data-raw '{
        "oaid": "f29eb7d12222fb6b",
        "widgetId": "666",
        "widgetProviderName": "com.miui.test",
        "extra": "{"test":123}"
    }'

编辑:yimen,如若转载,请注明出处:https://www.yimenapp.com/kb-yimen/12415/

部分内容来自网络投稿,如有侵权联系立删

未经允许不得转载:一门应用 » 小米MIUI 小部件系统能力说明

相关推荐

联系我们

微信公众号

yimendabao

关注官方微信,了解最新资讯

客服QQ
4001658508

企业QQ,点击发起咨询