/*
* Copyright (c) Huawei Device Co., Ltd. 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 { LogDomain, LogHelper, SingletonHelper } from '@ohos/basicutils';
import {
editModeManager,
CommonConstants,
GridLayoutItemInfo,
AppListInfo,
AppItemInfo,
CardItemInfo,
DefaultDesktopLayoutInfo
} from '../TsIndex';
import { DeviceHelper, HiSysReportEvent, ReportDomain } from '@ohos/frameworkwrapper';
const APP_AREA_SIZE = [1, 1];
const CARD_2_SIZE = 2;
const OUTER_LAYOUT_SIZE = 4;
const ADD_ICON_APP_STORAGE_NAME = 'isShowOuterAddAppIcon';
const IS_DELETE_OUTER_APP = 'isDeleteOuterApp';
const DEFAULT_MAX_PAGE_COUNT = 18;
// 打点参数
const PNAMEID: string = 'com.ohos.sceneboard';
const PVERSIONID: string = 'sceneBoard';
const LAUNCHER_OUTER_ADD_ICON_EVENT: string = 'LAUNCHER_OUTER_ADD_ICON_EVENT';
const TAG: string = 'OuterAddAppIconManager';
const log: LogHelper = LogHelper.getLogHelper(LogDomain.HOME, TAG);
/**
* +号图标打点参数
*/
interface ReportParams {
// 进程包名
PNAMEID: string;
// 进程名
PVERSIONID: string;
}
class IconPositionInfo {
public page: number = 0;
public row: number = 0;
public column: number = 0;
}
class LastPageInfo {
public appList: GridLayoutItemInfo[] = [];
public pageIndex: number = 0;
}
/**
* 新形态小折叠外屏+号管理类
*/
export class OuterAddAppIconManager {
private needAddAppPageIndex: number = 0;
private isNeedDelBlackPage: boolean = false;
private addIconBeforeApp: GridLayoutItemInfo | null | undefined;
private maxPageCount: number =
DefaultDesktopLayoutInfo.getDefaultLayoutInfo()?.layoutDescription?.maxPage ?? DEFAULT_MAX_PAGE_COUNT;
private mOuterHomeReportEvent: HiSysReportEvent = HiSysReportEvent.getHiSysReportEvent(ReportDomain.OUTER_HOME_UE);
private isOccupiedArr: string[][] = [];
private lastAppBundleName: string | undefined;
private isOnlyRelyBundleName: boolean = false;
constructor() {
}
/**
* 获取最后一个App所在尾部位置
*
* @param 页面信息
* @returns Array [结束行,结束列]
*/
private getLastAppPosition(appList: GridLayoutItemInfo[]): [number, number] {
this.isOnlyRelyBundleName = false;
if (appList.length === 0) {
this.addIconBeforeApp = null;
this.lastAppBundleName = '';
return [0, 0];
}
let endRow = 0;
let endColumn = 0;
this.isOccupiedArr = Array(OUTER_LAYOUT_SIZE).fill(null).map(() =>
Array(OUTER_LAYOUT_SIZE).fill('')
);
this.addIconBeforeApp = appList[appList.length - 1];
this.lastAppBundleName = this.addIconBeforeApp?.bundleName;
appList.forEach((appInfo: GridLayoutItemInfo) => {
if (this.isAddAppIcon(appInfo)) {
return;
}
// postion [行, 列]
let curStartPost: [number, number] = [appInfo.row ?? 0, appInfo.column ?? 0];
let curEndColumn = curStartPost[1] + (appInfo.area?.[0] ?? 0);
let curEndRow = curStartPost[0] + (appInfo.area?.[1] ?? 0);
if (curEndRow > endRow || (curEndRow === endRow && curEndColumn > endColumn)) {
endRow = curEndRow;
endColumn = curEndColumn;
this.addIconBeforeApp = appInfo;
this.lastAppBundleName = appInfo.bundleName;
}
// 更新被占据的位置
for (let row = curStartPost[0]; row < curEndRow; row++) {
for (let col = curStartPost[1]; col < curEndColumn; col++) {
this.isOccupiedArr[row][col] = appInfo.bundleName;
}
}
});
// 特殊情况
let updatePostion: [number, number] | undefined = this.getCardSpecialPostion([endRow, endColumn]);
if (updatePostion) {
log.showInfo(`update [${endRow}, ${endColumn}] to ${updatePostion}`);
endRow = updatePostion[0];
endColumn = updatePostion[1];
}
return [endRow, endColumn];
}
/**
* 判断该应用是否为2*2卡片
*
* @param appInfo
* @returns true/false
*/
private isCardSize2(appInfo: GridLayoutItemInfo): boolean {
if (!appInfo || !appInfo.area || appInfo.area.length < 2) {
log.showInfo(`Get addIconBeforeApp area size failed.`);
return false;
}
return (appInfo.area?.[0] === CARD_2_SIZE && appInfo.area?.[1] === CARD_2_SIZE);
}
/**
* 若最后一个占位为2*2卡片,列举特殊情况并更新位置
*
* @param endPostion
* @returns new endPostion
*/
private getCardSpecialPostion(endPostion: [number, number]): [number, number] | undefined {
if (!this.addIconBeforeApp) {
return undefined;
}
// 若最后一个不为2*2卡片,或+号位置需要换行,直接返回
if (!this.isCardSize2(this.addIconBeforeApp) || endPostion[1] === OUTER_LAYOUT_SIZE) {
return undefined;
}
// 判断2*2卡片右侧1/2号位是否还有app
let rightIPostion: [number, number] = [endPostion[0] - 2, endPostion[1]];
let rightIIPostion: [number, number] = [endPostion[0] - 2, endPostion[1] + 1];
// 若右侧2号位有App
if (rightIIPostion[1] < OUTER_LAYOUT_SIZE && this.isOccupiedArr[rightIIPostion[0]][rightIIPostion[1]]) {
this.lastAppBundleName = this.isOccupiedArr[rightIIPostion[0]][rightIIPostion[1]];
return undefined;
}
log.showInfo(`This endPostion is 2*2 card. Update endPositon.`);
// 若右侧1号位有App
if (this.isOccupiedArr[rightIPostion[0]][rightIPostion[1]]) {
this.lastAppBundleName = this.isOccupiedArr[rightIPostion[0]][rightIPostion[1]];
if (rightIPostion[1] + 1 === OUTER_LAYOUT_SIZE) {
return undefined;
}
this.isOnlyRelyBundleName = true;
return [rightIPostion[0] + 1, rightIPostion[1] + 1];
}
return [endPostion[0] - 1, endPostion[1]];
}
/**
* 页面尾部位置是否已被占用
*
* @param 页面信息
* @returns boolean true已有app,false无
*/
private isPageEndHaveApp(appList: GridLayoutItemInfo[]): boolean {
return appList.some((appInfo: GridLayoutItemInfo) => {
let curEndColumn = (appInfo.column ?? 0) + (appInfo.area?.[0] ?? 0);
let curEndRow = (appInfo.row ?? 0) + (appInfo.area?.[1] ?? 0);
return curEndColumn === OUTER_LAYOUT_SIZE && curEndRow === OUTER_LAYOUT_SIZE && !this.isAddAppIcon(appInfo);
});
}
/**
* 获取尾部非空白页
*
* @param 页面列表
* @returns LastPageInfo 页面应用列表,页面索引
*/
private getLastHaveAppPage(pageList: GridLayoutItemInfo[][]): LastPageInfo {
for (let i = pageList.length - 1; i >= 0; i--) {
if (pageList[i].length > 0) {
return { appList: pageList[i], pageIndex: i };
}
}
return { appList: [], pageIndex: 0 };
}
/**
* 计算+号的位置
*
* @param pageIndex 最后一个app/卡片所在页索引
* @param endColumn 最后一个app/卡片右下角所在列
* @param endRow 最后一个app/卡片右下角所在行
*
* @returns +号所在位置
*/
private getAddIconPosition(pageIndex: number, endColumn: number, endRow: number): IconPositionInfo {
// 最后一个app在页面尾部,+号需要出现在下一页
if (endRow === OUTER_LAYOUT_SIZE && endColumn === OUTER_LAYOUT_SIZE) {
return { page: pageIndex + 1, column: 0, row: 0 };
}
// 最后一个app在所在行尾部,+号需要出现在下一行第一位
if (endRow < OUTER_LAYOUT_SIZE && endColumn === OUTER_LAYOUT_SIZE) {
return { page: pageIndex, column: 0, row: endRow };
}
return { page: pageIndex, column: endColumn, row: endRow - 1 };
}
/**
* 判断app是不是+号
*
* @param GridLayoutItemInfo 应用信息
* @returns boolean true是+号,false不是+号
*/
public isAddAppIcon(appInfo: GridLayoutItemInfo): boolean {
return appInfo.typeId === CommonConstants.TYPE_ADD &&
appInfo.bundleName === CommonConstants.OUTER_ADD_APP_ICON_BUNDLE_NAME;
}
/**
* +号图标打点信息
*
*/
public outerAddAppIconReport(): void {
let params: ReportParams = {
PNAMEID: PNAMEID,
PVERSIONID: PVERSIONID
};
this.mOuterHomeReportEvent.reportBehavior(LAUNCHER_OUTER_ADD_ICON_EVENT, params);
}
}
export const outerAddAppIconManager: OuterAddAppIconManager = SingletonHelper.getInstance(OuterAddAppIconManager, TAG);