* Copyright (c) Huawei Device Co., Ltd. 2024-2025. All rights reserved. 2024-2025. All rights reserved.
* 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 taskPool from '@ohos.taskpool';
import { CheckEmptyUtils, CommonUtils } from '@ohos/basicutils';
import { CommonEvent, DeviceHelper, IconResourceManager } from '@ohos/frameworkwrapper';
import { UpdateType } from '@ohos/commonconstants';
import {
HiSysEventUtil,
EvtBus,
GlobalContext,
localEventManager,
PackageDataClearedEvent,
DownloadingProgressChangeEvent,
DownloadStatusChangeEvent,
InstallStatusChangeEvent,
ShutDownEvent,
} from '@ohos/frameworkwrapper';
import { LogDomain, LogHelper } from '@ohos/basicutils';
import type { AppItemInfo } from '../bean/AppItemInfo';
import { RdbStoreManager } from '../db/RdbStoreManager';
import { EventConstants } from '../constants/EventConstants';
import {
AppDownloadInfo,
AppGalleryEventInfo,
AppInstallStatusInfo,
BusinessType,
DownloadInfoItem,
DownloadStatusInfo
} from '../constants/CommonConstants';
import { AppGalleryEventStatus, AppStatus, CommonConstants } from '../constants/CommonConstants';
import type ctx from '@ohos.app.ability.common';
import GridLayoutItemInfo from '../bean/GridLayoutItemInfo';
import HashMap from '@ohos.util.HashMap';
import { AppModel } from '../model/AppModel';
import {
DisposedEventManager,
DockItemInfo,
FolderLayoutCacheManager,
LaunchLayoutCacheManager,
LayoutViewModel,
NotHarmonyUtil,
GetHideAppsFromConfig,
ResidentLayoutCacheMgr
} from '../TsIndex';
import { BundleMappingChangeEvent } from '@ohos/frameworkwrapper/src/main/ets/eventbus/events/CommonEvents';
import { preferences } from '@kit.ArkData';
import { AppCategoryInfoManager } from './AppCategoryInfoManager';
import { HashSet } from '@kit.ArkTS';
import AppUpdateUtils from '../utils/AppUpdateUtils';
import { AppInstallUtils } from '../appinstall/AppInstallUtils';
import { ObjectCopyUtil } from '@ohos/componenthelper';
type AppGalleryEvent = AppInstallStatusInfo | DownloadStatusInfo | AppDownloadInfo;
const TAG = 'AppGalleryDownloadManager';
const log: LogHelper = LogHelper.getLogHelper(LogDomain.HOME, TAG);
const FILE_PRE = 'file://';
const APPGALLERY_BUNDLENAME = 'com.ohos.appgallery';
const TYPE_ENTERPRISE = 'enterprise';
const TYPE_INTERNALTESTING = 'internaltesting';
const WAIT_FOR_STRING = '__WAIT_FOR_';
const PRE_DOWNLOAD_MENU_LEAST_NUM = 1;
const SHORTCUT_LIMIT_KEY = 'shortcutLimit';
const THROTTLING_INTERVAL = 500;
export class AppGalleryDownloadManager {
private static sInstance: AppGalleryDownloadManager;
private downloadProgressList: AppDownloadInfo[] = [];
private downloadTaskList: DownloadStatusInfo[] = [];
* 取消下载bundleName集合
*/
private cancelDownloadSet: HashSet<string> = new HashSet<string>();
private installEventAppStatusMap: Map<AppGalleryEventStatus, AppStatus> = new Map<AppGalleryEventStatus, AppStatus>();
private appDownloadMap: HashMap<String, number> = new HashMap<String, number>();
private lastClickBundleName: string = '';
private lastClickTime: number = 0;
* 是否开启下一代下载功能,默认不开启
*/
private enableNextDownload: boolean = false;
* Get instance of AppGalleryDownloadManager
* @returns instance of AppGalleryDownloadManager
*/
public static getInstance(): AppGalleryDownloadManager {
if (!AppGalleryDownloadManager.sInstance) {
AppGalleryDownloadManager.sInstance = new AppGalleryDownloadManager();
}
return AppGalleryDownloadManager.sInstance;
}
constructor() {
}
* 初始化AppGalleryDownloadManager
*/
public init(): void {
log.showWarn('int app install event');
this.initInstallEventAppStatusMap();
this.initAppGalleryEvent();
}
* 获取开启下一代下载功能
* @returns
*/
public isEnableNextDownload(): boolean {
return this.enableNextDownload;
}
* 设置是否开启下一代下载功能
* @param isEnableNextDownload
*/
public setEnableNextDownload(isEnableNextDownload: boolean): void {
this.enableNextDownload = isEnableNextDownload;
}
private initInstallEventAppStatusMap(): void {
this.installEventAppStatusMap.set(AppGalleryEventStatus.DOWNLOAD_CREATE_EVENT_ID, AppStatus.WAITING);
this.installEventAppStatusMap.set(AppGalleryEventStatus.DOWNLOAD_WAIT_EVENT_ID, AppStatus.WAITING);
this.installEventAppStatusMap.set(AppGalleryEventStatus.DOWNLOAD_PAUSE_EVENT_ID, AppStatus.PAUSING);
this.installEventAppStatusMap.set(AppGalleryEventStatus.DOWNLOAD_RESUME_EVENT_ID, AppStatus.DOWNLOADING);
this.installEventAppStatusMap.set(AppGalleryEventStatus.DOWNLOAD_WLAN_EVENT_ID, AppStatus.PAUSING);
this.installEventAppStatusMap.set(AppGalleryEventStatus.DOWNLOAD_SUCCEED_EVENT_ID, AppStatus.INSTALL_WAITING);
this.installEventAppStatusMap.set(AppGalleryEventStatus.INSTALL_START_EVENT_ID, AppStatus.INSTALLING);
this.installEventAppStatusMap.set(AppGalleryEventStatus.COMPANY_APP_RESET_EVENT_ID, AppStatus.PENDING);
this.installEventAppStatusMap.set(AppGalleryEventStatus.INSTALL_FAILED_EVENT_ID, AppStatus.INSTALL_FAIL);
}
private notifyDownloadCanceled(bundleName: string, appIndex: number): void {
AppModel.getInstance().onDownloadCanceled(EventConstants.EVENT_PACKAGE_REMOVED, bundleName, appIndex);
}
private initAppGalleryEvent(): void {
EvtBus.on(DownloadStatusChangeEvent, (event: CommonEvent) => {
this.handleAppGalleryEventChange(event);
});
EvtBus.on(DownloadingProgressChangeEvent, (event: CommonEvent) => {
this.handleAppGalleryEventChange(event);
});
EvtBus.on(InstallStatusChangeEvent, (event: CommonEvent) => {
this.handleAppGalleryEventChange(event);
});
EvtBus.on(ShutDownEvent, (event: CommonEvent) => {
this.changeAllCacheAppToPause();
});
EvtBus.on(PackageDataClearedEvent, (commonEvent: CommonEvent) => {
if (commonEvent.bundleName !== APPGALLERY_BUNDLENAME) {
return;
}
this.removeAllNotInstalledApp();
});
EvtBus.on(BundleMappingChangeEvent, (event: CommonEvent) => {
this.handleBundleMappingEventChange(event);
});
}
private handleAppGalleryEventChange(event: CommonEvent): void {
log.showInfo('handleAppGalleryEventChange: %{public}s bundleName: %{public}s', event?.event, event?.parameters?.bundleName);
if (event?.parameters) {
DisposedEventManager.getInstance().handleAppInstalled(event);
let actionBundleName: string = event?.parameters.bundleName;
this.dealNoImageIconCache(actionBundleName, event?.code);
if ((this.isAppUpdating(actionBundleName) || this.isUpdateStatus(event)) && !this.isDistributeType(event)) {
log.showWarn('handleAppGalleryEventChange %{public}s is updating', actionBundleName);
return;
}
let eventData: AppGalleryEvent;
switch (event?.event) {
case DownloadStatusChangeEvent.DOWNLOAD_STATUS_CHANGE:
eventData = event?.parameters as DownloadStatusInfo;
if (!event.code) {
log.showWarn('event code is empty!!!!');
return;
}
this.dealWithDownloadStatusChange(eventData as DownloadStatusInfo, event.code, actionBundleName);
break;
case DownloadingProgressChangeEvent.DOWNLOAD_PROGRESS_CHANGE:
eventData = event?.parameters as AppDownloadInfo;
this.dealWithDownloadProgressChange(eventData as AppDownloadInfo, actionBundleName);
break;
case InstallStatusChangeEvent.INSTALL_STATUS_CHANGE:
eventData = event?.parameters as AppInstallStatusInfo;
if (!event.code) {
log.showWarn('event code is empty!!!!');
return;
}
this.dealWithInstallStatusChange(eventData as AppInstallStatusInfo, event.code, actionBundleName);
break;
}
}
}
* 无图标应用更新安装完成时,清理图标缓存,通知systemui重新获取图标
*
* @param bundleName 包名
* @param code 事件id
*/
private dealNoImageIconCache(bundleName: string, code?: number): void {
if (CheckEmptyUtils.checkStrIsEmpty(bundleName)) {
log.showWarn(`bundleName is empty`);
return;
}
if (code !== AppGalleryEventStatus.INSTALL_SUCCEED_EVENT_ID) {
return;
}
let appItem: AppItemInfo | undefined = AppModel.getInstance().getAppInfoByBundleName(bundleName);
if (CheckEmptyUtils.isEmpty(appItem)) {
IconResourceManager.getInstance().deleteIconResource(bundleName, UpdateType.UPDATE);
}
}
private handleBundleMappingEventChange(event: CommonEvent): void {
log.showInfo('handleBundleMappingEventChange start');
if (!event?.parameters) {
log.showInfo('handleBundleMappingEventChange end, event parameters are null');
return;
}
let hasohosApp: string[] = event.parameters['ag.params.HARMONY_PKGS_ADDED'] as string[];
let notohosApp: string[] = event.parameters['ag.params.HARMONY_PKGS_REMOVED'] as string[];
let publicTestohosApp: string[] = event.parameters['ag.params.PUBLIC_TEST_APP_PKGS_ADDED'] as string[];
let relationMap: Map<string, boolean> = new Map();
let publicTestRelationMap: Map<string, boolean> = new Map();
if (hasohosApp && hasohosApp.length > 0) {
hasohosApp.forEach(item => {
log.showInfo(`BundleMappingEventChange hasohosApp: ${item}`);
relationMap.set(item, true);
});
}
if (publicTestohosApp && publicTestohosApp.length > 0) {
publicTestohosApp.forEach(item => {
log.showInfo(`BundleMappingEventChange publicTestohosApp: ${item}`);
publicTestRelationMap.set(item, true);
});
}
this.dealNotohosApp(notohosApp, relationMap, publicTestRelationMap);
NotHarmonyUtil.lightingNotHarmonyAppIcons(relationMap, publicTestRelationMap);
if (!CheckEmptyUtils.isEmptyArr(notohosApp)) {
for (let i = 0; i < notohosApp.length; i++) {
relationMap.delete(notohosApp[i]);
publicTestRelationMap.delete(notohosApp[i]);
}
}
NotHarmonyUtil.refreshNotHarmonyFolderPosition(relationMap, publicTestRelationMap);
log.showInfo('handleBundleMappingEventChange end');
}
private dealNotohosApp(notohosApp: string[], relationMap: Map<string, boolean>,
publicTestRelationMap: Map<string, boolean>): void {
if (CheckEmptyUtils.isEmptyArr(notohosApp)) {
log.showInfo('BundleMappingEventChange dealNotohosApp is empty');
return;
}
try {
let preference = preferences.getPreferencesSync(GlobalContext.getContext(), { name: 'DESKTOP_LAYOUT_INFO' });
let lightDeliverBundleNames: string = preference.getSync('lightDeliverBundleNames', '') as string;
let lightArr: string[] = lightDeliverBundleNames.split(',');
notohosApp.forEach(item => {
if (lightArr.indexOf(item) !== CommonConstants.INVALID_VALUE) {
log.showWarn('BundleMappingEventChange notohosApp is delivery light app %{public}s', item);
return;
}
log.showInfo(`BundleMappingEventChange notohosApp: ${item}`);
relationMap.set(item, false);
publicTestRelationMap.set(item, false);
});
} catch (error) {
log.showInfo('set notohosApp error: ${public}s', error?.message);
}
}
private isUpdateStatus(event: CommonEvent): boolean {
if (event.parameters?.updateStatus && event.parameters.updateStatus === 1) {
return true;
}
return false;
}
private isAppUpdating(bundleName: string): boolean {
let appItem: AppItemInfo | undefined = AppModel.getInstance().getAppInfoByBundleName(bundleName);
return appItem !== undefined && appItem !== null && (appItem.appStatus === undefined || appItem.appStatus === 0);
}
private isDistributeType(event: CommonEvent): boolean {
let IsAppDistribute: boolean = event?.parameters?.distributeType === TYPE_ENTERPRISE ||
event?.parameters?.distributeType === TYPE_INTERNALTESTING;
if (!IsAppDistribute) {
IsAppDistribute = AppUpdateUtils.checkIsEnterPriseTypeAppUpdate(event?.parameters?.bundleName);
}
return IsAppDistribute;
}
private dealCancelOrFailedTask(bundleName: string, statusCode: number): void {
log.showWarn('dealCancelOrFailedTask bundleName: %{public}s.', bundleName);
this.removeMainAndTwinAppByBundleName(bundleName);
this.dealUpdateAppItemAfterInstall(bundleName, statusCode);
}
private dealWithDownloadStatusChange(eventData: DownloadStatusInfo, statusCode: number, actionBundleName: string): void {
log.showWarn(`dealWithDownloadStatusChange statusCode is ${statusCode} bundleName is ${actionBundleName},updateStatus is ${eventData?.updateStatus}`);
let downloadInfo: DownloadStatusInfo | undefined = this.getDownDownloadTaskInfo(actionBundleName);
if (downloadInfo && downloadInfo.status === AppStatus.INSTALLING) {
log.showWarn(`app is installing return!`);
return;
}
let data: DownloadStatusInfo = eventData;
switch (statusCode) {
case AppGalleryEventStatus.DOWNLOAD_CANCEL_EVENT_ID:
log.showWarn('cancel task successful, bundleName: %{public}s.', actionBundleName);
if (data.bundleName.startsWith(WAIT_FOR_STRING)) {
this.deleteNotHarmonyItem(data);
break;
}
if (this.cancelDownloadSet.has(actionBundleName)) {
this.cancelDownloadSet.remove(actionBundleName);
} else {
this.dealCancelOrFailedTask(data.bundleName, statusCode);
}
break;
case AppGalleryEventStatus.DOWNLOAD_FAILED_EVENT_ID: {
if (data.bundleName.startsWith(WAIT_FOR_STRING)) {
this.deleteNotHarmonyItem(data);
break;
}
this.dealCancelOrFailedTask(data.bundleName, statusCode);
break;
}
case AppGalleryEventStatus.DOWNLOAD_CREATE_EVENT_ID: {
this.dealAppGalleryCreatedEvent(data, actionBundleName, eventData);
break;
}
case AppGalleryEventStatus.DOWNLOAD_SUCCEED_EVENT_ID: {
if (this.appDownloadMap.hasKey(data.bundleName)) {
HiSysEventUtil.reportAnimationDurationAppDownload(data.bundleName, Date.now() - this.appDownloadMap.get(data.bundleName));
}
break;
}
case AppGalleryEventStatus.COMPANY_APP_RESET_EVENT_ID: {
this.deleteDownloadProgressInfo(data.bundleName);
this.deleteDownloadTaskInfo(data.bundleName);
let appList: GridLayoutItemInfo[] = LaunchLayoutCacheManager.getInstance().getAllSameBundleNameAppItem(data.bundleName);
let status: AppStatus = this.installEventAppStatusMap.get(statusCode) ?? AppStatus.PENDING;
let icon: string | undefined = !CheckEmptyUtils.isEmptyArr(appList) ? appList[0].iconResource : '';
this.refreshAppIconCache(data, status, icon ?? '', appList);
this.dealWithDownloadStatusChangeEvent(statusCode, data, actionBundleName, eventData);
break;
}
default: {
this.dealWithDownloadStatusChangeEvent(statusCode, data, actionBundleName, eventData);
}
}
}
private refreshAppIconCache(data: DownloadStatusInfo, status: AppStatus, icon: string, appList: GridLayoutItemInfo[]): GridLayoutItemInfo {
data.status = status;
let item: GridLayoutItemInfo = new GridLayoutItemInfo();
item.bundleName = data.bundleName;
if (data.legacyInfos && !CheckEmptyUtils.isEmptyArr(data.legacyInfos) &&
!CheckEmptyUtils.checkStrIsEmpty(data.legacyInfos[0].pkgName)) {
item.oldBundleNames = [data.legacyInfos[0].pkgName];
}
if (CheckEmptyUtils.checkStrIsEmpty(data.appName)) {
item.appName = !CheckEmptyUtils.isEmptyArr(appList) ? appList[0].appName : '';
} else {
item.appName = data.appName;
}
item.appIndex = CommonConstants.MAIN_APP_INDEX;
item.keyName = data.bundleName + CommonConstants.MAIN_APP_INDEX;
item.typeId = CommonConstants.TYPE_APP;
item.appStatus = status;
item.iconResource = icon;
item.area = [1, 1];
item.container = CommonConstants.CONTAINER_DESKTOP;
item.intent = !CheckEmptyUtils.isEmptyArr(appList) ? appList[0].intent : '';
localEventManager.sendLocalEventSticky(EventConstants.EVENT_REQUEST_APPGALLERY_CREATED, [item]);
return item;
}
private dealWithDownloadStatusChangeEvent(statusCode: number, data: DownloadStatusInfo, actionBundleName: string,
eventData: DownloadStatusInfo): void {
let mapAppStatus: AppStatus | undefined = this.installEventAppStatusMap.get(statusCode);
if (mapAppStatus) {
data.status = mapAppStatus;
let downloadInfo: DownloadInfoItem = {
bundleName: actionBundleName,
appStatus: mapAppStatus
};
RdbStoreManager.getInstance().updateDownloadInfo(downloadInfo);
if (mapAppStatus !== AppStatus.PENDING) {
this.addDownloadTaskInfo(data);
}
let appGalleryEventInfo: AppGalleryEventInfo = eventData as AppGalleryEventInfo;
appGalleryEventInfo.eventType = DownloadStatusChangeEvent.DOWNLOAD_STATUS_CHANGE;
appGalleryEventInfo.status = mapAppStatus;
this.emitAppGalleryStatusChangeEvent(appGalleryEventInfo);
}
}
private dealWithInstallStatusChangeEvent(statusCode: number, data: DownloadStatusInfo, actionBundleName: string,
eventData: DownloadStatusInfo): void {
let mapAppStatus: AppStatus | undefined = this.installEventAppStatusMap.get(statusCode);
if (mapAppStatus) {
data.status = mapAppStatus;
let downloadInfo: DownloadInfoItem = {
bundleName: actionBundleName,
appStatus: mapAppStatus
};
let appGalleryEventInfo: AppGalleryEventInfo = eventData as AppGalleryEventInfo;
appGalleryEventInfo.eventType = InstallStatusChangeEvent.INSTALL_STATUS_CHANGE;
appGalleryEventInfo.status = mapAppStatus;
this.emitAppGalleryStatusChangeEvent(appGalleryEventInfo);
}
}
private dealAppGalleryCreatedEvent(data: DownloadStatusInfo, actionBundleName: string,
eventData: DownloadStatusInfo): void {
if (!data) {
log.showWarn(`dealAppGalleryCreatedEvent data is null or undefined`);
return;
}
if (GetHideAppsFromConfig.getInstance().isHideApp(actionBundleName)) {
return;
}
log.showInfo(`dealWithDownloadStatusChange ${data.bundleName} ${data.updateStatus}`);
if (data.updateStatus === 1) {
this.dealUpdateAppItem(eventData, data.bundleName);
} else {
data.status = AppStatus.WAITING;
this.addDownloadTaskInfo(data);
let appList: GridLayoutItemInfo[] = [];
if (!data.legacyInfos || CheckEmptyUtils.isEmptyArr(data.legacyInfos) ||
CheckEmptyUtils.checkStrIsEmpty(data.legacyInfos[0].pkgName)) {
appList = LaunchLayoutCacheManager.getInstance().getAllSameBundleNameAppItem(data.bundleName);
} else {
appList = LaunchLayoutCacheManager.getInstance().getAllSameBundleNameAppItem(data.legacyInfos[0].pkgName);
}
this.dealNotHarmonyTaskIfNeeded(data, appList);
this.deleteOtherNotHarmonyTaskIfNeed(data);
AppInstallUtils.getInstance().getIconFromAppGalleryPool([data.bundleName]).then((icon: string) => {
this.onGetIconFromAppGallery(data, icon, actionBundleName, appList);
if (AppCategoryInfoManager.getInstance().getAppCatEnable()) {
AppCategoryInfoManager.getInstance().updateAppCatInfo([data.bundleName]);
}
}).catch((err: Error) => {
log.showInfo('dealWithDownloadStatusChange error: %{public}s', err.message);
});
}
}
private dealNotHarmonyTaskIfNeeded(data: DownloadStatusInfo, appList: GridLayoutItemInfo[]): void {
if (!data.legacyInfos || CheckEmptyUtils.isEmptyArr(data.legacyInfos) ||
CheckEmptyUtils.isEmpty(data.legacyInfos[0].pkgName)) {
return;
}
if (FolderLayoutCacheManager.getInstance().isAppExistNotDragOutFolder(data.legacyInfos[0].pkgName)) {
for (let i = 0; i < appList?.length; i++) {
this.notifyDownloadCanceled(appList[i].bundleName, appList[i].appIndex ?? 0);
log.showInfo('wait for openHarmony app delete folder app bundleName %{public}s, appIndex :%{public}d',
appList[i].bundleName, appList[i].appIndex);
}
data.legacyInfos = [];
} else {
appList.forEach(item => {
if (item.container === CommonConstants.CONTAINER_SMARTDOCK) {
let residentList: DockItemInfo[] = ResidentLayoutCacheMgr.getInstance().getAllDockItems();
let appInDockIndex: number = residentList.findIndex(dockItem => dockItem.keyName === item.keyName);
residentList[appInDockIndex].bundleName = data.bundleName;
residentList[appInDockIndex].keyName = data.bundleName + residentList[appInDockIndex].appIndex;
LayoutViewModel.getInstance().updateResidentDockLayout(TAG.concat('_deal'), residentList);
log.showInfo(`wait for openHarmony app update bundleName in cache, oldBundleName ${data.legacyInfos?.[0].pkgName}, bundleName ${data.bundleName}`);
}
});
let downloadInfo: DownloadInfoItem = {
bundleName: data.bundleName,
appStatus: AppStatus.WAITING,
oldBundleName: data.legacyInfos[0].pkgName,
updateTwinApp: true
};
log.showInfo(`wait for openHarmony app update bundleName in db, oldBundleName ${data.legacyInfos[0].pkgName}, bundleName ${data.bundleName}`);
RdbStoreManager.getInstance().updateDownloadInfo(downloadInfo);
}
}
private deleteOtherNotHarmonyTaskIfNeed(data: DownloadStatusInfo): void {
if (data.legacyInfos && !CheckEmptyUtils.isEmptyArr(data.legacyInfos) && data.legacyInfos?.length > 1) {
for (let i = 1; i < data.legacyInfos.length; i++) {
if (data.legacyInfos[i].pkgName === data.legacyInfos[0].pkgName) {
continue;
}
let appList: GridLayoutItemInfo[] = LaunchLayoutCacheManager.getInstance().getAllSameBundleNameAppItem(data.legacyInfos[i].pkgName);
appList.forEach(item => {
this.notifyDownloadCanceled(item.bundleName, item.appIndex ?? 0);
log.showInfo('many to one, delete other item, bundleName %{public}s, appIndex :%{public}d',
item.bundleName, item.appIndex);
});
}
}
}
private dealUpdateAppItemAfterInstall(bundleName: string, statusCode: number): void {
if (AppUpdateUtils.checkIsEnterPriseTypeAppUpdate(bundleName) &&
(statusCode === AppGalleryEventStatus.DOWNLOAD_CANCEL_EVENT_ID ||
statusCode === AppGalleryEventStatus.DOWNLOAD_FAILED_EVENT_ID ||
statusCode === AppGalleryEventStatus.INSTALL_SUCCEED_EVENT_ID)
) {
AppUpdateUtils.deleteEnterPriseTypeAppUpdateList(bundleName);
let item: GridLayoutItemInfo[] = LaunchLayoutCacheManager.getInstance().getAllSameBundleNameAppItem(bundleName);
for (let i = 0; i < item.length; i++) {
log.showInfo(`item bundleName: ${item[i].bundleName}, item keyName: ${item[i].keyName}`);
item[i].appStatus = AppStatus.INSTALLED;
}
let downloadInfo: DownloadInfoItem = {
bundleName: bundleName,
appStatus: AppStatus.INSTALLED
};
RdbStoreManager.getInstance().updateDownloadInfo(downloadInfo);
localEventManager.sendLocalEventSticky(EventConstants.EVENT_REQUEST_APPGALLERY_CREATED, item);
} else {
log.showInfo(`no fresh updateAppBundleNameList`);
}
}
private dealUpdateAppItem(eventData: DownloadStatusInfo, bundleName: string): void {
log.showInfo(`enter dealUpdateAppItem`);
let appGalleryEventInfo: AppGalleryEventInfo = eventData as AppGalleryEventInfo;
if (appGalleryEventInfo.distributeType === TYPE_ENTERPRISE || appGalleryEventInfo.distributeType === TYPE_INTERNALTESTING) {
let item: GridLayoutItemInfo[] = LaunchLayoutCacheManager.getInstance().getAllSameBundleNameAppItem(bundleName);
for (let i = 0; i < item.length; i++) {
log.showInfo(`item bundleName: ${item[i].bundleName}, item keyName: ${item[i].keyName}`);
item[i].appStatus = AppStatus.UPDATING;
}
let downloadInfo: DownloadInfoItem = {
bundleName: bundleName,
appStatus: AppStatus.UPDATING
};
RdbStoreManager.getInstance().updateDownloadInfo(downloadInfo);
localEventManager.sendLocalEventSticky(EventConstants.EVENT_REQUEST_APPGALLERY_CREATED, item);
let updateAppBundleNameList: string[] = AppStorage.get<string[]>('updateAppBundleNameList') as string[];
if (!updateAppBundleNameList) {
AppStorage.setOrCreate('updateAppBundleNameList', [bundleName]);
} else {
updateAppBundleNameList.push(bundleName);
AppStorage.setOrCreate('updateAppBundleNameList', updateAppBundleNameList);
}
}
}
private deleteNotHarmonyItem(data: DownloadStatusInfo): void {
let pkgName = data.bundleName;
if (data.legacyInfos && !CheckEmptyUtils.isEmptyArr(data.legacyInfos)) {
pkgName = data.legacyInfos[0].pkgName ?? data.bundleName;
log.showInfo('pkgName : %{public}s bundleName : %{public}s', pkgName, data.bundleName);
} else {
pkgName = pkgName.split('__')[2];
log.showInfo('pkgName : %{public}s bundleName : %{public}s', pkgName, data.bundleName);
}
if (!this.isAppUpdating(pkgName)) {
this.removeMainAndTwinAppByBundleName(pkgName);
}
}
private onGetIconFromAppGallery(data: DownloadStatusInfo, icon: string, actionBundleName: string,
appList: GridLayoutItemInfo[]): void {
log.showInfo('AppGalleryEventStatus CREATE_EVENT_ID icon is %{public}s', icon);
let status: number = AppStatus.WAITING;
let downloadInfo: DownloadStatusInfo | undefined = this.getDownDownloadTaskInfo(actionBundleName);
if (downloadInfo) {
status = downloadInfo.status;
} else {
log.showInfo('onGetIconFromAppGallery return for canceled');
return;
}
data.status = status;
let item: GridLayoutItemInfo = new GridLayoutItemInfo();
item.bundleName = data.bundleName;
if (data.legacyInfos && !CheckEmptyUtils.isEmptyArr(data.legacyInfos)) {
item.oldBundleNames = [data.legacyInfos[0].pkgName];
}
item.appIndex = CommonConstants.MAIN_APP_INDEX;
item.keyName = data.bundleName + CommonConstants.MAIN_APP_INDEX;
item.typeId = CommonConstants.TYPE_APP;
item.appStatus = status;
item.appName = data?.appName;
item.iconResource = (icon === undefined) ? undefined : FILE_PRE + icon;
item.area = [1, 1];
const limitNum = !CheckEmptyUtils.isEmptyArr(appList) ? CommonUtils.jsonStrToMap(appList[0].intent)
.get(SHORTCUT_LIMIT_KEY) : undefined;
if (limitNum !== undefined) {
let intentMap: Map<string, Object> = CommonUtils.jsonStrToMap(item.intent);
intentMap.set(SHORTCUT_LIMIT_KEY, limitNum);
item.intent = CommonUtils.mapToJonStr(intentMap);
}
item.container = CommonConstants.CONTAINER_DESKTOP;
localEventManager.sendLocalEventSticky(EventConstants.EVENT_REQUEST_APPGALLERY_CREATED, [item]);
log.showInfo(`download main app, bundleName: ${item.bundleName}, appIndex: ${item.appIndex}, appStatus: ${item.appStatus}, iconResource: ${item.iconResource}`);
this.refreshAppTwinIcon(item, appList);
this.addDownloadTaskInfo(data);
this.appDownloadMap.set(item.bundleName, Date.now());
}
private refreshAppTwinIcon(item: GridLayoutItemInfo, appList: GridLayoutItemInfo[]): void {
if (CheckEmptyUtils.isEmptyArr(appList)) {
return;
}
appList.forEach(sameBundleNameItem => {
if ((sameBundleNameItem.appIndex ?? 0) > 0) {
item.appIndex = sameBundleNameItem.appIndex;
item.keyName = item.bundleName + sameBundleNameItem.appIndex;
item.appStatus = AppStatus.PENDING;
item.installTime = new Date().getTime().toString();
item.appName = CheckEmptyUtils.checkStrIsEmpty(item.appName) ? sameBundleNameItem.appName : item.appName;
localEventManager.sendLocalEventSticky(EventConstants.EVENT_REQUEST_APPGALLERY_CREATED, [item]);
log.showInfo(`download twin app, bundleName: ${item.bundleName}, appIndex: ${item.appIndex}, appStatus: ${item.appStatus}, iconResource: ${item.iconResource}`);
}
});
}
public removeDownloadInfo(bundleName: string, appIndex: number = 0): void {
this.notifyDownloadCanceled(bundleName, appIndex);
if (appIndex === CommonConstants.MAIN_APP_INDEX) {
this.deleteDownloadProgressInfo(bundleName);
this.deleteDownloadTaskInfo(bundleName);
}
}
private dealWithDownloadProgressChange(eventData: AppDownloadInfo, actionBundleName: string): void {
let totalSize: number = eventData.totalSize;
let downloadedSize: number = eventData.downloadedSize;
let progress: number = downloadedSize / totalSize;
let downloadInfo: DownloadInfoItem = {
bundleName: actionBundleName,
downloadProgress: progress
};
RdbStoreManager.getInstance().updateDownloadInfo(downloadInfo);
this.addDownloadProgressInfo(eventData);
let data: DownloadStatusInfo = {
bundleName: actionBundleName,
status: AppStatus.DOWNLOADING
};
this.addDownloadTaskInfo(data);
let appGalleryEventInfo: AppGalleryEventInfo = eventData as AppGalleryEventInfo;
appGalleryEventInfo.eventType = DownloadingProgressChangeEvent.DOWNLOAD_PROGRESS_CHANGE;
this.emitAppGalleryStatusChangeEvent(appGalleryEventInfo);
}
private dealWithInstallStatusChange(eventData: AppInstallStatusInfo, statusCode: number, actionBundleName: string): void {
log.showInfo('dealWithInstallStatusChange statusCode is %{public}d', statusCode);
if (statusCode === AppGalleryEventStatus.INSTALL_START_EVENT_ID) {
let statusChangeEventDate: DownloadStatusInfo = {
bundleName: actionBundleName,
status: AppStatus.INSTALLING
};
this.dealWithDownloadStatusChangeEvent(AppGalleryEventStatus.INSTALL_START_EVENT_ID, statusChangeEventDate, actionBundleName, statusChangeEventDate);
}
if (statusCode === AppGalleryEventStatus.INSTALL_FAILED_EVENT_ID) {
this.installFailedNotify(actionBundleName);
this.removeMainAndTwinAppByBundleName(actionBundleName);
}
this.dealUpdateAppItemAfterInstall(actionBundleName, statusCode);
}
private installFailedNotify(actionBundleName: string): void {
if (DeviceHelper.isPC()) {
let statusChangeEventDate: DownloadStatusInfo = {
bundleName: actionBundleName,
status: AppStatus.INSTALL_FAIL
};
this.dealWithInstallStatusChangeEvent(AppGalleryEventStatus.INSTALL_FAILED_EVENT_ID, statusChangeEventDate, actionBundleName, statusChangeEventDate);
}
}
private removeMainAndTwinAppByBundleName(bundleName: string): void {
this.deleteDownloadProgressInfo(bundleName);
this.deleteDownloadTaskInfo(bundleName);
let appList: GridLayoutItemInfo[] =
LaunchLayoutCacheManager.getInstance().getAllSameBundleNameAppItem(bundleName);
appList.forEach(item => {
log.showWarn(`remove item, bundleName:${item.bundleName}, appIndex:${item.appIndex}, status:${item.appStatus}`);
if (item.appStatus !== AppStatus.INSTALLED) {
this.notifyDownloadCanceled(bundleName, item.appIndex ?? 0);
}
});
}
private emitAppGalleryStatusChangeEvent(data: AppGalleryEventInfo): void {
const eventHub: ctx.EventHub = (GlobalContext.getInstance().getObject('desktopContext') as ctx.ServiceExtensionContext)?.eventHub;
eventHub.emit(`appGalleryStatusChange${data.bundleName}${CommonConstants.MAIN_APP_INDEX}item`, data);
eventHub.emit(`appGalleryStatusChange${data.bundleName}${CommonConstants.MAIN_APP_INDEX}appCenter`, data);
eventHub.emit(`appGalleryStatusChange${data.bundleName}${CommonConstants.MAIN_APP_INDEX}icon`, data);
eventHub.emit(`appGalleryStatusChange${data.bundleName}${CommonConstants.MAIN_APP_INDEX}name`, data);
eventHub.emit(`appGalleryStatusChange${data.bundleName}${CommonConstants.MAIN_APP_INDEX}folder`, data);
eventHub.emit(`appGalleryStatusChange${data.bundleName}${CommonConstants.MAIN_APP_INDEX}smallFolder`, data);
}
private addDownloadProgressInfo(appDownloadInfo: AppDownloadInfo): void {
let currentInfoIndex: number = this.downloadProgressList.findIndex(item => item.bundleName === appDownloadInfo.bundleName);
if (currentInfoIndex !== -1) {
this.downloadProgressList[currentInfoIndex] = appDownloadInfo;
} else {
this.downloadProgressList.push(appDownloadInfo);
}
}
public deleteDownloadProgressInfo(actionBundleName: string): void {
this.downloadProgressList = this.downloadProgressList.filter(item => item.bundleName !== actionBundleName);
}
private addDownloadTaskInfo(downloadStatusInfo: DownloadStatusInfo): void {
let currentInfoIndex: number = this.downloadTaskList.findIndex(item => item.bundleName === downloadStatusInfo.bundleName);
if (currentInfoIndex !== -1) {
this.downloadTaskList[currentInfoIndex] = ObjectCopyUtil.simpleClone(downloadStatusInfo);
} else {
this.downloadTaskList.push(downloadStatusInfo);
}
}
public deleteDownloadTaskInfo(actionBundleName: string): void {
this.downloadTaskList = this.downloadTaskList.filter(item => item.bundleName !== actionBundleName);
}
* 读取应用的下载进度
* @param actionBundleName 应用bundleName
* @returns
*/
public getDownloadProgressInfo(actionBundleName: string): AppDownloadInfo | undefined {
return this.downloadProgressList.find(item => item.bundleName === actionBundleName);
}
* 读取应用的下载信息
* @param actionBundleName 应用bundleName
* @returns
*/
public getDownDownloadTaskInfo(actionBundleName: string): DownloadStatusInfo | undefined {
return this.downloadTaskList.find(item => item.bundleName === actionBundleName);
}
private changeAllCacheAppToPause(): void {
let downloadingList: DownloadStatusInfo[] = [];
this.downloadTaskList.forEach((item) => {
if (item.status === AppStatus.WAITING || item.status === AppStatus.DOWNLOADING ||
item.status === AppStatus.INSTALL_WAITING || item.status === AppStatus.INSTALLING) {
downloadingList.push(item);
}
});
downloadingList.forEach((item) => {
let downloadInfo: DownloadInfoItem = {
bundleName: item.bundleName,
appStatus: AppStatus.PAUSING
};
RdbStoreManager.getInstance().updateDownloadInfo(downloadInfo);
});
}
private async removeAllNotInstalledApp(): Promise<void> {
let notInstalledApplicationList: string[] = await RdbStoreManager.getInstance().queryNotInstalledApplication();
notInstalledApplicationList.forEach(bundleName => {
log.showInfo('delete not installed app: %{public}s', bundleName);
this.removeDownloadInfo(bundleName);
});
}
}
export interface AppGalleryDownloadListener {
onDownloadCanceled(event: string, bundleName: string, appIndex: number): void;
}
export interface CheckTaskOptions {
ownerInfo: ESObject;
callerName: string;
}