0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

OpenHarmony开发实例:【仿桌面应用】

jf_46214456 来源:jf_46214456 作者:jf_46214456 2024-04-15 16:17 次阅读

介绍

本示例实现了一个简单桌面应用,实现了以下几点功能:

1.展示了系统安装的应用,实现点击启动、应用上滑弹出卡片、卡片添加到桌面、卡片移除功能。

2.实现桌面数据持久化存储,应用支持卸载、监听应用卸载和安装并显示。

3.实现最近任务管理功能,包括任务卡片加锁、解锁、清理和清理所有任务功能。

4.通过点击应用图标或点击由长按图标弹出的菜单栏中的打开按钮的方式打开应用,是以打开最近任务方式拉起应用Ability。

效果预览

image.png

使用说明

1.安装编译的hap包,使用hdc shell aa start -b ohos.samples.launcher -a MainAbility命令启动应用,应用启动后显示系统安装的应用。

2.点击应用主界面上的应用图标可以启动应用,长按弹出菜单,点击打开可以正常启动应用。

3.图库等支持卡片的应用,长按菜单中有服务卡片,点击进入卡片预览界面,在卡片预览界面点击 添加到桌面 ,返回到桌面并且卡片成功添加到桌面。

4.上滑图库等支持卡片的应用,可以弹出默认上滑卡片,点击上滑卡片右上角的**+**图标,可以添加卡片到桌面。

5.应用在桌面界面,使用hdc install安装一个应用,桌面可以监听到应用安装,并显示新安装的应用到桌面上。

6.应用在桌面界面,使用hdc uninstall 卸载第5步安装的应用,桌面可以监听到卸载,并移除桌面上的应用。

7.在桌面空白处上滑,可以进入最近任务管理界面,下滑任务卡片可以加锁/解锁,上滑卡片可以清理该后台任务,点击垃圾桶可以清除所有后台任务(加锁的应用不会被清理掉)。

代码解读

entry/src/main/ets/
|---Application
| |---MyAbilityStage.ts
|---components
| |---FormManagerComponent.ets // 弹窗组件
|---MainAbility
| |---MainAbility.ts
|---manager
| |---WindowManager.ts // 数据类型
|---pages
| |---FormPage.ets // 首页
| |---Home.ets // 详情页面
| |---RecentsPage.ets // 详情页面
鸿蒙HarmonyOSOpenHarmony知识更新+mau123789是v籽料领取


###具体实现

