diff --git a/zh-cn/third-party-cases/distributed-canvas.md b/zh-cn/third-party-cases/distributed-canvas.md
new file mode 100644
index 0000000000000000000000000000000000000000..911ba45da6ba3395f434e7f6a338d2ace302fdbd
--- /dev/null
+++ b/zh-cn/third-party-cases/distributed-canvas.md
@@ -0,0 +1,417 @@
+# 分布式画布流转场景
+
+## 场景说明
+
+两台设备组网,当其中一个设备修改文件时,两个设备可以同步修改的结果。分布式场景可以在协同办公(如多人多设备编辑同一文件),设备文档更新(分布式设备更新文件内容,所有设备同步更新)中发挥重要作用,有助于加快工作效率,减少工作中的冗余。
+
+本示例将为大家介绍如何实现上述功能。
+
+## 效果呈现
+
+本例效果如下:
+
+| 设置分布式权限 | 进行分布式连接 | 连接后状态显示 |
+| -------------------------------------- | ------------------------------------------ | ----------------------------------------- |
+| ![](figures/disributed_permission.png) | ![](figures/disributed_canvas_connect.png) | ![](figures/disributed_canvas-before.png) |
+
+| 点击rect和ellipse按钮后后本机显示 | 另外一台机器分布式应用显示 |
+| ---------------------------------------- | ---------------------------------------- |
+| ![](figures/disributed_canvas-after.png) | ![](figures/disributed_canvas-after.png) |
+
+## 运行环境
+
+本例基于以下环境开发,开发者也可以基于其他适配的版本进行开发。
+
+- IDE:DevEco Studio 4.0.0.201 Beta1
+- SDK:Ohos_sdk_public 4.0.7.5 (API Version 10 Beta1)
+
+## 实现思路
+
+在分布式文件场景中,分布式设备管理包含了分布式设备搜索、分布式设备列表弹窗、远端设备拉起三部分。
+首先在分布式组网内搜索设备,然后把设备展示到分布式设备列表弹窗中,最后根据用户的选择拉起远端设备。
+
+- 分布式设备搜索:通过SUBSCRIBE_ID搜索分布式组网内的设备。
+
+- 分布式设备列表弹窗:使用@CustomDialog装饰器来装饰分布式设备列表弹窗。
+
+- 远端设备拉起:通过startAbility(deviceId)方法拉起远端设备的包。
+
+- 分布式数据管理:(1)管理分布式数据库:创建一个distributedObject分布式数据对象实例,用于管理分布式数据对象。
+
+ (2)订阅分布式数据变化:通过this.distributedObject.on('status', this.statusCallback)监听分布式数据对象的变更。
+
+## 开发步骤
+
+1. 申请所需权限
+
+ 在model.json5中添加以下配置:
+
+ ```json
+ "requestPermissions": [
+ {
+ "name": "ohos.permission.DISTRIBUTED_DATASYNC"//允许不同设备间的数据交换
+ },
+ {
+ "name": "ohos.permission.ACCESS_SERVICE_DM"//允许系统应用获取分布式设备的认证组网能力
+ }
+ ]
+ ```
+
+2. 构建UI框架
+
+ indexCanvas页面:
+
+ TitleBar组件呈现标题栏。通过数据懒加载的方式遍历绘制的图形。被划出可视区域外的资源会被回收。
+
+ 绘制ellipse图形、rect图形的按钮使用Button组件呈现。
+
+ 返回按钮、删除按钮也通过Button组件呈现。
+
+ ```typescript
+ build() {
+ Column() {
+ TitleBar({ rightBtn: $r('app.media.trans'), onRightBtnClicked: this.showDialog })
+ //自/common/TitleBar.ets中引入标题栏相关。点击标题栏中的右侧按钮会调用showDialog()函数连接组网设备
+ Row() {
+ Text($r('app.string.state'))
+ .fontSize(30)
+ Image(this.isOnline ? $r('app.media.green') : $r('app.media.red'))
+ .size({ width: 30, height: 30 })
+ .objectFit(ImageFit.Contain)
+ }
+ .width('100%')
+ .padding(16)
+ //通过懒加载模式遍历绘制的图形,将每个图形绘制在画布上
+ LazyForEach(this.canvasDataSource, (item: CanvasPath, index) => {
+ Canvas(this.context)
+ .width('100%')
+ .height(200)
+ .backgroundColor('#00ffff')
+ .onReady(() => {
+ if (item.path === 'rect') {
+ this.context.save();
+ this.path2Df.rect(80, 80, 100, 100);
+ this.context.stroke(this.path2Df);
+ this.context.restore();
+ }
+ if (item.path === 'ellipse') {
+ this.context.restore();
+ this.path2De.ellipse(100, 100, 50, 100, Math.PI * 0.25, Math.PI * 0.5, Math.PI);
+ this.context.stroke(this.path2De);
+ this.context.save();
+ }
+ })
+ }, item => JSON.stringify(item))
+
+ Row() {
+ Button('ellipse')//绘制ellipse图形的按钮
+ .width(130)
+ .height(45)
+ .key('ellipse')
+ .onClick(() => {
+ if (this.globalObject.isContainString('ellipse') === -1) {
+ this.globalObject.add('ellipse'); //将绘制信息保存在持久全局数据中
+ }
+ this.onPageShow();
+ })
+ Button('rect')//绘制rect图形的按钮
+ .width(130)
+ .height(45)
+ .key('rect')
+ .onClick(() => {
+ if (this.globalObject.isContainString('rect') === -1) {
+ this.globalObject.add('rect');
+ }
+ this.onPageShow();
+ })
+ }.margin({ top: 10 })
+ .width('100%')
+ .justifyContent(FlexAlign.SpaceAround)
+
+ Row() {
+ Button('back')
+ .width(130)
+ .height(45)
+ .key('back')
+ .backgroundColor(Color.Orange)
+ .onClick(() => {
+ router.back()
+ })
+ Button('delete')//删除图形
+ .width(130)
+ .height(45)
+ .key('delete')
+ .onClick(() => {
+ this.globalObject.clear();
+ this.canvasDataSource['pathArray'] = [];
+ this.canvasDataSource.notifyDataReload();
+ this.context.clearRect(0, 0, 950, 950)
+ })
+ }.margin({ top: 10 })
+ .width('100%')
+ .justifyContent(FlexAlign.SpaceAround)
+ }
+ .width('100%')
+ .height('100%')
+ .justifyContent(FlexAlign.Center)
+ .alignItems(HorizontalAlign.Center)
+ }
+ }
+ ```
+
+3. 数据model
+
+ 通过registerDataChangeListener进行对数据变动的监听,数据发生变化时,调用notifyDataReload方法通知数据已经准备就绪。
+
+ ```typescript
+ //BasicDataSource.ets
+ class BasicDataSource implements IDataSource {
+ private listeners: DataChangeListener[] = []
+
+ public totalCount(): number {
+ return 0
+ }
+
+ public getData(index: number): any {
+ return undefined
+ }
+
+ //注册数据变动的监听
+ registerDataChangeListener(listener: DataChangeListener): void {
+ if (this.listeners.indexOf(listener) < 0) {
+ console.info('add listener')
+ this.listeners.push(listener)
+ }
+ }
+
+ unregisterDataChangeListener(listener: DataChangeListener): void {
+ const pos = this.listeners.indexOf(listener);
+ if (pos >= 0) {
+ console.info('remove listener')
+ this.listeners.splice(pos, 1)
+ }
+ }
+
+ //数据reloaded,分布式数据数值变化需要调用这个接口重载下
+ notifyDataReload(): void {
+ this.listeners.forEach(listener => {
+ listener.onDataReloaded()
+ })
+ }
+
+ notifyDataAdd(index: number): void {
+ this.listeners.forEach(listener => {
+ listener.onDataAdd(index)
+ })
+ }
+
+ ....
+
+ export class CanvasDataSource extends BasicDataSource {
+ //监听的数据类型
+ private pathArray: Canvas[] = []
+
+ //重载接口
+ public totalCount(): number {
+ return this.pathArray.length
+ }
+
+ public getData(index: number): any {
+ return this.pathArray[index]
+ }
+
+ public addData(index: number, data: Canvas): void {
+ this.pathArray.splice(index, 0, data)
+ this.notifyDataAdd(index)
+ }
+
+ public pushData(data: Canvas): void {
+ this.pathArray.push(data)
+ this.notifyDataAdd(this.pathArray.length - 1)
+ }
+ }
+ ```
+
+4. 将两台设备组网
+
+ 使用自RemoteDeviceModel.ts中引入的类RemoteDeviceModel以扫描获得附近可以连接的设备。
+
+ ```typescript
+ showDialog = () => {
+ //RemoteDeviceModel引入自model/RemoteDeviceModel.ts
+ RemoteDeviceModel.registerDeviceListCallback(() => {
+ //得到附近可信的设备列表
+ Logger.info(TAG, 'registerDeviceListCallback, callback entered')
+ this.devices = []
+ this.devices = RemoteDeviceModel.discoverDevices.length > 0 ? RemoteDeviceModel.discoverDevices : RemoteDeviceModel.devices
+ if (this.dialogController) {
+ this.dialogController.close()
+ this.dialogController = undefined
+ }
+ this.dialogController = new CustomDialogController({
+ builder: DeviceDialog({
+ devices: this.devices,
+ onSelectedIndexChange: this.onSelectedDevice
+ }),
+ autoCancel: true
+ })
+ this.dialogController.open()
+ })
+ }
+ ....................................
+ //model/RemoteDeviceModel.ts
+ import deviceManager from '@ohos.distributedHardware.deviceManager'
+ registerDeviceListCallback(stateChangeCallback: () => void) {
+ if (typeof (this.deviceManager) !== 'undefined') {
+ this.registerDeviceListCallbackImplement(stateChangeCallback)
+ return
+ }
+ Logger.info(TAG, 'deviceManager.createDeviceManager begin')
+ try {
+ deviceManager.createDeviceManager(BUNDLE, (error, value) => {
+ if (error) {
+ Logger.error(TAG, 'createDeviceManager failed.')
+ return
+ }
+ this.deviceManager = value
+ this.registerDeviceListCallbackImplement(stateChangeCallback)
+ Logger.info(TAG, `createDeviceManager callback returned,value=${value}`)
+ })
+ } catch (error) {
+ Logger.error(TAG, `createDeviceManager throw error, code=${error.code} message=${error.message}`)
+ }
+
+ Logger.info(TAG, 'deviceManager.createDeviceManager end')
+ }
+ registerDeviceListCallbackImplement(stateChangeCallback: () => void) {
+ Logger.info(TAG, 'registerDeviceListCallback')
+ this.stateChangeCallback = stateChangeCallback
+ if (this.deviceManager === undefined) {
+ Logger.error(TAG, 'deviceManager has not initialized')
+ this.stateChangeCallback()
+ return
+ }
+ Logger.info(TAG, 'getTrustedDeviceListSync begin')
+ try {
+ let list = this.deviceManager.getTrustedDeviceListSync()//同步获取所有可信设备列表
+ Logger.info(TAG, `getTrustedDeviceListSync end, devices=${JSON.stringify(list)}`)
+ if (typeof (list) !== 'undefined' && typeof (list.length) !== 'undefined') {
+ this.devices = list
+ }
+ } catch (error) {
+ Logger.error(TAG, `getLocalDeviceInfoSync throw error, code=${error.code} message=${error.message}`)
+ }
+ this.stateChangeCallback()
+ Logger.info(TAG, 'callback finished')
+ try {
+ this.deviceManager.on('deviceStateChange', (data) => {
+ if (data === null) {
+ return
+ }
+ Logger.info(TAG, `deviceStateChange data = ${JSON.stringify(data)}`)
+ switch (data.action) {
+ case deviceManager.DeviceStateChangeAction.READY://即设备处于可用状态,表示设备间信息已在分布式数据中同步完成, 可以运行分布式业务
+ this.discoverDevices = []
+ this.devices.push(data.device)
+ this.stateChangeCallback()
+ try {
+ let list = this.deviceManager.getTrustedDeviceListSync()
+ if (typeof (list) !== 'undefined' && typeof (list.length) !== 'undefined') {
+ this.devices = list
+ }
+ } catch (error) {
+ Logger.error(TAG, `getTrustedDeviceListSync throw error, code=${error.code} message=${error.message}`)
+ }
+ this.stateChangeCallback()
+ break
+ default:
+ break
+ }
+ })
+ this.deviceManager.on('deviceFound', (data) => {
+ if (data === null) {
+ return
+ }
+ Logger.info(TAG, `deviceFound data=${JSON.stringify(data)}`)
+ this.onDeviceFound(data)
+ })
+ this.deviceManager.on('discoverFail', (data) => {
+ Logger.info(TAG, `discoverFail data=${JSON.stringify(data)}`)
+ })
+ this.deviceManager.on('serviceDie', () => {
+ Logger.info(TAG, 'serviceDie')
+ })
+ } catch (error) {
+ Logger.error(TAG, `on throw error, code=${error.code} message=${error.message}`)
+ }
+ this.startDeviceDiscovery()
+ }
+ startDeviceDiscovery() {
+ SUBSCRIBE_ID = Math.floor(65536 * Math.random())
+ var info = {
+ subscribeId: SUBSCRIBE_ID,
+ mode: 0xAA,
+ medium: 2,
+ freq: 2,//高频率
+ isSameAccount: false,
+ isWakeRemote: true,
+ capability: 0
+ }
+ Logger.info(TAG, `startDeviceDiscovery${SUBSCRIBE_ID}`)
+ try {
+ this.deviceManager.startDeviceDiscovery(info)//开始发现周边设备
+ } catch (error) {
+ Logger.error(TAG, `startDeviceDiscovery throw error, code=${error.code} message=${error.message}`)
+ }
+
+ }
+ ```
+
+5. 实现同步编辑
+
+ 通过AppStorage设置持久性数据,然后实现IDataSource接口,通过注册数据监听接口监听数据的变化。
+
+ ```typescript
+ onPageShow() {
+ //每当完成编辑或者新建文件,就会回到主页,此时就会执行onPageShow()
+ //noteDataSource获取globalObject保存的分布式的持久性数据,并进行Reload操作传递。
+ this.noteDataSource['dataArray'] = this.globalObject.distributedObject.documents
+ this.noteDataSource.notifyDataReload()
+ Logger.info(TAG, `this.sessionId = ${this.sessionId}`)
+ Logger.info(TAG, `globalSessionId = ${this.globalSessionId}`)
+ if (this.sessionId !== this.globalSessionId) {
+ this.sessionId = this.globalSessionId
+ this.share()
+ }
+ }
+ share() {
+ //多个设备间的对象如果设置为同一个sessionId,数据自动同步
+ Logger.info(TAG, `sessionId = ${this.sessionId}`)
+ this.globalObject.setChangeCallback(() => {
+ this.noteDataSource['dataArray'] = this.globalObject.distributedObject.documents
+ this.noteDataSource.notifyDataReload()
+ })
+ this.globalObject.setStatusCallback((session, networkId, status) => {
+ Logger.info(TAG, `StatusCallback,${status}`)
+ if (status === 'online') {
+ this.isOnline = true
+ } else {
+ this.isOnline = false
+ }
+ })
+ this.globalObject.distributedObject.setSessionId(this.sessionId)
+ AppStorage.SetOrCreate('objectModel', this.globalObject)
+ }
+ ```
+
+## 全部代码
+
+本例完整代码sample示例链接:[分布式对象](https://gitee.com/openharmony/applications_app_samples/tree/master/code/SuperFeature/DistributedAppDev/DistributedNote)
+
+## 参考
+
+[权限列表](../application-dev/security/permission-list.md#ohospermissiondistributed_datasync)
+
+[Path2D对象](../application-dev/reference/arkui-ts/ts-components-canvas-path2d.md)
+
+[分布式数据对象](../application-dev/reference/apis/js-apis-data-distributedobject.md)
diff --git a/zh-cn/third-party-cases/distributed-file.md b/zh-cn/third-party-cases/distributed-file.md
new file mode 100644
index 0000000000000000000000000000000000000000..2cef7070e7aa4479d5b4c69639e91d06d2ad1f4f
--- /dev/null
+++ b/zh-cn/third-party-cases/distributed-file.md
@@ -0,0 +1,482 @@
+# 分布式文件场景
+
+## 场景说明
+
+两台设备组网的分布式场景是工作中常常需要的。常见的如代码的同步编辑、文档的同步修改等。这样的分布式场景有助于加快工作效率,减少工作中的冗余,本例将为大家介绍如何实现上述功能。
+
+## 效果呈现
+
+本例效果如下:
+
+| 设置分布式权限 | 进行分布式连接 | 连接后状态显示 |
+| -------------------------------------- | ---------------------------------------- | --------------------------------------- |
+| ![](figures/disributed_permission.png) | ![](figures/disributed_note_connect.png) | ![](figures/disributed_note-before.png) |
+
+| 点击添加进入编辑界面 | 保存后本机显示 | 另外一台机器分布式应用显示 |
+| ------------------------------------- | -------------------------------------- | -------------------------------------- |
+| ![](figures/disributed_note-edit.png) | ![](figures/disributed_note-after.png) | ![](figures/disributed_note-after.png) |
+
+## 运行环境
+
+本例基于以下环境开发,开发者也可以基于其他适配的版本进行开发。
+
+- IDE:DevEco Studio 4.0.0.201 Beta1
+- SDK:Ohos_sdk_public 4.0.7.5 (API Version 10 Beta1)
+
+## 实现思路
+
+在分布式文件场景中,分布式设备管理包含了分布式设备搜索、分布式设备列表弹窗、远端设备拉起三部分。
+首先在分布式组网内搜索设备,然后把设备展示到分布式设备列表弹窗中,最后根据用户的选择拉起远端设备。
+
+- 分布式设备搜索:通过SUBSCRIBE_ID搜索分布式组网内的设备。
+
+- 分布式设备列表弹窗:使用@CustomDialog装饰器来装饰分布式设备列表弹窗。
+
+- 远端设备拉起:通过startAbility(deviceId)方法拉起远端设备的包。
+
+- 分布式数据管理:(1)管理分布式数据库:创建一个distributedObject分布式数据对象实例,用于管理分布式数据对象。
+
+ (2)订阅分布式数据变化:通过this.distributedObject.on('status', this.statusCallback)监听分布式数据对象的变更。
+
+## 开发步骤
+
+1. 申请所需权限
+
+ 在model.json5中添加以下配置:
+
+ ```json
+ "requestPermissions": [
+ {
+ "name": "ohos.permission.DISTRIBUTED_DATASYNC"//允许不同设备间的数据交换
+ },
+ {
+ "name": "ohos.permission.ACCESS_SERVICE_DM"//允许系统应用获取分布式设备的认证组网能力
+ }
+ ]
+ ```
+
+2. 构建UI框架
+
+ index页面:
+
+ TitleBar组件呈现标题栏。使用List组件呈现文件列表,ListItem由一个呈现文件类型标志的Image组件,一个呈现文件标题的Text组件,一个呈现文件内容的Text组件组成。
+
+ ```typescript
+ build() {
+ Column() {
+ TitleBar({ rightBtn: $r('app.media.trans'), onRightBtnClicked: this.showDialog })
+ //自/common/TitleBar.ets中引入标题栏相关。点击标题栏中的右侧按钮会调用showDialog()函数连接组网设备
+ Row() {
+ Text($r('app.string.state'))
+ .fontSize(30)
+ Image(this.isOnline ? $r('app.media.green') : $r('app.media.red'))//两台设备组网成功后状态显示为绿色、否则为红色
+ .size({ width: 30, height: 30 })
+ .objectFit(ImageFit.Contain)
+ }
+ .width('100%')
+ .padding(16)
+ //通过数据懒加载的方式从数据源中每次迭代一个文件进行展示,可用列表被放置在滚动容器中,被划出可视区域外的资源会被回收
+ List({ space: 10 }) {
+ LazyForEach(this.noteDataSource, (item: Note, index) => {
+ ListItem() {
+ NoteItem({ note: item, index: index })//NoteItem引入自common/NoteItem.ets,负责主页文件信息的呈现
+ .id(`${item.title}`)
+ }
+ }, item => JSON.stringify(item))
+ }
+ .width('95%')
+ .margin(10)
+ .layoutWeight(1)
+
+ Row() {
+ Column() {
+ Image($r('app.media.clear'))//清除按钮
+ .size({ width: 40, height: 40 })
+ Text($r('app.string.clear'))
+ .fontColor(Color.Red)
+ .fontSize(20)
+ }.layoutWeight(1)
+ .id('clearNote')
+ .onClick(() => {
+ //点击清除按钮清除所有文件
+ Logger.info(TAG, 'clear notes')
+ this.noteDataSource['dataArray'] = []
+ this.noteDataSource.notifyDataReload()
+ this.globalObject.clear()
+ AppStorage.SetOrCreate('sessionId', this.sessionId)
+ })
+
+ Column() {
+ Image($r('app.media.add'))//添加按钮
+ .size({ width: 40, height: 40 })
+ Text($r('app.string.add'))
+ .fontColor(Color.Black)
+ .fontSize(20)
+ }.layoutWeight(1)
+ .id('addNote')
+ .onClick(() => {
+ //点击添加按钮跳转到编辑页面
+ router.push({
+ url: 'pages/Edit',
+ params: {
+ note: new Note('', '', -1),
+ isAdd: true
+ }
+ })
+ })
+ }
+ .width('100%')
+ .padding(10)
+ .backgroundColor('#F0F0F0')
+ }
+ .width('100%')
+ .height('100%')
+ .backgroundColor('#F5F5F5')
+ }
+ }
+ ...
+ //common/NoteItem.ets
+ import router from '@ohos.router'
+ import { MARKS } from '../model/Const'
+ import Note from '../model/Note'
+
+ @Component
+ export default struct NoteItem {
+ @State note: Note | undefined = undefined
+ private index: number = 0
+
+ build() {
+ Row() {
+ Image(this.note.mark >= 0 ? MARKS[this.note.mark] : $r('app.media.note'))//文件标志图片
+ .size({ width: 30, height: 30 })
+ .objectFit(ImageFit.Contain)
+ Column() {
+ Text(this.note.title)//文件标题
+ .fontColor(Color.Black)
+ .fontSize(30)
+ .maxLines(1)
+ .textOverflow({ overflow: TextOverflow.Ellipsis })
+ Text(this.note.content)//文件内容
+ .fontColor(Color.Gray)
+ .margin({ top: 10 })
+ .fontSize(25)
+ .maxLines(1)//在列表中最多展示一行
+ .textOverflow({ overflow: TextOverflow.Ellipsis })
+ }
+ .alignItems(HorizontalAlign.Start)
+ .margin({ left: 20 })
+ }
+ .padding(16)
+ .width('100%')
+ .borderRadius(16)
+ .backgroundColor(Color.White)
+ .onClick(() => {
+ //点击文件进入此文件编辑页面
+ router.push({
+ url: 'pages/Edit',
+ params: {
+ index: this.index,
+ note: this.note,
+ isAdd: false
+ }
+ })
+ })
+ }
+ }
+ ```
+
+ Edit页面:
+
+ 使用TextInput组件呈现文件标题输入框,使用TextArea组件呈现文件内容的输入区域,使用Button组件呈现保存按钮并绑定点击事件以新建或更新文件内容。
+
+ ```typescript
+ build() {
+ Column() {
+ TitleBar({ title: this.note.title === '' ? $r('app.string.add_note') : this.note.title })
+ Column() {
+ Row() {
+ Image(this.note.mark >= 0 ? MARKS[this.note.mark] : $r('app.media.mark'))
+ .width(30)
+ .aspectRatio(1)
+ .margin({ left: 16, top: 16 })
+ .objectFit(ImageFit.Contain)
+ .alignSelf(ItemAlign.Start)
+ Select([{ value: ' ', icon: MARKS[0] },
+ { value: ' ', icon: MARKS[1] },
+ { value: ' ', icon: MARKS[2] },
+ { value: ' ', icon: MARKS[3] },
+ { value: ' ', icon: MARKS[4] }])
+ .selected(this.note.mark)
+ .margin({ top: 5 })
+ .onSelect((index: number) => {
+ this.note.mark = index
+ })
+ }
+ .width('100%')
+
+ TextInput({ placeholder: 'input the title', text: this.note.title })//文件标题输入框
+ .id('titleInput')
+ .placeholderColor(Color.Gray)
+ .fontSize(30)
+ .margin({ left: 15, right: 15, top: 15 })
+ .height(60)
+ .backgroundColor(Color.White)
+ .onChange((value: string) => {
+ this.note.title = value
+ })
+ TextArea({ placeholder: 'input the content', text: this.note.content })//文件内容输入区域
+ .id('contentInput')
+ .placeholderColor(Color.Gray)
+ .backgroundColor(Color.White)
+ .fontSize(30)
+ .height('35%')
+ .margin({ left: 16, right: 16, top: 16 })
+ .textAlign(TextAlign.Start)
+ .onChange((value: string) => {
+ this.note.content = value
+ })
+
+ Button() {
+ //保存按钮
+ Text($r('app.string.save'))
+ .fontColor(Color.White)
+ .fontSize(17)
+ }
+ .id('saveNote')
+ .backgroundColor('#0D9FFB')
+ .height(50)
+ .width(200)
+ .margin({ top: 20 })
+ .onClick(() => {
+ //点击按钮时调用model/DistributedObjectModel.ts定义的类globalObject中的方法
+ if (!this.isAdd) {
+ let index = router.getParams()['index']
+ this.globalObject.update(index, this.note.title, this.note.content, this.note.mark)//编辑时更新内容
+ } else {
+ this.globalObject.add(this.note.title, this.note.content, this.note.mark)//新建时添加内容
+ }
+ router.back()//返回主页
+ })
+ }
+ }
+ .width('100%')
+ .height('100%')
+ .backgroundColor('#F5F5F5')
+ }
+ }
+ ```
+
+3. 将两台设备组网
+
+ 使用自RemoteDeviceModel.ts中引入的类RemoteDeviceModel以扫描获得附近可以连接的设备。
+
+ ```typescript
+ showDialog = () => {
+ //RemoteDeviceModel引入自model/RemoteDeviceModel.ts
+ RemoteDeviceModel.registerDeviceListCallback(() => {
+ //得到附近可信的设备列表
+ Logger.info(TAG, 'registerDeviceListCallback, callback entered')
+ this.devices = []
+ this.devices = RemoteDeviceModel.discoverDevices.length > 0 ? RemoteDeviceModel.discoverDevices : RemoteDeviceModel.devices
+ if (this.dialogController) {
+ this.dialogController.close()
+ this.dialogController = undefined
+ }
+ this.dialogController = new CustomDialogController({
+ builder: DeviceDialog({
+ devices: this.devices,
+ onSelectedIndexChange: this.onSelectedDevice
+ }),
+ autoCancel: true
+ })
+ this.dialogController.open()
+ })
+ }
+ ...
+ //model/RemoteDeviceModel.ts
+ import deviceManager from '@ohos.distributedHardware.deviceManager'
+ registerDeviceListCallback(stateChangeCallback: () => void) {
+ if (typeof (this.deviceManager) !== 'undefined') {
+ this.registerDeviceListCallbackImplement(stateChangeCallback)
+ return
+ }
+ Logger.info(TAG, 'deviceManager.createDeviceManager begin')
+ try {
+ deviceManager.createDeviceManager(BUNDLE, (error, value) => {
+ if (error) {
+ Logger.error(TAG, 'createDeviceManager failed.')
+ return
+ }
+ this.deviceManager = value
+ this.registerDeviceListCallbackImplement(stateChangeCallback)
+ Logger.info(TAG, `createDeviceManager callback returned,value=${value}`)
+ })
+ } catch (error) {
+ Logger.error(TAG, `createDeviceManager throw error, code=${error.code} message=${error.message}`)
+ }
+
+ Logger.info(TAG, 'deviceManager.createDeviceManager end')
+ }
+ registerDeviceListCallbackImplement(stateChangeCallback: () => void) {
+ Logger.info(TAG, 'registerDeviceListCallback')
+ this.stateChangeCallback = stateChangeCallback
+ if (this.deviceManager === undefined) {
+ Logger.error(TAG, 'deviceManager has not initialized')
+ this.stateChangeCallback()
+ return
+ }
+ Logger.info(TAG, 'getTrustedDeviceListSync begin')
+ try {
+ let list = this.deviceManager.getTrustedDeviceListSync()//同步获取所有可信设备列表
+ Logger.info(TAG, `getTrustedDeviceListSync end, devices=${JSON.stringify(list)}`)
+ if (typeof (list) !== 'undefined' && typeof (list.length) !== 'undefined') {
+ this.devices = list
+ }
+ } catch (error) {
+ Logger.error(TAG, `getLocalDeviceInfoSync throw error, code=${error.code} message=${error.message}`)
+ }
+ this.stateChangeCallback()
+ Logger.info(TAG, 'callback finished')
+ try {
+ this.deviceManager.on('deviceStateChange', (data) => {
+ if (data === null) {
+ return
+ }
+ Logger.info(TAG, `deviceStateChange data = ${JSON.stringify(data)}`)
+ switch (data.action) {
+ case deviceManager.DeviceStateChangeAction.READY://即设备处于可用状态,表示设备间信息已在分布式数据中同步完成, 可以运行分布式业务
+ this.discoverDevices = []
+ this.devices.push(data.device)
+ this.stateChangeCallback()
+ try {
+ let list = this.deviceManager.getTrustedDeviceListSync()
+ if (typeof (list) !== 'undefined' && typeof (list.length) !== 'undefined') {
+ this.devices = list
+ }
+ } catch (error) {
+ Logger.error(TAG, `getTrustedDeviceListSync throw error, code=${error.code} message=${error.message}`)
+ }
+ this.stateChangeCallback()
+ break
+ default:
+ break
+ }
+ })
+ this.deviceManager.on('deviceFound', (data) => {
+ if (data === null) {
+ return
+ }
+ Logger.info(TAG, `deviceFound data=${JSON.stringify(data)}`)
+ this.onDeviceFound(data)
+ })
+ this.deviceManager.on('discoverFail', (data) => {
+ Logger.info(TAG, `discoverFail data=${JSON.stringify(data)}`)
+ })
+ this.deviceManager.on('serviceDie', () => {
+ Logger.info(TAG, 'serviceDie')
+ })
+ } catch (error) {
+ Logger.error(TAG, `on throw error, code=${error.code} message=${error.message}`)
+ }
+ this.startDeviceDiscovery()
+ }
+ startDeviceDiscovery() {
+ SUBSCRIBE_ID = Math.floor(65536 * Math.random())
+ var info = {
+ subscribeId: SUBSCRIBE_ID,
+ mode: 0xAA,
+ medium: 2,
+ freq: 2,//高频率
+ isSameAccount: false,
+ isWakeRemote: true,
+ capability: 0
+ }
+ Logger.info(TAG, `startDeviceDiscovery${SUBSCRIBE_ID}`)
+ try {
+ this.deviceManager.startDeviceDiscovery(info)//开始发现周边设备
+ } catch (error) {
+ Logger.error(TAG, `startDeviceDiscovery throw error, code=${error.code} message=${error.message}`)
+ }
+
+ }
+ ```
+
+4. 实现同步编辑
+
+ 通过AppStorage设置持久性数据,然后实现IDataSource接口,通过注册数据监听接口监听数据的变化。
+
+ ```typescript
+ class BasicDataSource implements IDataSource {
+ private listeners: DataChangeListener[] = []
+
+ public totalCount(): number {
+ return 0
+ }
+
+ public getData(index: number): any {
+ return undefined
+ }
+
+ registerDataChangeListener(listener: DataChangeListener): void {
+ if (this.listeners.indexOf(listener) < 0) {
+ console.info('add listener')
+ this.listeners.push(listener)
+ }
+ }
+
+ unregisterDataChangeListener(listener: DataChangeListener): void {
+ const pos = this.listeners.indexOf(listener);
+ if (pos >= 0) {
+ console.info('remove listener')
+ this.listeners.splice(pos, 1)
+ }
+ }
+ //数据准备好了
+ notifyDataReload(): void {
+ this.listeners.forEach(listener => {
+ listener.onDataReloaded()
+ })
+ }
+ ...
+ }
+
+ onPageShow() {
+ //每当完成编辑或者新建文件,就会回到主页,此时就会执行onPageShow()
+ //noteDataSource获取globalObject保存的分布式的持久性数据,并进行Reload操作传递。
+ this.noteDataSource['dataArray'] = this.globalObject.distributedObject.documents
+ this.noteDataSource.notifyDataReload()
+ Logger.info(TAG, `this.sessionId = ${this.sessionId}`)
+ Logger.info(TAG, `globalSessionId = ${this.globalSessionId}`)
+ if (this.sessionId !== this.globalSessionId) {
+ this.sessionId = this.globalSessionId
+ this.share()
+ }
+ }
+ share() {
+ //多个设备间的对象如果设置为同一个sessionId的笔记数据自动同步
+ Logger.info(TAG, `sessionId = ${this.sessionId}`)
+ this.globalObject.setChangeCallback(() => {
+ this.noteDataSource['dataArray'] = this.globalObject.distributedObject.documents
+ this.noteDataSource.notifyDataReload()
+ })
+ this.globalObject.setStatusCallback((session, networkId, status) => {
+ Logger.info(TAG, `StatusCallback,${status}`)
+ if (status === 'online') {
+ this.isOnline = true
+ } else {
+ this.isOnline = false
+ }
+ })
+ this.globalObject.distributedObject.setSessionId(this.sessionId)
+ AppStorage.SetOrCreate('objectModel', this.globalObject)
+ }
+ ```
+
+## 全部代码
+
+本例完整代码sample示例链接:[分布式对象](https://gitee.com/openharmony/applications_app_samples/tree/master/code/SuperFeature/DistributedAppDev/DistributedNote)
+
+## 参考
+
+- [权限列表](../application-dev/security/permission-list.md#ohospermissiondistributed_datasync)
+- [分布式数据对象](../application-dev/reference/apis/js-apis-data-distributedobject.md)
diff --git a/zh-cn/third-party-cases/figures/bluetooth_delete.png b/zh-cn/third-party-cases/figures/bluetooth_delete.png
new file mode 100644
index 0000000000000000000000000000000000000000..60e73b525e27c0eb73e3f437e749bc96eff16943
Binary files /dev/null and b/zh-cn/third-party-cases/figures/bluetooth_delete.png differ
diff --git a/zh-cn/third-party-cases/figures/bluetooth_delete_en.png b/zh-cn/third-party-cases/figures/bluetooth_delete_en.png
new file mode 100644
index 0000000000000000000000000000000000000000..e9b040c7cd39ba4162feb5f4da81dbc0e64af474
Binary files /dev/null and b/zh-cn/third-party-cases/figures/bluetooth_delete_en.png differ
diff --git a/zh-cn/third-party-cases/figures/bluetooth_dialog.png b/zh-cn/third-party-cases/figures/bluetooth_dialog.png
new file mode 100644
index 0000000000000000000000000000000000000000..38ab4a29a780b6baba25d353a170c0f74743b668
Binary files /dev/null and b/zh-cn/third-party-cases/figures/bluetooth_dialog.png differ
diff --git a/zh-cn/third-party-cases/figures/bluetooth_dialog_en.png b/zh-cn/third-party-cases/figures/bluetooth_dialog_en.png
new file mode 100644
index 0000000000000000000000000000000000000000..63857c729802e00d4c3cc65ac236aa2ff34ce607
Binary files /dev/null and b/zh-cn/third-party-cases/figures/bluetooth_dialog_en.png differ
diff --git a/zh-cn/third-party-cases/figures/bluetooth_list.png b/zh-cn/third-party-cases/figures/bluetooth_list.png
new file mode 100644
index 0000000000000000000000000000000000000000..6edbda56e6cf01a45d055f995169ccf6e9463cbb
Binary files /dev/null and b/zh-cn/third-party-cases/figures/bluetooth_list.png differ
diff --git a/zh-cn/third-party-cases/figures/bluetooth_list_en.png b/zh-cn/third-party-cases/figures/bluetooth_list_en.png
new file mode 100644
index 0000000000000000000000000000000000000000..4df05c5c4aee9216e0a0b5d2c0d7164fb18949be
Binary files /dev/null and b/zh-cn/third-party-cases/figures/bluetooth_list_en.png differ
diff --git a/zh-cn/third-party-cases/figures/bluetooth_match.png b/zh-cn/third-party-cases/figures/bluetooth_match.png
new file mode 100644
index 0000000000000000000000000000000000000000..1daa1043afbdc2648c73675aba365722dc3df2b1
Binary files /dev/null and b/zh-cn/third-party-cases/figures/bluetooth_match.png differ
diff --git a/zh-cn/third-party-cases/figures/bluetooth_match_en.png b/zh-cn/third-party-cases/figures/bluetooth_match_en.png
new file mode 100644
index 0000000000000000000000000000000000000000..4cb0101c6dd18298dbd1f94c4c56af4bc6c5da12
Binary files /dev/null and b/zh-cn/third-party-cases/figures/bluetooth_match_en.png differ
diff --git a/zh-cn/third-party-cases/figures/camera.png b/zh-cn/third-party-cases/figures/camera.png
new file mode 100644
index 0000000000000000000000000000000000000000..245a0585d523889ba1f4411a1593176160ff80be
Binary files /dev/null and b/zh-cn/third-party-cases/figures/camera.png differ
diff --git a/zh-cn/third-party-cases/figures/camerapreview.gif b/zh-cn/third-party-cases/figures/camerapreview.gif
new file mode 100644
index 0000000000000000000000000000000000000000..1e1f06dac46a9386f941d4701e5143fd0388ab1c
Binary files /dev/null and b/zh-cn/third-party-cases/figures/camerapreview.gif differ
diff --git a/zh-cn/third-party-cases/figures/connect.png b/zh-cn/third-party-cases/figures/connect.png
new file mode 100644
index 0000000000000000000000000000000000000000..18be004d104f1672e42af11186eecc7586333a41
Binary files /dev/null and b/zh-cn/third-party-cases/figures/connect.png differ
diff --git a/zh-cn/third-party-cases/figures/disributed_canvas-after.png b/zh-cn/third-party-cases/figures/disributed_canvas-after.png
new file mode 100644
index 0000000000000000000000000000000000000000..7bc98d9f9f596af7150c173f9577a30f1f5ee1ff
Binary files /dev/null and b/zh-cn/third-party-cases/figures/disributed_canvas-after.png differ
diff --git a/zh-cn/third-party-cases/figures/disributed_canvas-before.png b/zh-cn/third-party-cases/figures/disributed_canvas-before.png
new file mode 100644
index 0000000000000000000000000000000000000000..e6a17222c21405ee2a191946af81c5429fa821e7
Binary files /dev/null and b/zh-cn/third-party-cases/figures/disributed_canvas-before.png differ
diff --git a/zh-cn/third-party-cases/figures/disributed_canvas_connect.png b/zh-cn/third-party-cases/figures/disributed_canvas_connect.png
new file mode 100644
index 0000000000000000000000000000000000000000..249fae29cd541d0bae0150448eed500d70fdaf06
Binary files /dev/null and b/zh-cn/third-party-cases/figures/disributed_canvas_connect.png differ
diff --git a/zh-cn/third-party-cases/figures/disributed_note-after.png b/zh-cn/third-party-cases/figures/disributed_note-after.png
new file mode 100644
index 0000000000000000000000000000000000000000..9eef39c0351577a06a15be085bca058051bc8012
Binary files /dev/null and b/zh-cn/third-party-cases/figures/disributed_note-after.png differ
diff --git a/zh-cn/third-party-cases/figures/disributed_note-before.png b/zh-cn/third-party-cases/figures/disributed_note-before.png
new file mode 100644
index 0000000000000000000000000000000000000000..5b863b7a491a6a3671815637c90b13830043cc7e
Binary files /dev/null and b/zh-cn/third-party-cases/figures/disributed_note-before.png differ
diff --git a/zh-cn/third-party-cases/figures/disributed_note-edit.png b/zh-cn/third-party-cases/figures/disributed_note-edit.png
new file mode 100644
index 0000000000000000000000000000000000000000..585a7084b4a95f63a08aaa2ac8a1a783c4727602
Binary files /dev/null and b/zh-cn/third-party-cases/figures/disributed_note-edit.png differ
diff --git a/zh-cn/third-party-cases/figures/disributed_note_connect.png b/zh-cn/third-party-cases/figures/disributed_note_connect.png
new file mode 100644
index 0000000000000000000000000000000000000000..a983cf00ca81f7280c84bb1e05d03494a5b76df4
Binary files /dev/null and b/zh-cn/third-party-cases/figures/disributed_note_connect.png differ
diff --git a/zh-cn/third-party-cases/figures/disributed_permission.png b/zh-cn/third-party-cases/figures/disributed_permission.png
new file mode 100644
index 0000000000000000000000000000000000000000..8d86371911f97124694b2555b1dd6eee975a9147
Binary files /dev/null and b/zh-cn/third-party-cases/figures/disributed_permission.png differ
diff --git a/zh-cn/third-party-cases/figures/distributed.png b/zh-cn/third-party-cases/figures/distributed.png
new file mode 100644
index 0000000000000000000000000000000000000000..b0fb2e9bc8958a06521bf2befe5377f4a195776b
Binary files /dev/null and b/zh-cn/third-party-cases/figures/distributed.png differ
diff --git a/zh-cn/third-party-cases/figures/wlanconnect.png b/zh-cn/third-party-cases/figures/wlanconnect.png
new file mode 100644
index 0000000000000000000000000000000000000000..7e1a604a564ad61652d907311a68dc15ecbf0e29
Binary files /dev/null and b/zh-cn/third-party-cases/figures/wlanconnect.png differ
diff --git a/zh-cn/third-party-cases/figures/wlandisconnect.png b/zh-cn/third-party-cases/figures/wlandisconnect.png
new file mode 100644
index 0000000000000000000000000000000000000000..5050ec683cd16c0fc8a224924584f1facccf2906
Binary files /dev/null and b/zh-cn/third-party-cases/figures/wlandisconnect.png differ
diff --git a/zh-cn/third-party-cases/figures/wlanmain.png b/zh-cn/third-party-cases/figures/wlanmain.png
new file mode 100644
index 0000000000000000000000000000000000000000..c8454fc71d01b013dbb68c0579773d12278782a6
Binary files /dev/null and b/zh-cn/third-party-cases/figures/wlanmain.png differ
diff --git a/zh-cn/third-party-cases/figures/wlanscan.png b/zh-cn/third-party-cases/figures/wlanscan.png
new file mode 100644
index 0000000000000000000000000000000000000000..366799135770c2bb5fa3d93f67b251f304d42641
Binary files /dev/null and b/zh-cn/third-party-cases/figures/wlanscan.png differ
diff --git a/zh-cn/third-party-cases/how-to-connect-to-bluetooth.md b/zh-cn/third-party-cases/how-to-connect-to-bluetooth.md
new file mode 100644
index 0000000000000000000000000000000000000000..45b22ac7716edab37e44b46b6ed54cc39b7e7fd6
--- /dev/null
+++ b/zh-cn/third-party-cases/how-to-connect-to-bluetooth.md
@@ -0,0 +1,413 @@
+# 如何进行蓝牙连接
+
+## 场景说明
+蓝牙技术是一种无线数据和语音通信开放的全球规范,它是基于低成本的近距离无线连接,为固定和移动设备建立通信环境的一种特殊的连接。本示例通过[@ohos.bluetoothManager](../application-dev/reference/apis/js-apis-bluetoothManager.md)接口实现蓝牙设备发现、配对、取消配对功能。
+
+## 效果呈现
+
+本示例最终效果如下:
+
+| 发现设备 | 连接设备 | 断开连接 |
+| ------------------------------- | --------------------------------- | --------------------------------- |
+| ![](figures/bluetooth_list.png) | ![](figures/bluetooth_dialog.png) | ![](figures/bluetooth_delete.png) |
+
+## 运行环境
+
+本例基于以下环境开发,开发者也可以基于其他适配的版本进行开发。
+
+- IDE:DevEco Studio 3.1.1 Release
+- SDK:Ohos_sdk_full 4.0.8.5(API Version 10 Beta1)
+
+## 实现思路
+本文涉及到蓝牙的设备发现、配对、取消配对三个功能特性,实现思路如下:
+
+- 启动和关闭蓝牙:在Index页面中通过Toggle组件的onChange函数控制蓝牙的开关,开关打开的情况下执行initBluetooth函数,关闭的情况下执行bluetooth.disableBluetooth()方法来断开蓝牙;
+- 验证蓝牙是否处于连接状态:蓝牙打开的时候通过bluetooth.on('stateChange')方法监听蓝牙连接状态改变事件,如确认已打开,执行foundDevices()函数来查找设备接口,确认已关闭则执行bluetooth.stopBluetoothDiscovery()方法停止查询接口。
+- 发现设备:在foundDevices()函数中通过bluetooth.on('bluetoothDeviceFind')方法监听设备发现事件,通过bluetooth.getPairedDevices()方法更新已配对蓝牙地址,然后通过bluetooth.startBluetoothDiscovery()方法开启蓝牙扫描发现远端设备,并且通过bluetooth.setBluetoothScanMode()方法来被远端设备发现。
+- 蓝牙配对:通过bluetooth.on('pinRequired')方法监听远端蓝牙设备的配对请求事件,点击配对执行bluetooth.setDevicePairingConfirmation(this.data.deviceId, true)方法,点击取消执行bluetooth.setDevicePairingConfirmation(this.data.deviceId, false)方法。
+- 取消配对:使用bluetooth.cancelPairedDevice()断开指定的远端设备连接。
+
+## 开发步骤
+1. 申请蓝牙权限。
+ 使用蓝牙需要先申请对应的权限,在module.json5文件中添加以下配置:
+
+ ```json
+ "requestPermissions": [
+ {
+ //允许应用查看蓝牙的配置
+ "name": "ohos.permission.USE_BLUETOOTH",
+ "reason": "$string:grant_use_bluetooth",
+ "usedScene": {
+ "abilities": [
+ "MainAbility"
+ ],
+ "when": "inuse"
+ }
+ },
+ {
+ //允许应用配置本地蓝牙,查找远端设备且与之配对连接
+ "name": "ohos.permission.DISCOVER_BLUETOOTH",
+ "reason": "$string:grant_discovery_bluetooth",
+ "usedScene": {
+ "abilities": [
+ "MainAbility"
+ ],
+ "when": "inuse"
+ }
+ },
+ {
+ //允许应用获取设备位置信息
+ "name": "ohos.permission.LOCATION",
+ "reason": "$string:grant_location",
+ "usedScene": {
+ "abilities": [
+ "MainAbility"
+ ],
+ "when": "inuse"
+ }
+ },
+ {
+ //允许应用获取设备模糊位置信息
+ "name": "ohos.permission.APPROXIMATELY_LOCATION",
+ "reason": "$string:grant_location",
+ "usedScene": {
+ "abilities": [
+ "MainAbility"
+ ],
+ "when": "inuse"
+ }
+ },
+ {
+ //允许应用配对蓝牙设备,并对设备的电话簿或消息进行访问
+ "name": "ohos.permission.MANAGE_BLUETOOTH",
+ "reason": "$string:grant_manage_bluetooth",
+ "usedScene": {
+ "abilities": [
+ "MainAbility"
+ ],
+ "when": "inuse"
+ }
+ }
+ ]
+ ```
+
+2. 构建UI框架,整体的UI框架分为TitleBar(页面名称),PinDialog(配对蓝牙弹框),index(主页面)三个部分。
+
+ ```ts
+ //Common/TitleBar.ets
+ @Component
+ export struct TitleBar {
+ private handlerClickButton: () => void
+
+ build() {
+ Row() {
+ Image($r('app.media.ic_back'))
+ .width(40)
+ .height(30)
+ .onClick(() => {
+ this.handlerClickButton()
+ })
+ Text($r('app.string.bluetooth'))
+ .fontSize(30)
+ .width(150)
+ .height(50)
+ .margin({ left: 15 })
+ .fontColor('#ffa2a3a4')
+ }
+ .width('100%')
+ .height(60)
+ .padding({ left: 20, top: 10 })
+ .backgroundColor('#ff2d30cb')
+ .constraintSize({ minHeight: 50 })
+ }
+ }
+
+ //Common/PinDalog.ets
+ ...
+ aboutToAppear() {
+ this.titleText = `"${this.data.deviceId}"要与您配对。请确认此配对码已在"${this.data.deviceId}"上直接显示,且不是手动输入的。`
+ this.pinCode = JSON.stringify(this.data.pinCode)
+ }
+ build() {
+ //配对弹框描述文字
+ Column({ space: 10 }) {
+ Text($r('app.string.match_request'))
+ .fontSize(30)
+ .alignSelf(ItemAlign.Start)
+ Text(this.titleText)
+ .alignSelf(ItemAlign.Start)
+ .margin({ top: 20 })
+ .fontSize(21)
+ Text(this.pinCode)
+ .fontSize(40)
+ .fontWeight(FontWeight.Bold)
+ .margin({ top: 20 })
+ Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center }) {
+ Checkbox({ name: 'checkbox' })
+ .select(false)
+ .selectedColor('#ff3d6fb8')
+ .key('checkBox')
+ Text($r('app.string.grant_permission'))
+ .fontSize(15)
+ .margin({ left: 3, top: 6 })
+ }
+ .alignSelf(ItemAlign.Start)
+ .width('95%')
+ .margin({ top: 5 })
+
+ Row() {
+ //配对选择UI,取消or配对
+ this.choiceText($r('app.string.cancel'), () => {
+ bluetooth.setDevicePairingConfirmation(this.data.deviceId, false)
+ logger.info(TAG, `setDevicePairingConfirmation = ${bluetooth.setDevicePairingConfirmation(this.data.deviceId, false)}`)
+ this.controller.close()
+ })
+
+ Divider()
+ .vertical(true)
+ .height(32)
+
+ this.choiceText($r('app.string.match'), () => {
+ bluetooth.setDevicePairingConfirmation(this.data.deviceId, true)
+ logger.info(TAG, `setDevicePairingConfirmation = ${bluetooth.setDevicePairingConfirmation(this.data.deviceId, true)}`)
+ this.controller.close()
+ })
+ }
+ .margin({ top: 20 })
+ }
+ .width('100%')
+ .padding(15)
+ }
+ ...
+
+ //pages/index.ets
+ @Entry
+ @Component
+ struct Index {
+ build() {
+ Column() {
+ TitleBar({ handlerClickButton: this.handlerClickButton })
+ Scroll() {
+ Column() {
+ Row() {
+ //蓝牙开关
+ Column() {
+ Text($r('app.string.bluetooth'))
+ .fontSize(30)
+ .margin({ top: 20 })
+ .alignSelf(ItemAlign.Start)
+ if (true === this.isOn) {
+ Text($r('app.string.discovery'))
+ .fontSize(20)
+ .alignSelf(ItemAlign.Start)
+ }
+ }
+
+ Blank()
+
+ Column() {
+ Toggle({ type: ToggleType.Switch, isOn: this.isOn })
+ .selectedColor('#ff2982ea')
+ .onChange((isOn: boolean) => {
+ if (isOn) {
+ this.isOn = true
+ this.initBluetooth()
+ } else {
+ this.isOn = false
+ bluetooth.disableBluetooth()
+ this.deviceList = []
+ this.discoveryList = []
+ }
+ })
+ }
+ .id('toggleBtn')
+ }
+ .width('90%')
+
+ if (this.isOn) {
+ Divider()
+ .vertical(false)
+ .strokeWidth(10)
+ .color('#ffece7e7')
+ .lineCap(LineCapStyle.Butt)
+ .margin('1%')
+ //已配对的设备
+ Text($r('app.string.paired_device'))
+ .fontSize(25)
+ .fontColor('#ff565555')
+ .margin({ left: '5%' })
+ .alignSelf(ItemAlign.Start)
+
+ ForEach(this.deviceList, (item, index) => {
+ Row() {
+ Text(item)
+ .fontSize(20)
+ }
+ .alignSelf(ItemAlign.Start)
+ .width('100%')
+ .height(50)
+ .margin({ left: '5%', top: '1%' })
+ .id(`pairedDevice${index}`)
+ .onClick(() => {
+ AlertDialog.show({
+ //取消配对
+ title: $r('app.string.disconnect'),
+ message: '此操作将会断开您与以下设备的连接:' + item,
+ primaryButton: {
+ value: $r('app.string.cancel'),
+ action: () => {
+ }
+ },
+ //确认取消
+ secondaryButton: {
+ value: $r('app.string.confirm'),
+ action: () => {
+ try {
+ bluetooth.cancelPairedDevice(item);
+ this.deviceList = bluetooth.getPairedDevices()
+ this.discoveryList = []
+ bluetooth.startBluetoothDiscovery()
+ } catch (err) {
+ console.error("errCode:" + err.code + ",errMessage:" + err.message);
+ }
+ }
+ }
+ })
+ })
+ })
+
+ Divider()
+ .vertical(false)
+ .strokeWidth(10)
+ .color('#ffece7e7')
+ .lineCap(LineCapStyle.Butt)
+ .margin('1%')
+
+ Text($r('app.string.available_device'))
+ .fontSize(25)
+ .fontColor('#ff565555')
+ .margin({ left: '5%', bottom: '2%' })
+ .alignSelf(ItemAlign.Start)
+ //可用设备列表
+ ForEach(this.discoveryList, (item) => {
+ Row() {
+ Text(item)
+ .fontSize(20)
+ }
+ .alignSelf(ItemAlign.Start)
+ .width('100%')
+ .height(50)
+ .margin({ left: '5%', top: '1%' })
+ //进行配对操作点击
+ .onClick(() => {
+ logger.info(TAG, `start bluetooth.pairDevice,item = ${item}`)
+ let pairStatus = bluetooth.pairDevice(item)
+ logger.info(TAG, `pairStatus = ${pairStatus}`)
+ })
+
+ Divider()
+ .vertical(false)
+ .color('#ffece7e7')
+ .lineCap(LineCapStyle.Butt)
+ .margin('1%')
+ })
+ }
+ }
+ }
+ .constraintSize({ maxHeight: '85%' })
+ }
+ }
+ }
+ ```
+
+3. 蓝牙开关打开关闭操作,需要打开蓝牙开关才能进行后续操作:
+ ```ts
+ initBluetooth() {
+ bluetooth.on('stateChange', (data) => {
+ logger.info(TAG, `enter on stateChange`)
+ //判断蓝牙开关状态
+ if (data === bluetooth.BluetoothState.STATE_ON) {
+ logger.info(TAG, `enter BluetoothState.STATE_ON`)
+ //蓝牙打开后的相关操作
+ this.foundDevices()
+ }
+ if (data === bluetooth.BluetoothState.STATE_OFF) {
+ logger.info(TAG, `enter BluetoothState.STATE_OFF`)
+ bluetooth.off('bluetoothDeviceFind', (data) => {
+ logger.info(TAG, `offBluetoothDeviceFindData = ${JSON.stringify(data)}`)
+ })
+ //关闭
+ bluetooth.stopBluetoothDiscovery()
+ this.discoveryList = []
+ }
+ logger.info(TAG, `BluetoothState = ${JSON.stringify(data)}`)
+ })
+ //开启蓝牙
+ bluetooth.enableBluetooth()
+ }
+ ```
+
+4. 设置蓝牙扫描模式并开启扫描去发现设备,并订阅蓝牙设备发现上报时间获取设备列表
+ ```ts
+ foundDevices() {
+ //订阅蓝牙设备发现上报事件
+ bluetooth.on('bluetoothDeviceFind', (data) => {
+ logger.info(TAG, `enter on bluetoothDeviceFind`)
+ if (data !== null && data.length > 0) {
+ if (this.discoveryList.indexOf(data[0]) === -1 && this.deviceList.indexOf(data[0]) === -1) {
+ this.discoveryList.push(data[0])
+ }
+ logger.info(TAG, `discoveryList = ${JSON.stringify(this.discoveryList)}`)
+ }
+ let list = bluetooth.getPairedDevices()
+ if (list !== null && list.length > 0) {
+ this.deviceList = list
+ logger.info(TAG, `deviceList = ${JSON.stringify(this.deviceList)}`)
+ }
+ })
+ //开启蓝牙扫描,可以发现远端设备
+ bluetooth.startBluetoothDiscovery()
+ //设置蓝牙扫描模式,可以被远端设备发现
+ bluetooth.setBluetoothScanMode(bluetooth.ScanMode.SCAN_MODE_CONNECTABLE_GENERAL_DISCOVERABLE, TIME)
+ }
+ ```
+
+5. 设置蓝牙扫描模式并开启扫描去发现设备,并订阅蓝牙设备发现上报时间获取设备列表
+
+ ```ts
+ //配对确定和取消代码在PinDialog.ets文件中
+ //setDevicePairingConfirmation(device: string, accept: boolean): void
+ //device string 表示远端设备地址,例如:"XX:XX:XX:XX:XX:XX
+ //accept boolean 接受配对请求设置为true,否则设置为false
+
+ //订阅蓝牙配对状态改变事件,根据蓝牙状态更新设备列表
+ bluetooth.on('bondStateChange', (data) => {
+ logger.info(TAG, `enter bondStateChange`)
+ logger.info(TAG, `data = ${JSON.stringify(data)}`)
+ if (data.state === bluetooth.BondState.BOND_STATE_BONDED) {
+ logger.info(TAG, `BOND_STATE_BONDED`)
+ let index = this.discoveryList.indexOf(data.deviceId)
+ this.discoveryList.splice(index, 1)
+ this.deviceList = bluetooth.getPairedDevices()
+ }
+ if (data.state === bluetooth.BondState.BOND_STATE_INVALID) {
+ logger.info(TAG, `BOND_STATE_INVALID`)
+ this.deviceList = bluetooth.getPairedDevices()
+ }
+ logger.info(TAG, `bondStateChange,data = ${JSON.stringify(data)}`)
+ })
+ ```
+
+
+
+## 完整代码
+
+本例完整代码sample示例链接:[蓝牙Sample](https://gitee.com/openharmony/applications_app_samples/tree/master/code/SystemFeature/Connectivity/Bluetooth)
+
+
+
+## 参考
+- [权限申请指导](../application-dev/security/accesstoken-guidelines.md)
+- [@ohos.bluetooth](../application-dev/reference/apis/js-apis-bluetooth.md)
+
+
\ No newline at end of file
diff --git a/zh-cn/third-party-cases/take-picture-and-preview.md b/zh-cn/third-party-cases/take-picture-and-preview.md
new file mode 100644
index 0000000000000000000000000000000000000000..f45e3fee25151ba9b519beb56fac2f5087ee4e2f
--- /dev/null
+++ b/zh-cn/third-party-cases/take-picture-and-preview.md
@@ -0,0 +1,257 @@
+# 如何调用设备摄像头进行拍照、预览并将拍摄结果保存在媒体库中
+
+## 场景说明
+
+调用设备摄像头进行拍照、预览是许多应用开发过程中都需要的功能。在拍照完成时显示照片预览图可以确认拍摄的照片是否达到预期,本例将为大家介绍如何实现上述功能。
+
+## 效果呈现
+
+本例效果如下:
+
+| 拍照 | 预览 |
+| :----------------------------------------------------------: | :----------------------------------------------------------: |
+| | |
+
+
+
+## 运行环境
+
+本例基于以下环境开发,开发者也可以基于其他适配的版本进行开发。
+
+- IDE:DevEco Studio 4.0.0.201 Beta1
+- SDK:Ohos_sdk_public 4.0.7.5 (API Version 10 Beta1)
+
+## 实现思路
+
+本例使用@ohos.multimedia.camera接口实现相机示例的主要功能:拍照、预览;
+
+- 拍照:XComponent组件负责绘制摄像头画面呈现的窗口,其onload事件调用cameraModel.ts的initCamera方法初始化相机功能输出画面信息。拍照动作使用Image组件实现,其onclick事件调用cameraModel.ts的takepicture方法开始拍照。
+
+- 预览:返回相机界面点击底部左侧预览图可进入相册应用,可以在其中查看照片和录制的视频。
+
+## 开发步骤
+
+1. 申请所需权限
+
+ 在model.json5中添加以下配置:
+
+ ```json
+ "requestPermissions": [
+ {
+ "name": "ohos.permission.CAMERA"//允许应用使用相机拍摄照片和录制视频
+ },
+ {
+ "name": "ohos.permission.MICROPHONE"//允许应用使用麦克风
+ },
+ {
+ "name": "ohos.permission.MEDIA_LOCATION"//允许应用访问用户媒体文件中的地理位置信息
+ },
+ {
+ "name": "ohos.permission.WRITE_MEDIA"//允许应用读写用户外部存储中的媒体文件信息
+ },
+ {
+ "name": "ohos.permission.READ_MEDIA"//允许应用读取用户外部存储中的媒体文件信息
+ }
+ ]
+ ```
+
+2. 创建绘制组件XComponent以输出摄像头获取的画面,其绑定的onload方法中设定了画幅的大小。
+
+ ```typescript
+ build() {
+ Column() {
+ Title()
+ .visibility(this.isTitleShow ? Visibility.Visible : Visibility.None)
+ Stack({ alignContent: Alignment.Bottom }) {
+ Stack({ alignContent: Alignment.TopStart }) {
+ XComponent({
+ id: 'componentId',
+ type: 'surface',
+ controller: this.mXComponentController //将控制器绑定至XComponent组件
+ })
+ .onLoad(() => {
+ this.mXComponentController.setXComponentSurfaceSize({ surfaceWidth: 640, surfaceHeight: 480 });//设置surface大小
+ this.surfaceId = this.mXComponentController.getXComponentSurfaceId();
+ this.currentModel = CameraMode.modePhoto;
+ this.cameraModel.initCamera(this.surfaceId); //调用model/cameraModel.ts初始化相机功能
+ })
+ .width('100%')
+ .height('100%')
+ .margin({ bottom: 152 })
+ Column() {
+ }
+ .width('97%')
+ .height('100%')
+ ```
+
+3. 初始化相机功能
+
+ initCamera方法通过创建相机管理器实例cameraMgr来创建画面输出对象previewOutput。cameraMgr再通过创建CaptureSession实例来配置会话,完成相机功能的准备工作。
+
+ ```typescript
+ import image from '@ohos.multimedia.image';//自@ohos.multimedia.image引入image,提供图片处理效果
+ ...
+ private receiver: image.ImageReceiver = undefined;//图像接收类,用于获取组件surface id,接收最新的图片和读取下一张图片
+ ...
+ constructor() {
+ this.mediaModel = MediaModel.getMediaInstance();//通过调用model/MediaModel.ets中的方法创建mediaInstance类mediaModel
+ //创建ImageReceiver实例receiver
+ this.receiver = image.createImageReceiver(
+ cameraWH.width,
+ cameraWH.height,
+ FOUR,
+ EIGHT
+ );
+ //接收图片时注册回调
+ this.receiver.on('imageArrival', () => {
+ //从ImageReceiver读取下一张图片
+ this.receiver.readNextImage((err, image) => {
+ if (err || image === undefined) {
+ return;
+ }
+ //根据图像的组件类型从图像中获取组件缓存
+ image.getComponent(FOUR, (errMsg, img) => {
+ if (errMsg || img === undefined) {
+ return;
+ }
+ let buffer = new ArrayBuffer(FOUR_THOUSAND_AND_SIXTY_NINE);
+ if (img.byteBuffer) {
+ buffer = img.byteBuffer;
+ }
+ this.saveImage(buffer, image);
+ });
+ });
+ });
+ }
+
+
+ async initCamera(surfaceId: string): Promise {
+ ...
+ try {
+ this.cameraMgr = camera.getCameraManager(globalThis.cameraContext);//获取相机管理器实例
+ }
+ this.camerasArray = this.cameraMgr.getSupportedCameras();//获取支持指定的相机设备对象
+ if (this.camerasArray.length === 0) {
+ return;
+ }
+ let mCamera = this.camerasArray[0];
+ this.cameraInput = this.cameraMgr.createCameraInput(mCamera);
+ this.cameraInput.open();
+ this.capability = this.cameraMgr.getSupportedOutputCapability(mCamera);//查询相机设备支持的输出能力
+ let previewProfile = this.capability.previewProfiles[0];
+ //通过相机管理器创建预览输出对象
+ this.previewOutput = this.cameraMgr.createPreviewOutput(
+ previewProfile,
+ surfaceId //surfaceId从XComponent组件获取
+ );
+ let rSurfaceId = await this.receiver.getReceivingSurfaceId();//获取一个surface id供其他组件使用
+ let photoProfile = this.capability.photoProfiles[0];
+ //通过相机管理器创建照片输出对象
+ this.photoOutPut = this.cameraMgr.createPhotoOutput(
+ photoProfile,
+ rSurfaceId //rSurfaceId通过构造函数中定义的图像接收类receiver获取
+ );
+ this.capSession = this.cameraMgr.createCaptureSession();//创建CaptureSession实例
+ this.capSession.beginConfig();//开始配置会话
+ this.capSession.addInput(this.cameraInput);//将cameraInput加入会话
+ this.capSession.addOutput(this.previewOutput);//将预览输出加入会话
+ this.capSession.addOutput(this.photoOutPut);//将照片输出加入会话
+ await this.capSession.commitConfig();//提交配置信息
+ await this.capSession.start();//开始输出
+ }
+
+ ```
+
+4. 点击按钮进行拍照
+
+ 拍照按钮通过Image组件呈现,其绑定的onClick方法调用takePicture方法开始拍照。
+
+ ```typescript
+ Image(this.getCameraIcon())
+ .size({ width: 64, height: 64 })
+ .margin({ left: 10 })
+ .id('camera')
+ .onClick(() => {
+ if (this.currentModel === CameraMode.modePhoto) {
+ prompt.showToast({ message: '拍照中...', duration: 200 });
+ this.cameraModel.takePicture();//调用model/cameraModel.takePicture()开始拍照
+ }
+ })
+ ```
+
+5. 拍照功能具体实现
+
+ - 拍照
+
+ ```typescript
+ async takePicture(): Promise {
+ //设置拍照相关参数
+ let photoSettings = {
+ rotation: this.imageRotation,
+ quality: camera.QualityLevel.QUALITY_LEVEL_MEDIUM,
+ location: {
+ // 位置信息,经纬度
+ latitude: 12.9698,
+ longitude: 77.75,
+ altitude: 1000,
+ },
+ mirror: false,
+ };
+ await this.photoOutPut.capture(photoSettings);
+ AppStorage.Set('isRefresh', true);
+ }
+ ```
+
+ - 保存图片
+
+ saveImage方法使用MediaModel中的createAndGetUri方法创建Image类型资源,将拍摄到的照片写入到这个资源中去。
+
+ ```typescript
+ //model/MediaModel.ts中定义的负责保存图片的相关方法
+ async createAndGetUri(mediaType: mediaLibrary.MediaType): Promise {
+ let dateTimeUtil: DateTimeUtil = new DateTimeUtil();
+ let info: FileInfo = this.getInfoFromMediaType(mediaType);
+ let name: string = `${dateTimeUtil.getDate()}_${dateTimeUtil.getTime()}`;//获取当前时间
+ let displayName: string = `${info.prefix}${name}${info.suffix}`;
+ //获取公共目录路径。
+ let publicPath: string = await this.mediaLibraryTest.getPublicDirectory(
+ info.directory
+ );//通过引用自@ohos.multimedia.mediaLibrary的mediaLibraryTest类创建媒体资源,其中定义了媒体类型、名称、路径。
+ let fileAsset: mediaLibrary.FileAsset = await this.mediaLibraryTest.createAsset(
+ mediaType,//根据传入函数createAndGetUri的mediaType参数决定创建什么类型的媒体资源
+ displayName,
+ publicPath
+ );
+ return fileAsset;
+ }
+ async getFdPath(fileAsset: mediaLibrary.FileAsset): Promise {
+ let fd: number = await fileAsset.open('Rw');//打开当前文件
+ return fd;
+ }
+ ...
+
+ async saveImage(buffer: ArrayBuffer, img: image.Image): Promise {
+ this.fileAsset = await this.mediaModel.createAndGetUri(mediaLibrary.MediaType.IMAGE);
+ //通过调用MediaModel中的方法创建Image类型资源
+ this.photoPath = this.fileAsset.uri;
+ this.fd = await this.mediaModel.getFdPath(this.fileAsset);
+ await fileIo.write(this.fd, buffer);//将拍摄的照片写入到Mediamodel传回的资源中去
+ await this.fileAsset.close(this.fd);//释放open函数
+ await img.release();
+ if (this.takePictureHandle) {
+ this.takePictureHandle(this.photoPath);
+ }
+ }
+ ```
+
+## 全部代码
+
+本例完整代码sample示例链接:[相机](https://gitee.com/openharmony/applications_app_samples/tree/master/code/SystemFeature/Media/Camera)
+
+## 参考
+
+- [权限列表](https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/security/permission-list.md#ohospermissiondistributed_datasync)
+- [@ohos.multimedia.camera](../application-dev/reference/apis/js-apis-camera.md)
+
+
+
diff --git a/zh-cn/third-party-cases/wlan-search-connect-disconnect.md b/zh-cn/third-party-cases/wlan-search-connect-disconnect.md
new file mode 100644
index 0000000000000000000000000000000000000000..8439befad413a6c6a9460f9b9eb5e60990935fb2
--- /dev/null
+++ b/zh-cn/third-party-cases/wlan-search-connect-disconnect.md
@@ -0,0 +1,448 @@
+# 如何实现WLAN网络扫描、连接、断开
+
+## 场景说明
+
+对可用的WLAN列表进行扫描,连接、断开WLAN网络是设备常见的功能,本例将为大家介绍如何实现上述功能。
+
+## 效果呈现
+
+本例效果如下:
+
+| 扫描WLAN | 连接WLAN | 断开WLAN |
+| :----------------------------------------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: |
+| | | |
+
+## 运行环境
+
+本例基于以下环境开发,开发者也可以基于其他适配的版本进行开发。
+
+- IDE:DevEco Studio 4.0.0.201 Beta1
+- SDK:Ohos_sdk_public 4.0.7.5 (API Version 10 Beta1)
+
+## 实现思路
+
+本例主要通过@ohos.wifiManager 相关API实现WLAN激活和关闭、扫描和连接WLAN等功能。
+
+- WLAN功能的激活和关闭:点击首页的切换按钮,如果是打开,使用wifi.enableWifi()开启WLAN功能;如果是关闭,则使用wifi.disconnect()断开WLAN功能, 然后使用wifi.disableWifi()关闭WLAN。
+- WLAN的连接:点击WLAN列表中加密的WLAN,并在弹窗中输入密码后,会在AvailableWifi.ets中通过WifiModule.connectNetwork()调用wifi.connectToDevice()连接WLAN。
+- WLAN的扫描:进入Index.ets 后就会间歇性的调用wifi.scan()开启扫描,然后通过WifiModel模块中的getScanInfos()调用wifi.getScanResults()来获取扫描的结果.
+
+## 开发步骤
+
+1. 申请所需权限
+
+ 在model.json5中添加以下配置:
+
+ ```json
+ "requestPermissions": [
+ {
+ "name": "ohos.permission.GET_WIFI_INFO"//允许应用获取WLAN信息
+ },
+ {
+ "name": "ohos.permission.GET_WIFI_INFO_INTERNAL"//允许应用获取WLAN信息
+ },
+ {
+ "name": "ohos.permission.SET_WIFI_INFO"//允许应用配置WLAN设备
+ },
+ {
+ "name": "ohos.permission.GET_WIFI_PEERS_MAC"//允许应用获取对端WLAN或者蓝牙设备的MAC地址
+ },
+ {
+ "name": "ohos.permission.GET_WIFI_LOCAL_MAC"//允许应用获取本机WLAN或者蓝牙设备的MAC地址
+ },
+ {
+ "name": "ohos.permission.GET_WIFI_CONFIG"//允许应用获取WLAN配置信息
+ },
+ {
+ "name": "ohos.permission.SET_WIFI_CONFIG"//允许应用配置WLAN信息
+ },
+ {
+ "name": "ohos.permission.MANAGE_WIFI_CONNECTION"//允许应用管理WLAN连接
+ },
+ {
+ "name": "ohos.permission.MANAGE_WIFI_HOTSPOT"//允许应用开启或者关闭WLAN热点
+ },
+ {
+ "name": "ohos.permission.LOCATION"//允许应用获取设备位置信息
+ },
+ {
+ "name": "ohos.permission.APPROXIMATELY_LOCATION"//允许应用获取设备模糊位置信息
+ }
+ ]
+ ```
+
+2. 准备工作
+
+ 定义boolean变量isSwitchOn作为WLAN开关是否打开的标志。
+
+ ```typescript
+ import wifi from '@ohos.wifiManager'
+ aboutToAppear() {
+ // 如果WLAN是开的,就记录下状态,然后扫描WLAN,并获取连接信息
+ if (wifi.isWifiActive()) {//查询WLAN是否已使能
+ Logger.log(TAG, 'wifi is active')
+ this.isSwitchOn = true//已使能说明WLAN开关已被打开,那么让isSwitchOn为true
+ wifi.scan()//@ohos.wifiManager的接口,对WLAN进行扫描
+ this.scan()//自定义的scan函数,确保扫描的持续性
+ this.getLinkedInfo()
+ }
+ // 启动监听
+ this.addListener()//监听连接状态的变化
+ }
+ ```
+
+3. 持续地扫描可用WLAN
+
+ scan方法不断地自我调用以实现对WLAN的不停扫描。
+
+ ```typescript
+ async getLinkedInfo() {
+ try {
+ //调用getLinkedInfo接口得到是否已连接WLAN的信息
+ let wifiLinkedInfo = await wifi.getLinkedInfo()
+ if (wifiLinkedInfo === null || wifiLinkedInfo.bssid === '') {
+ //如果结果为空,则说明未连接
+ this.isLinked = false
+ this.linkedInfo = null
+ return
+ }
+ this.isLinked = true
+ this.linkedInfo = wifiLinkedInfo
+ } catch (err) {
+ Logger.info(`getLinkedInfo failed err is ${JSON.stringify(err)}`)
+ }
+ }
+ async scan() {
+ // 获取有关WLAN连接的信息,存入linkedInfo
+ await this.getLinkedInfo()
+ // 不停地扫描WLAN
+ let result: Array = await this.wifiModel.getScanInfos()//调用model/WifiModel.ets中的getScanInfos得到扫描结果
+ if (this.isSwitchOn) {
+ //如果WLAN功能已打开,则要进行不停地扫描
+ AppStorage.SetOrCreate('wifiList', result)//将扫描结果存至全局
+ //每3000毫秒调用一次scan(),实现不停扫描可用WLAN
+ setTimeout(async () => {
+ await this.scan()
+ }, 3000)
+ }
+ }
+ addListener() {
+ // WLAN连接状态改变事件发生时,修改连接信息
+ wifi.on('wifiConnectionChange', async state => {
+ Logger.log(TAG, `wifiConnectionChange: ${state}`)
+ await this.getLinkedInfo()//WLAN连接状态改变,重新获取WLAN连接信息
+ })
+ // WLAN状态改变时,先清空WLAN列表,然后判断是否是开启状态,如果是就扫描
+ wifi.on('wifiStateChange', state => {
+ Logger.log(TAG, `wifiStateLisener state: ${state}`)
+ AppStorage.SetOrCreate('wifiList', [])
+ if (state === 1) { // 1: wifi is enable, 0:wifi is disable
+ this.scan()
+ }
+ })
+ }
+ ...
+ //model/WifiModel.ets
+ async getScanInfos(): Promise> {
+ Logger.log(TAG, 'scanWifi begin')
+ let wifiList: Array = []
+ let result: Array = []
+ try {
+ result = await wifi.getScanResults()//因为在abouttoappear()中执行了wifi.scan(),所以这里直接调用getScanResults接口得到扫描结果
+ } catch (err) {
+ Logger.log(TAG, `scan info err: ${JSON.stringify(err)}`)
+ return wifiList
+ }
+ Logger.log(TAG, `scan info call back: ${result.length}`)
+ for (var i = 0; i < result.length; ++i) {
+ wifiList.push({
+ ssid: result[i].ssid,
+ bssid: result[i].bssid,
+ securityType: result[i].securityType,
+ rssi: result[i].rssi,
+ band: result[i].band,
+ frequency: result[i].frequency,
+ timestamp: result[i].timestamp
+ })
+ }
+ return wifiList
+ }
+ ```
+
+4. 构建UI框架、通过关闭按钮实现断开WLAN连接。
+
+ TitleBar组件呈现了标题框。WLAN开关按钮由一个Toggle组件呈现,其绑定的onChange事件在按钮被打开时开始扫描可用WLAN,在被关闭时断开当前连接WLAN并且关闭WLAN功能。当前已连接的WLAN由两个Text组件分别显示其ssid与连接状态。
+
+ ```typescript
+ build() {
+ Column() {
+ TitleBar()//引入自component/TitleBar.ets,负责标题框的具体呈现
+ Row() {
+ Text($r('app.string.wlan'))
+ .fontSize(22)
+ .fontWeight(FontWeight.Bold)
+ .height(40)
+ Column() {
+ Toggle({ type: ToggleType.Switch, isOn: this.isSwitchOn })//切换按钮
+ .id('switch')
+ .onChange((isOn: boolean) => {
+ Logger.log(`LSQ: wifi swtich is: ${isOn}`)
+ AppStorage.SetOrCreate('wifiList', [])
+ try {
+ // 如果按钮被打开了,记录状态,打开网络,开始扫描可用WLAN
+ if (isOn) {
+ this.isSwitchOn = true
+ wifi.enableWifi()//使WLAN功能可用
+ return
+ } else {
+ // 如果按钮被关闭了记录状态,断开网络禁用网络
+ this.isSwitchOn = false
+ this.isLinked = false
+ wifi.disconnect()//断开当前WLAN连接
+ wifi.disableWifi()//使WLAN功能不可用
+ }
+ } catch (error) {
+ Logger.error(TAG, `failed,code:${JSON.stringify(error.code)},message:${JSON.stringify(error.message)}`)
+ }
+ })
+ }
+ }
+ .width('100%')
+ .padding({ left: 16, right: 16 })
+ if (this.isLinked && this.isSwitchOn) {
+ //如果当前按钮处于打开状态,且WLAN是已连接状态
+ Column() {
+ Text($r('app.string.connected'))
+ .fontSize(22)
+ .width('100%')
+ Row() {
+ Text(this.linkedInfo.ssid)//展示当前已连接的WLAN
+ .fontSize(20)
+ .fontColor(Color.Black)
+ .layoutWeight(1)
+ Text($r('app.string.connected'))
+ .fontSize(18)
+ .fontColor(Color.Black)
+ }
+ .width('100%')
+ .padding(10)
+ .margin({ left: 16, right: 16 })
+ .border({ radius: 15, color: Color.Gray, width: 1 })
+ .backgroundColor(Color.White)
+ }
+ .width('100%')
+ .padding({ left: 16, right: 16 })
+ }
+ if (this.isSwitchOn) {
+ //如果按钮处于打开状态,展示所有可用WLAN
+ AvailableWifi({ linkedInfo: this.linkedInfo })//AvailableWifi引入自component/AvailableWifi.ets
+ }
+ }
+ .width('100%')
+ .height('100%')
+ .backgroundColor($r('app.color.index_bg'))
+ }
+ ```
+
+5. 展示可用WLAN列表及WLAN的连接。
+
+ 使用List组件呈现可用WLAN列表,ListItem由一个呈现WLANssid的Text组件,一个表示其是否被加密的Text组件和一个展示其加密与否图像的Image组件组成。
+
+ ```typescript
+ //component/AvailableWifi.ets
+ build() {
+ List() {
+ ListItem() {
+ Row() {
+ Text($r('app.string.available_wlan'))
+ .fontSize(22)
+ .layoutWeight(1)
+ }
+ .id('validWlan')
+ .width('100%')
+ }
+ //通过数据懒加载的方式从数据源中每次迭代一个可用WLAN进行展示,可用列表被放置在滚动容器中,被划出可视区域外的资源会被回收
+ LazyForEach(this.wifiDataResource, (item, index) => {
+ ListItem() {
+ WifiView({ wifi: item })//WifiView引入自Component/WifiView.ets,负责可用WLAN信息展示的具体实现
+ }
+ .id(`Wifi${index}`)
+ //对可用WLAN点击时触发事件
+ .onClick(() => {
+ Logger.info(TAG, 'wifi click')
+ this.scanInfo = item
+ if (this.linkedInfo !== null && item.ssid === this.linkedInfo.ssid) {
+ prompt.showToast({ message: 'this wifi is connected' })//如果当前已连接WLAN并且点击的就是这个WLAN,创建并显示文本提示框
+ return
+ }
+ if (item.securityType === 0 || item.securityType === 1) {
+ //如果点击的这个WLAN的加密类型是无效加密类型或者开放加密类型,调用model/Wifimodel.ets中的connectNetwork方法连接此WLAN
+ this.wifiModel.connectNetwork(item, '')
+ return
+ }
+ //如果点击的这个WLAN的加密类型是其他加密类型,则需要输入密码,调用component/PswDialog.ets中的方法进行弹窗的生成
+ this.pswDialogController.open()
+ })
+ }, item => JSON.stringify(item))
+ }
+ .width('100%')
+ .height('100%')
+ .padding({ left: 16, right: 16 })
+ .layoutWeight(1)
+ .divider({ strokeWidth: 1, color: Color.Gray, startMargin: 10, endMargin: 10 })
+ .margin({ top: 10 })
+ }
+ //pswDialogController回调的action函数,将传回的WLAN信息与密码传入model/Wifimodel.ets中的connectNetwork方法中
+ onAccept(scanInfo, psw) {
+ Logger.info(TAG, 'connect wifi')
+ self.wifiModel.connectNetwork(scanInfo, psw)
+ }
+ ...
+ //Component/WifiView.ets
+ aboutToAppear() {
+ Logger.debug(TAG, `aboutToAppear ${JSON.stringify(this.wifi)}`)
+ if (this.wifi) {
+ if (this.wifi.securityType) {
+ if (this.wifi.securityType === 0 || this.wifi.securityType === 1) {
+ //WLAN的加密类型是无效加密类型或者开放加密类型
+ this.securityString = $r('app.string.open')
+ this.isLock = false
+ }
+ }
+ }
+ }
+ build() {
+ Row() {
+ Column() {
+ if (this.wifi) {
+ if (this.wifi.ssid) {
+ Text(this.wifi.ssid)
+ .fontSize(20)
+ .width('100%')
+ }
+ }
+ //如果WLAN的加密类型是无效加密类型或者开放加密类型,显示“开放”,反之显示“加密”
+ Text(this.securityString)
+ .fontSize(18)
+ .fontColor(Color.Gray)
+ .width('100%')
+ }
+ .layoutWeight(1)
+
+ Stack({ alignContent: Alignment.BottomEnd }) {
+ //如果WLAN的加密类型是无效加密类型或者开放加密类型,显示不带锁的图标,反之显示带锁的图标
+ Image($r('app.media.wifi'))
+ .height(30)
+ .width(30)
+ .objectFit(ImageFit.Contain)
+ if (this.isLock) {
+ Image($r('app.media.lock'))
+ .objectFit(ImageFit.Contain)
+ .width(15)
+ .height(15)
+ }
+ }
+ .width(40)
+ .height(40)
+ .margin({ right: 10 })
+ }
+ .backgroundColor(Color.White)
+ .width('100%')
+ .padding(10)
+ }
+ }
+ ...
+ //model/Wifimodel.ets
+ connectNetwork(scanInfo: wifi.WifiScanInfo, psw) {
+ prompt.showToast({ message: 'connecting', duration: 5000 })
+ Logger.debug(TAG, `connectNetwork bssid=${scanInfo.bssid}`)
+ // 这里因为api问题,需要声明为any,已提单
+ let deviceConfig: any = {
+ ssid: scanInfo.ssid,
+ bssid: scanInfo.bssid,
+ preSharedKey: psw,
+ isHiddenSsid: false,
+ securityType: scanInfo.securityType
+ }
+ try {
+ wifi.connectToDevice(deviceConfig)//连接到指定网络
+ Logger.debug(TAG, `connectToDevice success`)
+ } catch (err) {
+ Logger.debug(TAG, `connectToDevice fail err is ${JSON.stringify(err)}`)
+ }
+ try {
+ wifi.addDeviceConfig(deviceConfig)//添加网络配置
+ } catch (err) {
+ Logger.debug(TAG, `addDeviceConfig fail err is ${JSON.stringify(err)}`)
+ }
+ }
+ ...
+ //component/PswDialog.ets
+ build() {
+ Column() {
+ Text(this.scanInfo.ssid)
+ .fontSize(20)
+ .width('95%')
+
+ TextInput({ placeholder: 'input password' })//密码的输入框
+ .id('input')
+ .type(InputType.Password)
+ .placeholderColor(Color.Gray)
+ .fontSize(19)
+ .margin({ top: 15 })
+ .width('95%')
+ .height(36)
+ //每当输入框中的内容变化,它就赋值给psw
+ .onChange((value: string) => {
+ this.psw = value
+ })
+
+ Row() {
+ //确认按钮
+ Button() {
+ Text($r('app.string.sure'))
+ .fontColor(Color.Blue)
+ .fontSize(17)
+ }
+ .id('sure')
+ .layoutWeight(7)
+ .backgroundColor(Color.White)
+ .margin(5)
+ .onClick(() => {
+ this.controller.close()
+ this.action(this.scanInfo, this.psw)
+ })
+
+ Text()
+ .width(1)
+ .height(35)
+ .backgroundColor($r('app.color.text_color'))
+ //取消按钮
+ Button() {
+ Text($r('app.string.cancel'))
+ .fontColor(Color.Red)
+ .fontSize(17)
+ }
+ .layoutWeight(7)
+ .backgroundColor(Color.White)
+ .margin(5)
+ .onClick(() => {
+ this.controller.close()
+ })
+ }
+ .width('100%')
+ .margin({ top: '3%' })
+ }
+ .padding(15)
+ }
+ ```
+
+## 全部代码
+
+本例完整代码sample示例链接:[WLAN](https://gitee.com/openharmony/applications_app_samples/tree/master/code/SystemFeature/Connectivity/Wlan)
+
+## 参考
+
+- [权限列表](https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/security/permission-list.md#ohospermissiondistributed_datasync)
+- [@ohos.wifiManager](../application-dev/reference/apis/js-apis-wifiManager.md)
+