一、引言

1.1 概述

欢迎您关注并在Flutter工程中使用HarmonyOS特性功能(意图分享和碰一碰传输)。以下是可参考代码:

1、Flutter侧Sample代码仓库

2、Flutter侧一镜到底依赖库

3、Flutter侧意图框架依赖库

4、Flutter侧碰一碰依赖库

1.2 场景使用说明

本文档旨在向开发者清晰地传达以下关键信息:

1. 理解一镜到底、意图框架和碰一碰分享。

2. 如何在Flutter工程中集成一镜到底、意图框架和碰一碰分享的功能。

3. 建议在使用开发前,仔细阅读本文档。

二、实例效果展示

图10-1 一镜到底

图10-2 意图分享成功

图10-3 小艺推荐中出现分享卡片

图10-4 未开启碰一碰监听

图10-5 碰一碰分待接收

三、什么是意图框架

3.1 Intents Kit简介

Intents Kit(意图框架服务)是HarmonyOS级的意图标准体系 ,意图连接了应用/元服务内的业务功能。

意图框架能帮开发者将应用/元服务内的业务功能,智能分发到各系统入口,这个过程即智慧分发。其中系统入口包括:小艺对话、小艺搜索、小艺建议。

3.2 Intents Kit优势

利用HarmonyOS的大模型、多维设备感知等AI能力,准确且及时地获取到用户显性、潜在意图,从而实现个性化、多模态、精准的智慧分发。

3.3 意图的运行逻辑

HarmonyOS、应用/元服务的交互中,意图运行方式分为意图调用和意图共享:

3.4 约束与限制

  • 设备限制

本Kit仅适用于Phone、Tablet、PC/2in1,暂不支持模拟器使用。

  • 地区限制

本Kit仅支持中国境内(不包含中国香港、中国澳门、中国台湾)提供服务。

  • 操作系统限制

HarmonyOS 5.0及以上。

3.5 Flutter侧接入实现

依赖包安装

hadss_intents:
    git:
      url: https://gitcode.com/openharmony-sig/flutter_ohfeatures.git
      path: packages/intents

核心接口

Intents().shareIntent(List<Map> intentData)。

代码示例

import 'package:hadss_intents/hadss_intents.dart';
// 以分享音乐为例;注意此处shareIntent方法接受的数组里数据可替换为想分享的数据(如:视频数据)
void handleShareIntentPlayMusic(){
    Intents().shareIntent([
   {
    'intentName': 'PlayMusic',
    'intentVersion': '1.0',
    'identifier': 'uuid',
    'intentActionInfo': {
      'actionMode': 'EXECUTED',
      'executedTimeSlots': {
        'executedStartTime': 1637393212000,
        'executedEndTime': 1637393112000
      },
      'currentPercentage': 50
    },
    'intentEntityInfo': {
      'entityName': 'Music',
      'entityId': 'C10194368',
      'entityGroupId': 'C10194321312',
      'displayName': '测试1',
      'description': 'NA',
      'logoURL':
          'https://www-file.huawei.com/-/media/corporate/images/home/logo/huawei_logo.png',
      'keywords': ['华为音乐', '化妆'],
      'rankingHint': 99,
      'expirationTime': 1637393212000,
      'metadataModificationTime': 1637393212000,
      'activityType': ['1', '2', '3'],
      'artist': ['测试歌手1', '测试歌手2'],
      'lyricist': ['测试词作者1', '测试词作者2'],
      'composer': ['测试曲作者1', '测试曲作者2'],
      'albumName': '测试专辑',
      'duration': 244000,
      'playCount': 100000,
      'musicalGenre': ['流行', '华语', '金曲', '00后'],
      'isPublicData': false
    }
  }
    ]).then(() => {
      Alert.alert(`意图分享成功`);
    })
  };
GestureDetector(
  onTap: () => handleShareIntentPlayMusic(),
)

3.6 HarmonyOS工程处理

步骤一、在HarmonyOS工程如下目录(src/main/ets/entryability)下加入InsightIntentExecutorImpl.ets文件。

import Logger from '../utils/Logger';
import { insightIntent, InsightIntentExecutor } from '@kit.AbilityKit';
import { window } from '@kit.ArkUI';
import type { BusinessError } from '@kit.BasicServicesKit';
const TAG: string = 'InsightIntentExecutorImpl';
/**
 * 意图执行样例
 */