![搜狗高速浏览器截图20240326151344.png](//file1.elecfans.com/web2/M00/C6/C5/wKgaomYCyYKAZp6HAAB4LWPdpdQ014.jpg)

* 获取应用功能模块
  * 使用launcherBundleManager模块接口(系统能力:SystemCapability.BundleManager.BundleFramework),获取所有应用信息和给定包名获取应用信息,实现桌面展示所有安装的应用。使用on接口监听应用的安装和卸载从而实现应用安装和卸载刷新桌面。
* 源码链接:[LauncherAbilityManager.ts]

/*

  • Copyright (c) 2022-2023 Huawei Device Co., Ltd.

  • Licensed under the Apache License, Version 2.0 (the "License");

  • you may not use this file except in compliance with the License.

  • You may obtain a copy of the License at

  • http://www.apache.org/licenses/LICENSE-2.0
    
  • Unless required by applicable law or agreed to in writing, software

  • distributed under the License is distributed on an "AS IS" BASIS,

  • WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

  • See the License for the specific language governing permissions and

  • limitations under the License.

*/

import installer from '@ohos.bundle.installer';

import launcherBundleManager from '@ohos.bundle.launcherBundleManager';

import bundleMonitor from '@ohos.bundle.bundleMonitor';

import osAccount from '@ohos.account.osAccount'

import { AppItemInfo } from '../bean/AppItemInfo'

import { CheckEmptyUtils } from '../utils/CheckEmptyUtils'

import { CommonConstants } from '../constants/CommonConstants'

import { EventConstants } from '../constants/EventConstants'

import { ResourceManager } from './ResourceManager'

import { Logger } from '../utils/Logger'

import type { BusinessError } from '@ohos.base';

const TAG: string = 'LauncherAbilityManager'

/**

  • Wrapper class for innerBundleManager and formManager interfaces.

*/

export class LauncherAbilityManager {

private static readonly BUNDLE_STATUS_CHANGE_KEY_REMOVE = 'remove'

private static readonly BUNDLE_STATUS_CHANGE_KEY_ADD = 'add'

private static launcherAbilityManager: LauncherAbilityManager = undefined

private readonly mAppMap = new Map< string, AppItemInfo >()

private mResourceManager: ResourceManager = undefined

private readonly mLauncherAbilityChangeListeners: any[] = []

private mUserId: number = 100

private context: any = undefined

constructor(context) {

this.context = context

this.mResourceManager = ResourceManager.getInstance(context)

const osAccountManager = osAccount.getAccountManager()

osAccountManager.getOsAccountLocalIdFromProcess((err, localId) = > {

  Logger.debug(TAG, `getOsAccountLocalIdFromProcess localId ${localId}`)

  this.mUserId = localId

})

}

/**

  • Get the application data model object.

  • @return {object} application data model singleton

*/

static getInstance(context): LauncherAbilityManager {

if (this.launcherAbilityManager === null || this.launcherAbilityManager === undefined) {

  this.launcherAbilityManager = new LauncherAbilityManager(context)

}

return this.launcherAbilityManager

}

/**

  • get all app List info from BMS

  • @return 应用的入口Ability信息列表

*/

async getLauncherAbilityList(): Promise< AppItemInfo[] > {

Logger.info(TAG, 'getLauncherAbilityList begin')

let abilityList = await launcherBundleManager.getAllLauncherAbilityInfo(this.mUserId)

const appItemInfoList = new Array< AppItemInfo >()

if (CheckEmptyUtils.isEmpty(abilityList)) {

  Logger.info(TAG, 'getLauncherAbilityList Empty')

  return appItemInfoList

}

for (let i = 0; i < abilityList.length; i++) {

  let appItem = await this.transToAppItemInfo(abilityList[i])

  appItemInfoList.push(appItem)

}

return appItemInfoList

}

/**

  • get AppItemInfo from BMS with bundleName

  • @params bundleName

  • @return AppItemInfo

*/

async getAppInfoByBundleName(bundleName: string): Promise< AppItemInfo | undefined > {

let appItemInfo: AppItemInfo | undefined = undefined

// get from cache

if (this.mAppMap != null && this.mAppMap.has(bundleName)) {

  appItemInfo = this.mAppMap.get(bundleName)

}

if (appItemInfo != undefined) {

  Logger.info(TAG, `getAppInfoByBundleName from cache: ${JSON.stringify(appItemInfo)}`)

  return appItemInfo

}

// get from system

let abilityInfos = await launcherBundleManager.getLauncherAbilityInfo(bundleName, this.mUserId)

if (abilityInfos == undefined || abilityInfos.length == 0) {

  Logger.info(TAG, `${bundleName} has no launcher ability`)

  return undefined

}

let appInfo = abilityInfos[0]

const data = await this.transToAppItemInfo(appInfo)

Logger.info(TAG, `getAppInfoByBundleName from BMS: ${JSON.stringify(data)}`)

return data

}

private async transToAppItemInfo(info): Promise< AppItemInfo > {

const appItemInfo = new AppItemInfo()

appItemInfo.appName = await this.mResourceManager.getAppNameSync(

info.labelId, info.elementName.bundleName, info.applicationInfo.label

)

appItemInfo.isSystemApp = info.applicationInfo.systemApp

appItemInfo.isUninstallAble = info.applicationInfo.removable

appItemInfo.appIconId = info.iconId

appItemInfo.appLabelId = info.labelId

appItemInfo.bundleName = info.elementName.bundleName

appItemInfo.abilityName = info.elementName.abilityName

await this.mResourceManager.updateIconCache(appItemInfo.appIconId, appItemInfo.bundleName)

this.mAppMap.set(appItemInfo.bundleName, appItemInfo)

return appItemInfo

}

/**

  • 启动应用

  • @params paramAbilityName Ability名

  • @params paramBundleName 应用包名

*/

startLauncherAbility(paramAbilityName, paramBundleName) {

Logger.info(TAG, `startApplication abilityName: ${paramAbilityName}, bundleName: ${paramBundleName}`)

this.context.startAbility({

  bundleName: paramBundleName,

  abilityName: paramAbilityName

}).then(() = > {

  Logger.info(TAG, 'startApplication promise success')

}, (err) = > {

  Logger.error(TAG, `startApplication promise error: ${JSON.stringify(err)}`)

})

}

/**

  • 通过桌面图标启动应用

  • @params paramAbilityName Ability名

  • @params paramBundleName 应用包名

*/

startLauncherAbilityFromRecent(paramAbilityName, paramBundleName): void {

Logger.info(TAG, `startApplication abilityName: ${paramAbilityName}, bundleName: ${paramBundleName}`);

this.context.startRecentAbility({

  bundleName: paramBundleName,

  abilityName: paramAbilityName

}).then(() = > {

  Logger.info(TAG, 'startApplication promise success');

}, (err) = > {

  Logger.error(TAG, `startApplication promise error: ${JSON.stringify(err)}`);

});

}

/**

  • 卸载应用

  • @params bundleName 应用包名

  • @params callback 卸载回调

*/

async uninstallLauncherAbility(bundleName: string, callback): Promise< void > {

Logger.info(TAG, `uninstallLauncherAbility bundleName: ${bundleName}`);

const bundlerInstaller = await installer.getBundleInstaller();

bundlerInstaller.uninstall(bundleName, {

  userId: this.mUserId,

  installFlag: 0,

  isKeepData: false

}, (err: BusinessError) = > {

  Logger.info(TAG, `uninstallLauncherAbility result = > ${JSON.stringify(err)}`);

  callback(err);

})

}

/**

  • 开始监听系统应用状态.

  • @params listener 监听对象

*/

registerLauncherAbilityChangeListener(listener: any): void {

if (!CheckEmptyUtils.isEmpty(listener)) {

  if (this.mLauncherAbilityChangeListeners.length == 0) {

    bundleMonitor.on(LauncherAbilityManager.BUNDLE_STATUS_CHANGE_KEY_ADD, (bundleChangeInfo) = > {

      Logger.debug(TAG, `mBundleStatusCallback add bundleName: ${bundleChangeInfo.bundleName},

        userId: ${bundleChangeInfo.userId}, mUserId ${this.mUserId}`)

      if (this.mUserId === bundleChangeInfo.userId) {

        this.notifyLauncherAbilityChange(EventConstants.EVENT_PACKAGE_ADDED,

          bundleChangeInfo.bundleName, bundleChangeInfo.userId)

      }

    })

    bundleMonitor.on(LauncherAbilityManager.BUNDLE_STATUS_CHANGE_KEY_REMOVE, (bundleChangeInfo) = > {

      Logger.debug(TAG, `mBundleStatusCallback remove bundleName: ${bundleChangeInfo.bundleName},

        userId: ${bundleChangeInfo.userId}, mUserId ${this.mUserId}`)

      if (this.mUserId === bundleChangeInfo.userId) {

        this.notifyLauncherAbilityChange(EventConstants.EVENT_PACKAGE_REMOVED,

          bundleChangeInfo.bundleName, bundleChangeInfo.userId)

      }

      AppStorage.Set('isRefresh', true)

    })

  }

  const index = this.mLauncherAbilityChangeListeners.indexOf(listener)

  if (index == CommonConstants.INVALID_VALUE) {

    this.mLauncherAbilityChangeListeners.push(listener)

  }

}

}

/**

  • 取消监听系统应用状态.

  • @params listener 监听对象

*/

unregisterLauncherAbilityChangeListener(listener: any): void {

if (!CheckEmptyUtils.isEmpty(listener)) {

  const index = this.mLauncherAbilityChangeListeners.indexOf(listener)

  if (index != CommonConstants.INVALID_VALUE) {

    this.mLauncherAbilityChangeListeners.splice(index, 1)

  }

  if (this.mLauncherAbilityChangeListeners.length == 0) {

    bundleMonitor.off(LauncherAbilityManager.BUNDLE_STATUS_CHANGE_KEY_ADD)

    bundleMonitor.off(LauncherAbilityManager.BUNDLE_STATUS_CHANGE_KEY_REMOVE)

  }

}

}

private notifyLauncherAbilityChange(event: string, bundleName: string, userId: number): void {

for (let index = 0; index < this.mLauncherAbilityChangeListeners.length; index++) {

  this.mLauncherAbilityChangeListeners[index](event, bundleName, userId)

}

}

}


* 接口参考:[@ohos.bundle.launcherBundleManager]
* 应用卸载功能模块
* 使用bundle模块的getBundleInstaller接口获取到BundleInstaller(系统能力:SystemCapability.BundleManager.BundleFramework),调用uninstall接口实现应用卸载功能。
* 源码链接:[LauncherAbilityManager.ts]

/*

  • Copyright (c) 2022-2023 Huawei Device Co., Ltd.

  • Licensed under the Apache License, Version 2.0 (the "License");

  • you may not use this file except in compliance with the License.

  • You may obtain a copy of the License at

  • http://www.apache.org/licenses/LICENSE-2.0
    
  • Unless required by applicable law or agreed to in writing, software

  • distributed under the License is distributed on an "AS IS" BASIS,

  • WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

  • See the License for the specific language governing permissions and

  • limitations under the License.

*/

import installer from '@ohos.bundle.installer';

import launcherBundleManager from '@ohos.bundle.launcherBundleManager';

import bundleMonitor from '@ohos.bundle.bundleMonitor';

import osAccount from '@ohos.account.osAccount'

import { AppItemInfo } from '../bean/AppItemInfo'

import { CheckEmptyUtils } from '../utils/CheckEmptyUtils'

import { CommonConstants } from '../constants/CommonConstants'

import { EventConstants } from '../constants/EventConstants'

import { ResourceManager } from './ResourceManager'

import { Logger } from '../utils/Logger'

import type { BusinessError } from '@ohos.base';

const TAG: string = 'LauncherAbilityManager'

/**

  • Wrapper class for innerBundleManager and formManager interfaces.

*/

export class LauncherAbilityManager {

private static readonly BUNDLE_STATUS_CHANGE_KEY_REMOVE = 'remove'

private static readonly BUNDLE_STATUS_CHANGE_KEY_ADD = 'add'

private static launcherAbilityManager: LauncherAbilityManager = undefined

private readonly mAppMap = new Map< string, AppItemInfo >()

private mResourceManager: ResourceManager = undefined

private readonly mLauncherAbilityChangeListeners: any[] = []

private mUserId: number = 100

private context: any = undefined

constructor(context) {

this.context = context

this.mResourceManager = ResourceManager.getInstance(context)

const osAccountManager = osAccount.getAccountManager()

osAccountManager.getOsAccountLocalIdFromProcess((err, localId) = > {

  Logger.debug(TAG, `getOsAccountLocalIdFromProcess localId ${localId}`)

  this.mUserId = localId

})

}

/**

  • Get the application data model object.

  • @return {object} application data model singleton

*/

static getInstance(context): LauncherAbilityManager {

if (this.launcherAbilityManager === null || this.launcherAbilityManager === undefined) {

  this.launcherAbilityManager = new LauncherAbilityManager(context)

}

return this.launcherAbilityManager

}

/**

  • get all app List info from BMS

  • @return 应用的入口Ability信息列表

*/

async getLauncherAbilityList(): Promise< AppItemInfo[] > {

Logger.info(TAG, 'getLauncherAbilityList begin')

let abilityList = await launcherBundleManager.getAllLauncherAbilityInfo(this.mUserId)

const appItemInfoList = new Array< AppItemInfo >()

if (CheckEmptyUtils.isEmpty(abilityList)) {

  Logger.info(TAG, 'getLauncherAbilityList Empty')

  return appItemInfoList

}

for (let i = 0; i < abilityList.length; i++) {

  let appItem = await this.transToAppItemInfo(abilityList[i])

  appItemInfoList.push(appItem)

}

return appItemInfoList

}

/**

  • get AppItemInfo from BMS with bundleName

  • @params bundleName

  • @return AppItemInfo

*/

async getAppInfoByBundleName(bundleName: string): Promise< AppItemInfo | undefined > {

let appItemInfo: AppItemInfo | undefined = undefined

// get from cache

if (this.mAppMap != null && this.mAppMap.has(bundleName)) {

  appItemInfo = this.mAppMap.get(bundleName)

}

if (appItemInfo != undefined) {

  Logger.info(TAG, `getAppInfoByBundleName from cache: ${JSON.stringify(appItemInfo)}`)

  return appItemInfo

}

// get from system

let abilityInfos = await launcherBundleManager.getLauncherAbilityInfo(bundleName, this.mUserId)

if (abilityInfos == undefined || abilityInfos.length == 0) {

  Logger.info(TAG, `${bundleName} has no launcher ability`)

  return undefined

}

let appInfo = abilityInfos[0]

const data = await this.transToAppItemInfo(appInfo)

Logger.info(TAG, `getAppInfoByBundleName from BMS: ${JSON.stringify(data)}`)

return data

}

private async transToAppItemInfo(info): Promise< AppItemInfo > {

const appItemInfo = new AppItemInfo()

appItemInfo.appName = await this.mResourceManager.getAppNameSync(

info.labelId, info.elementName.bundleName, info.applicationInfo.label

)

appItemInfo.isSystemApp = info.applicationInfo.systemApp

appItemInfo.isUninstallAble = info.applicationInfo.removable

appItemInfo.appIconId = info.iconId

appItemInfo.appLabelId = info.labelId

appItemInfo.bundleName = info.elementName.bundleName

appItemInfo.abilityName = info.elementName.abilityName

await this.mResourceManager.updateIconCache(appItemInfo.appIconId, appItemInfo.bundleName)

this.mAppMap.set(appItemInfo.bundleName, appItemInfo)

return appItemInfo

}

/**

  • 启动应用

  • @params paramAbilityName Ability名

  • @params paramBundleName 应用包名

*/

startLauncherAbility(paramAbilityName, paramBundleName) {

Logger.info(TAG, `startApplication abilityName: ${paramAbilityName}, bundleName: ${paramBundleName}`)

this.context.startAbility({

  bundleName: paramBundleName,

  abilityName: paramAbilityName

}).then(() = > {

  Logger.info(TAG, 'startApplication promise success')

}, (err) = > {

  Logger.error(TAG, `startApplication promise error: ${JSON.stringify(err)}`)

})

}

/**

  • 通过桌面图标启动应用

  • @params paramAbilityName Ability名

  • @params paramBundleName 应用包名

*/

startLauncherAbilityFromRecent(paramAbilityName, paramBundleName): void {

Logger.info(TAG, `startApplication abilityName: ${paramAbilityName}, bundleName: ${paramBundleName}`);

this.context.startRecentAbility({

  bundleName: paramBundleName,

  abilityName: paramAbilityName

}).then(() = > {

  Logger.info(TAG, 'startApplication promise success');

}, (err) = > {

  Logger.error(TAG, `startApplication promise error: ${JSON.stringify(err)}`);

});

}

/**

  • 卸载应用

  • @params bundleName 应用包名

  • @params callback 卸载回调

*/

async uninstallLauncherAbility(bundleName: string, callback): Promise< void > {

Logger.info(TAG, `uninstallLauncherAbility bundleName: ${bundleName}`);

const bundlerInstaller = await installer.getBundleInstaller();

bundlerInstaller.uninstall(bundleName, {

  userId: this.mUserId,

  installFlag: 0,

  isKeepData: false

}, (err: BusinessError) = > {

  Logger.info(TAG, `uninstallLauncherAbility result = > ${JSON.stringify(err)}`);

  callback(err);

})

}

/**

  • 开始监听系统应用状态.

  • @params listener 监听对象

*/

registerLauncherAbilityChangeListener(listener: any): void {

if (!CheckEmptyUtils.isEmpty(listener)) {

  if (this.mLauncherAbilityChangeListeners.length == 0) {

    bundleMonitor.on(LauncherAbilityManager.BUNDLE_STATUS_CHANGE_KEY_ADD, (bundleChangeInfo) = > {

      Logger.debug(TAG, `mBundleStatusCallback add bundleName: ${bundleChangeInfo.bundleName},

        userId: ${bundleChangeInfo.userId}, mUserId ${this.mUserId}`)

      if (this.mUserId === bundleChangeInfo.userId) {

        this.notifyLauncherAbilityChange(EventConstants.EVENT_PACKAGE_ADDED,

          bundleChangeInfo.bundleName, bundleChangeInfo.userId)

      }

    })

    bundleMonitor.on(LauncherAbilityManager.BUNDLE_STATUS_CHANGE_KEY_REMOVE, (bundleChangeInfo) = > {

      Logger.debug(TAG, `mBundleStatusCallback remove bundleName: ${bundleChangeInfo.bundleName},

        userId: ${bundleChangeInfo.userId}, mUserId ${this.mUserId}`)

      if (this.mUserId === bundleChangeInfo.userId) {

        this.notifyLauncherAbilityChange(EventConstants.EVENT_PACKAGE_REMOVED,

          bundleChangeInfo.bundleName, bundleChangeInfo.userId)

      }

      AppStorage.Set('isRefresh', true)

    })

  }

  const index = this.mLauncherAbilityChangeListeners.indexOf(listener)

  if (index == CommonConstants.INVALID_VALUE) {

    this.mLauncherAbilityChangeListeners.push(listener)

  }

}

}

/**

  • 取消监听系统应用状态.

  • @params listener 监听对象

*/

unregisterLauncherAbilityChangeListener(listener: any): void {

if (!CheckEmptyUtils.isEmpty(listener)) {

  const index = this.mLauncherAbilityChangeListeners.indexOf(listener)

  if (index != CommonConstants.INVALID_VALUE) {

    this.mLauncherAbilityChangeListeners.splice(index, 1)

  }

  if (this.mLauncherAbilityChangeListeners.length == 0) {

    bundleMonitor.off(LauncherAbilityManager.BUNDLE_STATUS_CHANGE_KEY_ADD)

    bundleMonitor.off(LauncherAbilityManager.BUNDLE_STATUS_CHANGE_KEY_REMOVE)

  }

}

}

private notifyLauncherAbilityChange(event: string, bundleName: string, userId: number): void {

for (let index = 0; index < this.mLauncherAbilityChangeListeners.length; index++) {

  this.mLauncherAbilityChangeListeners[index](event, bundleName, userId)

}

}

}


* 接口参考:[@ohos.bundle]
* 添加卡片功能模块
* 使用formHost接口(系统能力:SystemCapability.Ability.Form),获取应用卡片信息,使用FormComponent组件展示卡片内容,从而实现添加卡片到桌面的功能。
* 源码链接:[FormManager.ts]

/*

  • Copyright (c) 2022 Huawei Device Co., Ltd.

  • Licensed under the Apache License, Version 2.0 (the "License");

  • you may not use this file except in compliance with the License.

  • You may obtain a copy of the License at

  • http://www.apache.org/licenses/LICENSE-2.0
    
  • Unless required by applicable law or agreed to in writing, software

  • distributed under the License is distributed on an "AS IS" BASIS,

  • WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

  • See the License for the specific language governing permissions and

  • limitations under the License.

*/

import formManagerAbility from '@ohos.app.form.formHost'

import { CardItemInfo } from '../bean/CardItemInfo'

import { CommonConstants } from '../constants/CommonConstants'

import { Logger } from '../utils/Logger'

const TAG: string = 'FormManager'

/**

  • Wrapper class for formManager interfaces.

*/

class FormManagerModel {

private readonly CARD_SIZE_1x2: number[] = [1, 2]

private readonly CARD_SIZE_2x2: number[] = [2, 2]

private readonly CARD_SIZE_2x4: number[] = [2, 4]

private readonly CARD_SIZE_4x4: number[] = [4, 4]

/**

  • get all form info

  • @return Array< CardItemInfo > cardItemInfoList

*/

public async getAllFormsInfo(): Promise< CardItemInfo[] > {

const formList = await formManagerAbility.getAllFormsInfo()

const cardItemInfoList = new Array< CardItemInfo >()

for (const formItem of formList) {

  const cardItemInfo = new CardItemInfo()

  cardItemInfo.bundleName = formItem.bundleName

  cardItemInfo.abilityName = formItem.abilityName

  cardItemInfo.moduleName = formItem.moduleName

  cardItemInfo.cardName = formItem.name

  cardItemInfo.cardDimension = formItem.defaultDimension

  cardItemInfo.description = formItem.description

  cardItemInfo.formConfigAbility = formItem.formConfigAbility

  cardItemInfo.supportDimensions = formItem.supportDimensions

  cardItemInfo.area = this.getCardSize(cardItemInfo.cardDimension)

  cardItemInfoList.push(cardItemInfo)

}

return cardItemInfoList

}

/**

  • get card area by dimension

  • @param dimension

  • @return number[]

*/

public getCardSize(dimension: number): number[] {

if (dimension == CommonConstants.CARD_DIMENSION_1x2) {

  return this.CARD_SIZE_1x2

} else if (dimension == CommonConstants.CARD_DIMENSION_2x2) {

  return this.CARD_SIZE_2x2

} else if (dimension == CommonConstants.CARD_DIMENSION_2x4) {

  return this.CARD_SIZE_2x4

} else {

  return this.CARD_SIZE_4x4

}

}

/**

  • get card dimension bty area

  • @param dimension

  • @return number[]

*/

public getCardDimension(area: number[]) {

if (area.toString() === this.CARD_SIZE_1x2.toString()) {

  return CommonConstants.CARD_DIMENSION_1x2

} else if (area.toString() === this.CARD_SIZE_2x2.toString()) {

  return CommonConstants.CARD_DIMENSION_2x2

} else if (area.toString() == this.CARD_SIZE_2x4.toString()) {

  return CommonConstants.CARD_DIMENSION_2x4

} else {

  return CommonConstants.CARD_DIMENSION_4x4

}

}

/**

  • get form info by bundleName

  • @param bundle

  • @return Array< CardItemInfo > cardItemInfoList

*/

public async getFormsInfoByApp(bundle: string): Promise< CardItemInfo[] > {

Logger.info(TAG, `getFormsInfoByApp bundle: ${bundle}`)

const formList = await formManagerAbility.getFormsInfo(bundle)

const cardItemInfoList = new Array< CardItemInfo >()

for (const formItem of formList) {

  const cardItemInfo = new CardItemInfo()

  cardItemInfo.bundleName = formItem.bundleName

  cardItemInfo.abilityName = formItem.abilityName

  cardItemInfo.moduleName = formItem.moduleName

  cardItemInfo.cardName = formItem.name

  cardItemInfo.cardDimension = formItem.defaultDimension

  cardItemInfo.area = this.getCardSize(cardItemInfo.cardDimension)

  cardItemInfo.description = formItem.description

  cardItemInfo.formConfigAbility = formItem.formConfigAbility

  cardItemInfo.supportDimensions = formItem.supportDimensions

  cardItemInfoList.push(cardItemInfo)

}

return cardItemInfoList

}

}

export let FormManager = new FormManagerModel()


* 接口参考:[@ohos.app.form.formHost]
* 桌面数据持久化存储功能模块
  * 使用关系型数据库rdb接口(系统能力:SystemCapability.DistributedDataManager.RelationalStore.Core),实现桌面数据持久化存储,存储应用的位置信息,卡片信息。
* 源码链接:[RdbManager.ts]

/*

  • Copyright (c) 2022-2023 Huawei Device Co., Ltd.

  • Licensed under the Apache License, Version 2.0 (the "License");

  • you may not use this file except in compliance with the License.

  • You may obtain a copy of the License at

  • http://www.apache.org/licenses/LICENSE-2.0
    
  • Unless required by applicable law or agreed to in writing, software

  • distributed under the License is distributed on an "AS IS" BASIS,

  • WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

  • See the License for the specific language governing permissions and

  • limitations under the License.

*/

import dataRdb from '@ohos.data.relationalStore'

import { CheckEmptyUtils } from '../utils/CheckEmptyUtils'

import { CommonConstants } from '../constants/CommonConstants'

import { GridLayoutItemInfo } from '../bean/GridLayoutItemInfo'

import { GridLayoutInfoColumns } from '../bean/GridLayoutInfoColumns'

import { Logger } from '../utils/Logger'

export const TABLE_NAME: string = 'launcher'

export const SQL_CREATE_TABLE = 'CREATE TABLE IF NOT EXISTS launcher ' +

'(id INTEGER PRIMARY KEY AUTOINCREMENT, ' +

'app_name TEXT, ' +

'appIcon_id INTEGER, ' +

'container INTEGER, ' +

'type_id INTEGER, ' +

'card_id INTEGER, ' +

'card_name TEXT, ' +

'badge_number INTEGER, ' +

'module_name TEXT, ' +

'bundle_name TEXT, ' +

'ability_name TEXT, ' +

'area TEXT, ' +

'page INTEGER, ' +

'column INTEGER, ' +

'row INTEGER)'

export const STORE_CONFIG = { name: 'launcher.db', securityLevel: dataRdb.SecurityLevel.S1 }

const TAG: string = 'RdbModel'

class RdbManagerModel {

private mRdbStore: dataRdb.RdbStore = undefined

constructor() {

}

/**

  • initRdbConfig

  • @param context

*/

async initRdbConfig(context): Promise< void > {

Logger.info(TAG, 'initRdbConfig start')

if (this.mRdbStore === undefined) {

  this.mRdbStore = await dataRdb.getRdbStore(context, STORE_CONFIG);

  await this.mRdbStore.executeSql(SQL_CREATE_TABLE);

  Logger.info(TAG, 'create table end');

}

}

/**

  • deleteTable

  • @param tableName

*/

async deleteTable(tableName: string): Promise< void > {

Logger.info(TAG, 'deleteTable start')

try {

  let detelSql = `DELETE FROM ${tableName};`

  let detelSequenceSql = `UPDATE sqlite_sequence SET seq=0 WHERE name = '${tableName}';`

  await this.mRdbStore.executeSql(detelSql)

  await this.mRdbStore.executeSql(detelSequenceSql)

  Logger.debug(TAG, 'deleteTable end')

} catch (e) {

  Logger.error(TAG, `deleteTable err: ${e}`)

}

}

/**

  • insertData

  • @param layoutInfo

*/

async insertData(layoutInfo: any) {

Logger.info(TAG, 'insertGridLayoutInfo start');

let result: boolean = true

if (CheckEmptyUtils.isEmpty(layoutInfo)) {

  Logger.error(TAG, 'insertGridLayoutInfo gridlayoutinfo is empty')

  result = false

  return result

}

try {

  // delete gridlayoutinfo table

  await this.deleteTable(TABLE_NAME)

  // insert into gridlayoutinfo

  for (let i in layoutInfo) {

    let layout = layoutInfo[i]

    for (let j in layout) {

      let element = layout[j]

      Logger.info(TAG, `insertGridLayoutInfo i= ${i}`)

      let item = {}

      if (element.typeId === CommonConstants.TYPE_APP) {

        item = {

          'app_name': element.appName,

          'bundle_name': element.bundleName,

          'module_name': element.modelName,

          'ability_name': element.abilityName,

          'appIcon_id': element.appIconId,

          'type_id': element.typeId,

          'area': element.area[0] + ',' + element.area[1],

          'page': element.page,

          'column': element.column,

          'row': element.row,

          'container': -100

        }

        let ret = await this.mRdbStore.insert(TABLE_NAME, item)

        Logger.debug(TAG, `insertGridLayoutInfo type is app ${i} ret: ${ret}`)

      } else if (element.typeId === CommonConstants.TYPE_CARD) {

        item = {

          'app_name': element.appName,

          'bundle_name': element.bundleName,

          'module_name': element.modelName,

          'ability_name': element.abilityName,

          'card_id': element.cardId,

          'card_name': element.cardName,

          'type_id': element.typeId,

          'area': element.area[0] + ',' + element.area[1],

          'page': element.page,

          'column': element.column,

          'row': element.row,

          'container': -100

        }

        let ret = await this.mRdbStore.insert(TABLE_NAME, item)

        Logger.debug(TAG, `insertGridLayoutInfo type is card ${i} ret: ${ret}`)

      }

    }

  }

} catch (e) {

  Logger.error(TAG, `insertGridLayoutInfo error: ${e}`)

}

return result

}

async queryLayoutInfo() {

Logger.info(TAG, 'queryLayoutInfo start')

const resultList: GridLayoutItemInfo[] = []

const predicates = new dataRdb.RdbPredicates(TABLE_NAME)

predicates.equalTo(GridLayoutInfoColumns.CONTAINER, -100)

  .and().orderByAsc('page').and().orderByAsc('row').and().orderByAsc('column')

let resultSet = await this.mRdbStore.query(predicates)

Logger.info(TAG, `queryLayoutInfo query,count=${resultSet.rowCount}`)

let isLast = resultSet.goToFirstRow()

while (isLast) {

  const layoutInfo: GridLayoutItemInfo = GridLayoutItemInfo.fromResultSet(resultSet)

  resultList.push(layoutInfo)

  isLast = resultSet.goToNextRow()

}

resultSet.close()

resultSet = null

return resultList

}

async insertItem(item: GridLayoutItemInfo) {

if (CheckEmptyUtils.isEmpty(item)) {

  return

}

let element = {

  'app_name': item.appName,

  'module_name': item.moduleName,

  'bundle_name': item.bundleName,

  'ability_name': item.abilityName,

  'appIcon_id': item.appIconId,

  'card_id': item.cardId,

  'card_name': item.cardName,

  'type_id': item.typeId,

  'area': item.area[0] + ',' + item.area[1],

  'page': item.page,

  'column': item.column,

  'row': item.row,

  'container': -100

}

let ret = await this.mRdbStore.insert(TABLE_NAME, element)

Logger.debug(TAG, `insertGridLayoutInfo ret: ${ret}`)

}

async deleteItemByPosition(page: number, row: number, column: number) {

const predicates = new dataRdb.RdbPredicates(TABLE_NAME);

predicates.equalTo('page', page)

  .and().equalTo('row', row)

  .and().equalTo('column', column);

let query = await this.mRdbStore.query(predicates);

if (query.rowCount > 0) {

  let ret = await this.mRdbStore.delete(predicates);

  Logger.debug(TAG, `deleteItem ret: ${ret}`);

}

}

}

export let RdbManager = new RdbManagerModel()


* 接口参考:[@ohos.data.relationalStore]
* 加锁、解锁、清理后台任务功能模块
  * 使用missionManager模块接口(系统能力:SystemCapability.Ability.AbilityRuntime.Mission),获取最近任务信息,并实现加锁、解锁、清理后台任务的功能。
  * 源码链接:[MissionModel.ts]
  * 接口参考:[@ohos.application.missionManager]
* 点击桌面应用拉起最近任务至前台功能模块
  * 使用ServiceExtensionContext模块的startRecentAbility接口(系统能力:SystemCapability.Ability.AbilityRuntime.Core),拉起最近任务至前台显示,若应用Ability未启动时,则拉起新创建的应用Ability显示到前台。
    源码链接:[LauncherAbilityManager.ts]

/*

  • Copyright (c) 2022-2023 Huawei Device Co., Ltd.

  • Licensed under the Apache License, Version 2.0 (the "License");

  • you may not use this file except in compliance with the License.

  • You may obtain a copy of the License at

  • http://www.apache.org/licenses/LICENSE-2.0
    
  • Unless required by applicable law or agreed to in writing, software

  • distributed under the License is distributed on an "AS IS" BASIS,

  • WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

  • See the License for the specific language governing permissions and

  • limitations under the License.

*/

import installer from '@ohos.bundle.installer';

import launcherBundleManager from '@ohos.bundle.launcherBundleManager';

import bundleMonitor from '@ohos.bundle.bundleMonitor';

import osAccount from '@ohos.account.osAccount'

import { AppItemInfo } from '../bean/AppItemInfo'

import { CheckEmptyUtils } from '../utils/CheckEmptyUtils'

import { CommonConstants } from '../constants/CommonConstants'

import { EventConstants } from '../constants/EventConstants'

import { ResourceManager } from './ResourceManager'

import { Logger } from '../utils/Logger'

import type { BusinessError } from '@ohos.base';

const TAG: string = 'LauncherAbilityManager'

/**

  • Wrapper class for innerBundleManager and formManager interfaces.

*/

export class LauncherAbilityManager {

private static readonly BUNDLE_STATUS_CHANGE_KEY_REMOVE = 'remove'

private static readonly BUNDLE_STATUS_CHANGE_KEY_ADD = 'add'

private static launcherAbilityManager: LauncherAbilityManager = undefined

private readonly mAppMap = new Map< string, AppItemInfo >()

private mResourceManager: ResourceManager = undefined

private readonly mLauncherAbilityChangeListeners: any[] = []

private mUserId: number = 100

private context: any = undefined

constructor(context) {

this.context = context

this.mResourceManager = ResourceManager.getInstance(context)

const osAccountManager = osAccount.getAccountManager()

osAccountManager.getOsAccountLocalIdFromProcess((err, localId) = > {

  Logger.debug(TAG, `getOsAccountLocalIdFromProcess localId ${localId}`)

  this.mUserId = localId

})

}

/**

  • Get the application data model object.

  • @return {object} application data model singleton

*/

static getInstance(context): LauncherAbilityManager {

if (this.launcherAbilityManager === null || this.launcherAbilityManager === undefined) {

  this.launcherAbilityManager = new LauncherAbilityManager(context)

}

return this.launcherAbilityManager

}

/**

  • get all app List info from BMS

  • @return 应用的入口Ability信息列表

*/

async getLauncherAbilityList(): Promise< AppItemInfo[] > {

Logger.info(TAG, 'getLauncherAbilityList begin')

let abilityList = await launcherBundleManager.getAllLauncherAbilityInfo(this.mUserId)

const appItemInfoList = new Array< AppItemInfo >()

if (CheckEmptyUtils.isEmpty(abilityList)) {

  Logger.info(TAG, 'getLauncherAbilityList Empty')

  return appItemInfoList

}

for (let i = 0; i < abilityList.length; i++) {

  let appItem = await this.transToAppItemInfo(abilityList[i])

  appItemInfoList.push(appItem)

}

return appItemInfoList

}

/**

  • get AppItemInfo from BMS with bundleName

  • @params bundleName

  • @return AppItemInfo

*/

async getAppInfoByBundleName(bundleName: string): Promise< AppItemInfo | undefined > {

let appItemInfo: AppItemInfo | undefined = undefined

// get from cache

if (this.mAppMap != null && this.mAppMap.has(bundleName)) {

  appItemInfo = this.mAppMap.get(bundleName)

}

if (appItemInfo != undefined) {

  Logger.info(TAG, `getAppInfoByBundleName from cache: ${JSON.stringify(appItemInfo)}`)

  return appItemInfo

}

// get from system

let abilityInfos = await launcherBundleManager.getLauncherAbilityInfo(bundleName, this.mUserId)

if (abilityInfos == undefined || abilityInfos.length == 0) {

  Logger.info(TAG, `${bundleName} has no launcher ability`)

  return undefined

}

let appInfo = abilityInfos[0]

const data = await this.transToAppItemInfo(appInfo)

Logger.info(TAG, `getAppInfoByBundleName from BMS: ${JSON.stringify(data)}`)

return data

}

private async transToAppItemInfo(info): Promise< AppItemInfo > {

const appItemInfo = new AppItemInfo()

appItemInfo.appName = await this.mResourceManager.getAppNameSync(

info.labelId, info.elementName.bundleName, info.applicationInfo.label

)

appItemInfo.isSystemApp = info.applicationInfo.systemApp

appItemInfo.isUninstallAble = info.applicationInfo.removable

appItemInfo.appIconId = info.iconId

appItemInfo.appLabelId = info.labelId

appItemInfo.bundleName = info.elementName.bundleName

appItemInfo.abilityName = info.elementName.abilityName

await this.mResourceManager.updateIconCache(appItemInfo.appIconId, appItemInfo.bundleName)

this.mAppMap.set(appItemInfo.bundleName, appItemInfo)

return appItemInfo

}

/**

  • 启动应用

  • @params paramAbilityName Ability名

  • @params paramBundleName 应用包名

*/

startLauncherAbility(paramAbilityName, paramBundleName) {

Logger.info(TAG, `startApplication abilityName: ${paramAbilityName}, bundleName: ${paramBundleName}`)

this.context.startAbility({

  bundleName: paramBundleName,

  abilityName: paramAbilityName

}).then(() = > {

  Logger.info(TAG, 'startApplication promise success')

}, (err) = > {

  Logger.error(TAG, `startApplication promise error: ${JSON.stringify(err)}`)

})

}

/**

  • 通过桌面图标启动应用

  • @params paramAbilityName Ability名

  • @params paramBundleName 应用包名

*/

startLauncherAbilityFromRecent(paramAbilityName, paramBundleName): void {

Logger.info(TAG, `startApplication abilityName: ${paramAbilityName}, bundleName: ${paramBundleName}`);

this.context.startRecentAbility({

  bundleName: paramBundleName,

  abilityName: paramAbilityName

}).then(() = > {

  Logger.info(TAG, 'startApplication promise success');

}, (err) = > {

  Logger.error(TAG, `startApplication promise error: ${JSON.stringify(err)}`);

});

}

/**

  • 卸载应用

  • @params bundleName 应用包名

  • @params callback 卸载回调

*/

async uninstallLauncherAbility(bundleName: string, callback): Promise< void > {

Logger.info(TAG, `uninstallLauncherAbility bundleName: ${bundleName}`);

const bundlerInstaller = await installer.getBundleInstaller();

bundlerInstaller.uninstall(bundleName, {

  userId: this.mUserId,

  installFlag: 0,

  isKeepData: false

}, (err: BusinessError) = > {

  Logger.info(TAG, `uninstallLauncherAbility result = > ${JSON.stringify(err)}`);

  callback(err);

})

}

/**

  • 开始监听系统应用状态.

  • @params listener 监听对象

*/

registerLauncherAbilityChangeListener(listener: any): void {

if (!CheckEmptyUtils.isEmpty(listener)) {

  if (this.mLauncherAbilityChangeListeners.length == 0) {

    bundleMonitor.on(LauncherAbilityManager.BUNDLE_STATUS_CHANGE_KEY_ADD, (bundleChangeInfo) = > {

      Logger.debug(TAG, `mBundleStatusCallback add bundleName: ${bundleChangeInfo.bundleName},

        userId: ${bundleChangeInfo.userId}, mUserId ${this.mUserId}`)

      if (this.mUserId === bundleChangeInfo.userId) {

        this.notifyLauncherAbilityChange(EventConstants.EVENT_PACKAGE_ADDED,

          bundleChangeInfo.bundleName, bundleChangeInfo.userId)

      }

    })

    bundleMonitor.on(LauncherAbilityManager.BUNDLE_STATUS_CHANGE_KEY_REMOVE, (bundleChangeInfo) = > {

      Logger.debug(TAG, `mBundleStatusCallback remove bundleName: ${bundleChangeInfo.bundleName},

        userId: ${bundleChangeInfo.userId}, mUserId ${this.mUserId}`)

      if (this.mUserId === bundleChangeInfo.userId) {

        this.notifyLauncherAbilityChange(EventConstants.EVENT_PACKAGE_REMOVED,

          bundleChangeInfo.bundleName, bundleChangeInfo.userId)

      }

      AppStorage.Set('isRefresh', true)

    })

  }

  const index = this.mLauncherAbilityChangeListeners.indexOf(listener)

  if (index == CommonConstants.INVALID_VALUE) {

    this.mLauncherAbilityChangeListeners.push(listener)

  }

}

}

/**

  • 取消监听系统应用状态.

  • @params listener 监听对象

*/

unregisterLauncherAbilityChangeListener(listener: any): void {

if (!CheckEmptyUtils.isEmpty(listener)) {

  const index = this.mLauncherAbilityChangeListeners.indexOf(listener)

  if (index != CommonConstants.INVALID_VALUE) {

    this.mLauncherAbilityChangeListeners.splice(index, 1)

  }

  if (this.mLauncherAbilityChangeListeners.length == 0) {

    bundleMonitor.off(LauncherAbilityManager.BUNDLE_STATUS_CHANGE_KEY_ADD)

    bundleMonitor.off(LauncherAbilityManager.BUNDLE_STATUS_CHANGE_KEY_REMOVE)

  }

}

}

private notifyLauncherAbilityChange(event: string, bundleName: string, userId: number): void {

for (let index = 0; index < this.mLauncherAbilityChangeListeners.length; index++) {

  this.mLauncherAbilityChangeListeners[index](event, bundleName, userId)

}

}

}


接口参考:[@ohos.app.ability.ServiceExtensionAbility]

审核编辑 黄宇

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 鸿蒙
    +关注

    关注

    55

    文章

    1641

    浏览量

    42123
  • OpenHarmony
    +关注

    关注

    23

    文章

    3322

    浏览量

    15161
收藏 人收藏

    评论

    相关推荐

    玩嗨OpenHarmony:基于OpenHarmony的仿生四足狗开发分享

    原文引自51CTO 开源基础软件社区 《刘潇翔:基于OpenHarmony的仿生四足狗开发分享》 作者介绍 刘潇翔, 现就读于南方科技大学(Southern University
    的头像 发表于 10-31 21:50 3157次阅读

    OpenHarmony应用开发安装数学库

    OpenHarmony应用开发安装数学库
    的头像 发表于 12-01 14:15 946次阅读
    <b class='flag-5'>OpenHarmony</b>应用<b class='flag-5'>开发</b>安装数学库

    OpenHarmony南向开发实例:【游戏手柄】

    基于TS扩展的声明式开发范式编程语言,以及OpenHarmony的分布式能力实现的一个手柄游戏。
    的头像 发表于 04-17 10:21 406次阅读
    <b class='flag-5'>OpenHarmony</b>南向<b class='flag-5'>开发</b><b class='flag-5'>实例</b>:【游戏手柄】

    OpenHarmony开发实例:【 待办事项TodoList】

    TodoList应用是基于OpenHarmony SDK开发的安装在润和HiSpark Taurus AI Camera(Hi3516d)开发板标准系统上的应用;应用主要功能是以列表的形式,展示需要完成的日程;通过本demo可以
    的头像 发表于 04-22 22:00 330次阅读
    <b class='flag-5'>OpenHarmony</b><b class='flag-5'>开发</b><b class='flag-5'>实例</b>:【 待办事项TodoList】

    HarmonyOS与OpenHarmony应用开发差异

    DevEco Studio是HarmonyOS的配套的开发IDE,因为HarmonyOS是基于OpenHarmony开发的,因此,使用DevEco Studio(配套HarmonyOS)也可以进行
    发表于 10-22 10:35

    OpenHarmony 应用开发快速入门

    本文档适用于OpenHarmony应用开发的初学者。通过构建一个简单的具有页面跳转/返回功能的应用(如下图所示),快速了解工程目录的主要文件,熟悉OpenHarmony应用开发流程。
    发表于 05-06 16:03

    迅为RK3568开发OpenHarmony v3.2-Beta4 版本测试

    迅为RK3568开发OpenHarmony v3.2-Beta4 版本测试在上个章节,我们在 RK3568 开发板上烧写了 OpenHarmony 镜像,本章节我们来对底板外设进行
    发表于 03-29 15:09

    openharmony开发企业有哪些

    将会在各种智能终端上运用。那么openharmony开发企业有哪些?下面小编就为大家带来openharmony开发企业有哪些的答案。 openhar
    的头像 发表于 06-23 11:13 1858次阅读

    openharmony开发openharmony开发

    现在市面上支持OpenHarmony开发板已经非常多了,OpenHarmony不仅仅只能在海思系列芯片上运行,比较常见的有HiSpark、小熊派系列。这些开发板都是基于海思的Hi38
    的头像 发表于 06-24 09:03 3294次阅读

    使用Python开发OpenHarmony设备程序-I2C应用实例分享

    在上一篇帖子《使用Python开发OpenHarmony设备程序(1-GPIO外设控制)》中,已经成功的使用 Python 对 GPIO 上的外设进行了控制。这是非常重要的一个里
    的头像 发表于 12-07 11:34 3119次阅读
    使用Python<b class='flag-5'>开发</b><b class='flag-5'>OpenHarmony</b>设备程序-I2C应用<b class='flag-5'>实例</b>分享

    OpenHarmony轻量系统开发【1】初始OH

    的。 后续将给大家陆续分享轻量系统开发相关内容 目录: OpenHarmony轻量系统开发【1】初始OpenHarmony OpenHarmony
    发表于 02-25 17:49 805次阅读

    openharmony开源社区 OpenHarmony开发样例上新了

    openharmony开源社区 OpenHarmony开发样例上新了 OpenHarmony 开源项目是由开放原子开源基金会孵化及运营的开源项目,由开放原子开源基金会
    的头像 发表于 04-25 16:37 2119次阅读

    OpenHarmony开发体系内容介绍

    初学OpenHarmony开发者最需要构建对OpenHarmony开发体系的整体了解。
    的头像 发表于 07-04 10:34 1200次阅读

    OpenHarmony轻量系统书籍推荐《OpenHarmony轻量设备开发理论与实战》

    最近大家问的智能家居套件方面有没有可以参考的资料,这里给大家统一回复一下 推荐大家可以看这本书 《OpenHarmony轻量设备开发理论与实战》 本书系统地讲授OpenHarmony 轻量系统 设备
    的头像 发表于 07-20 12:43 670次阅读

    鸿蒙开发实例:【配置OpenHarmony SDK】

    在设置OpenHarmony应用开发环境时,需要开发者在DevEco Studio中配置对应的SDK信息。
    的头像 发表于 04-22 15:24 162次阅读
    鸿蒙<b class='flag-5'>开发</b><b class='flag-5'>实例</b>:【配置<b class='flag-5'>OpenHarmony</b> SDK】