export default class InsightIntentExecutorImpl extends InsightIntentExecutor {
  private static readonly PLAY_MUSIC = 'PlayMusic';
  private static readonly VIEW_REPAYMENT = 'PlayVideo';
  private static readonly ERROR_CODE = -1;
  /**
   * override 执行前台UiAbility意图
   *
   * @param name 意图名称
   * @param param 意图参数
   * @param pageLoader 窗口
   * @returns 意图调用结果
   */
  onExecuteInUIAbilityForegroundMode(name: string, param: Record<string, Object>, pageLoader: window.WindowStage):
    Promise<insightIntent.ExecuteResult> {
    Logger.info(TAG, `onExecuteInUIAbilityForegroundMode name: ${name}, param: ${JSON.stringify(param)}`);
    // 根据意图名称分发处理逻辑
    switch (name) {
      case InsightIntentExecutorImpl.PLAY_MUSIC:
        Logger.info(TAG, `PLAY_MUSIC}`);
        return this.playMusic(param, pageLoader);
      case InsightIntentExecutorImpl.VIEW_REPAYMENT:
        Logger.info(TAG, `PLAY_Video}`);
        return this.viewRepayment(param, pageLoader);
      default:
        Logger.info(TAG, `no intent match`);
        break;
    }
    Logger.info(TAG, `no intent match`);
    return Promise.resolve({
      code: InsightIntentExecutorImpl.ERROR_CODE,
      result: {
        message: 'unknown intent'
      }
    } as insightIntent.ExecuteResult);
  }
  /**
   * 实现调用播放音乐功能
   *
   * @param param 意图参数
   * @param pageLoader 窗口
   */
  private playMusic(param: Record<string, Object>,
    pageLoader: window.WindowStage): Promise<insightIntent.ExecuteResult> {
    return new Promise(() => {
      let para: Record<string, string> = {
        'result': `intent execute success, entityId: ${param.entityId}`
      };
      let localStorage: LocalStorage = new LocalStorage(para);
      // TODO 实现意图调用
      pageLoader.loadContent('pages/Index', localStorage)
        .then(() => {
          // TODO 调用成功的情况
          Logger.info(TAG, "Intent execute succeed");
        })
        .catch((err: BusinessError) => {
          // TODO 调用失败的情况
          Logger.error(TAG, `Intent execute failed: ${JSON.stringify(err)}`);
        });
    });
  }
  /**
   * 实现调用查看还款功能
   *
   * @param param 意图参数
   * @param pageLoader 窗口
   */
  private viewRepayment(param: Record<string, Object>,
    pageLoader: window.WindowStage): Promise<insightIntent.ExecuteResult> {
    return new Promise(() => {
      let para: Record<string, string> = {
        'result': JSON.stringify(param)
      };
      let localStorage: LocalStorage = new LocalStorage(para);
      // TODO 实现意图调用
      pageLoader.loadContent('pages/Index', localStorage)
        .then(() => {
          // TODO 调用成功的情况
          Logger.info(TAG, "Intent execute succeed");
        })
        .catch((err: BusinessError) => {
          // TODO 调用失败的情况
          Logger.error(TAG, `Intent execute failed: ${JSON.stringify(err)}`);
        });
    });
  }
};

四、什么是碰一碰分享

4.1 碰一碰分享简介

Share Kit推出碰一碰分享,支持用户通过碰一碰发起跨端分享,可实现传输图片、共享Wi-Fi等,并结合App Linking技术,可实现内容的快速跨设备分享,直达目标应用,无需依赖第三方应用中转,提供高效、便捷、无缝的分享体验。例如视频分享示例

4.2 分享的业务流程

流程说明:

  1. 宿主应用注册碰一碰分享事件,并与亮屏的对端设备碰一碰。
  2. 宿主应用发现设备,调用碰一碰分享事件回调,在回调事件中构造分享数据并发送。
  3. 目标设备接收并处理分享数据。
  4. 宿主应用解除注册靠近分享事件。

4.3 约束与限制

手机应用发起碰一碰分享时,双端设备需要在亮屏、解锁的状态下并且都已开启华为分享服务(系统默认开启),设备顶部轻碰即可触发。如果用户已手动关闭华为分享服务开关,轻碰事件触发时,用户会接收到系统通知提示开启。

Share Kit的处理机制:

  • 任意一端设备不支持碰一碰能力时,轻碰无任何响应。
  • 宿主应用无法获得分享结果,Share Kit会通过系统通知消息告知用户对端接收或拒绝。

环境要求:

  • 支持的手机系统:HarmonyOS 5.0 Release及以上版本。
  • 集成开发环境:DevEco Studio 5.0 Beta1及以上版本。

4.4 Flutter侧接入实现

依赖包安装

hadss_knock_share:
    git:
      url: https://gitcode.com/openharmony-sig/flutter_ohfeatures.git
      path: packages/knock_share

源端设备

代码示例

import 'package:hadss_knock_share/hadss_knock_share.dart';
// 1. 启用碰一碰监听
KnockShare.addKnockShareListener()
// 2. 启用成功后,手机碰一碰
KnockShare.setKnockShareData(shareData)
// 3. 关闭碰一碰监听
 KnockShare.removeKnockShareListener();
//要先检测碰一碰支不支持,才去调用其他接口的
Future<void> getCanUseKnockShareState() async {
  bool canUseKnockShare = false;
  canUseKnockShare = await knockSharePlugin.canUseKnockShare() ?? false;
  setState(() {
    canUseKnockShareState = canUseKnockShare;
  });
}
// 本示例提供三种不同分享场景
1)分享纯图片
var shareBean = SharedRecord(UniformDataType.PNG);
shareBean.thumbnailUri = "缓存图片的file地址";
addKnockShareListener(shareBean);
2)沉浸式分享
var shareBean = SharedRecord(UniformDataType.HYPERLINK);
shareBean.content = "分享的内容";
shareBean.thumbnailUri = "缓存图片的file地址";
shareBean.description = "描述";
shareBean.title = "标题";
addKnockShareListener(shareBean);
3)上下白卡布局
var shareBean = SharedRecord(UniformDataType.HYPERLINK);
shareBean.content = "分享的内容";
shareBean.thumbnailUri = "缓存图片的file地址";
shareBean.description = "描述";
shareBean.title = "标题";
addKnockShareListener(shareBean);

目标设备

需要在HarmonyOS工程下添加碰一碰分享的缓存文件(oho/entity/src/main/resources/rawfile)。在oho/entity/src/main/ets/pages下的index.ets中增加碰一碰分享的缓存读取。

import common from '@ohos.app.ability.common';
import { FlutterPage } from '@ohos/flutter_ohos'
import { fileIo } from '@kit.CoreFileKit';
import { BusinessError } from '@kit.BasicServicesKit';
let storage = LocalStorage.getShared()
const EVENT_BACK_PRESS = 'EVENT_BACK_PRESS'
const TAG = "Index : "
@Entry(storage)
@Component
struct Index {
  private context = getContext(this) as common.UIAbilityContext
  @LocalStorageLink('viewId') viewId: string = "";
  aboutToAppear(): void {
    // raw目录未对外暴露沙箱地址 需要将raw目录文件拷贝至缓存目录
    let rawFileList = getContext(this).resourceManager.getRawFileListSync("");
    for (let rawFileListElement of rawFileList) {
      try {
        if (getContext().resourceManager.isRawDir(rawFileListElement)) {
          continue
        }
        console.log(TAG, `copyFileSync rawFileListElement : ${rawFileListElement}`)
        let rawFileContent = getContext().resourceManager.getRawFileContentSync(rawFileListElement);
        let cacheFilePath = getContext().cacheDir + "/" + rawFileListElement;
        if (!fileIo.accessSync(cacheFilePath, fileIo.AccessModeType.EXIST)) {
          let cacheFile = fileIo.openSync(cacheFilePath, fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE)
          fileIo.writeSync(cacheFile.fd, rawFileContent.buffer)
          fileIo.closeSync(cacheFile.fd)
          console.log(TAG, `copyFileSync cacheFilePath : ${cacheFilePath}`)
        } else {
          console.log(TAG, ` check cacheFilePath : ${cacheFilePath} is exists`)
        }
      } catch (error) {
        let code = (error as BusinessError).code;
        let message = (error as BusinessError).message;
        console.error(TAG,`aboutToAppear rawFileListElement : ${rawFileListElement} code: ${code}, message: ${message}`);
      }
    }
  }
  build() {
    Column() {
      FlutterPage({ viewId: this.viewId })
    }
  }
  onBackPress(): boolean {
    this.context.eventHub.emit(EVENT_BACK_PRESS)
    return true
  }
}

五、什么是一镜到底

5.1 一镜到底简介

共享元素转场是一种界面切换时对相同或者相似的两个元素做的一种位置和大小匹配的过渡动画效果,也称一镜到底动效。

5.2 Flutter侧接入实现

依赖包安装

hadss_geometry_transition:
    git:
      url: https://gitcode.com/openharmony-sig/flutter_ohfeatures.git
      path: packages/geometry_transition

代码示例:

import 'package:hadss_geometry_transition/hadss_geometry_transition.dart';
GeometryTransition(
  closedBuilder: (context, open) {
    return GestureDetector(
        onTap: open,
        child: _buildCustomButton(0, buttonTexts[0]));
  },
  openBuilder: (context, close) {
    return const FullScreenOverlayTimer();
  },
  tappable: true,
  onPushCallback: (data) => (){},
)
Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