/*
* 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 {
AppItemInfo,
LayoutViewModel,
AppGridStyleConfig,
} from '@ohos/launchercommon';
import { LogDomain, LogHelper, SingleContext } from '@ohos/basicutils'
import { DragEventManager, DragEventCallback } from '@ohos/componentdrag'
import { AppCenterDragMgr } from '../controller/AppCenterDragMgr';
import AppGridLayoutUtil from '../common/util/AppGridLayoutUtil';
import AppGridLayoutCacheManager from '../common/cache/AppGridLayoutCacheManager';
import { APP_CENTER_DROP_FRAME_KEY } from '../constants/LayoutConstants';
import { FoldedDeviceAcViewModel } from '../folded/FoldedDeviceAcViewModel';
import { TranslateItem } from '../controller/CommonAcDragMgr';
import { SCBPropertyChangeReason, SCBScreenProperty, SCBScreenSessionManager } from '@ohos/windowscene';
const TAG = 'AppCenterDropFrame';
const log = LogHelper.getLogHelper(LogDomain.HOME, TAG);
@Component
export struct AppCenterDropFrame {
@Prop screenProp: SCBScreenProperty;
@Prop singleContext?: SingleContext;
@State mWhiteBoxX: number = 0;
@State mWhiteBoxY: number = 0;
@State mShowWhiteBox: boolean = false;
@Prop @Watch('onPageChange') pageIndex: number;
@Prop showDropFrame: boolean;
private appGridStyleConfig: AppGridStyleConfig = AppGridStyleConfig.getInstance(this.singleContext);
@State dropFrameOuterHeight: number = 0;
@State dropFrameOuterWidth: number = 0;
@State dropFrameInnerHeight: number = 0;
@State dropFrameInnerWidth: number = 0;
@State dropFrameRadius: number = 0;
private currentScreenHeight: number = this.screenProp.height;
private currentScreenWidth: number = this.screenProp.width;
private appCenterDragMgr: AppCenterDragMgr = AppCenterDragMgr.getInstance();
private mDragEventCallback: DragEventCallback = {};
private appCenterBubbleMarginTop?: number;
private appCenterBubbleMarginBottom?: number;
private startTranslatePosition: number[] = [];
private currentTranslatePosition: number[] = [];
private isDragScene: boolean = false;
aboutToAppear(): void {
log.showInfo('aboutToAppear');
this.updateDropFrameStyle(this.screenProp);
this.initDragEventCallback();
this.registerDragEvent();
SCBScreenSessionManager.getInstance()
.registerScreenPropertyChangeCallbacks(this.onScreenPropertyChange, this.screenProp.screenId);
}
aboutToDisappear(): void {
log.showInfo('aboutToDisappear');
this.unRegisterDragEvent();
this.mDragEventCallback = {};
SCBScreenSessionManager.getInstance()
.unRegisterScreenPropertyChangeCallbacks(this.onScreenPropertyChange, this.screenProp.screenId);
}
private registerDragEvent(): void {
log.showInfo('registerDragEvent');
DragEventManager.getInstance().registerDragEvent(this.mDragEventCallback, APP_CENTER_DROP_FRAME_KEY);
}
private unRegisterDragEvent(): void {
log.showInfo('unRegisterDragEvent');
DragEventManager.getInstance().unRegisterDragEvent(this.mDragEventCallback, APP_CENTER_DROP_FRAME_KEY);
}
onScreenPropertyChange = (screenProperty: SCBScreenProperty, reason: SCBPropertyChangeReason) => {
if (this.screenProp.screenId !== screenProperty.screenId) {
log.showInfo(`screen id not equal: ${this.screenProp.screenId} ${screenProperty.screenId}`);
return;
}
if (this.currentScreenHeight === screenProperty.height &&
this.currentScreenWidth === screenProperty.width) {
log.showInfo('screen width or height not change');
return;
}
if (FoldedDeviceAcViewModel.getInstance().isFoldedDevice()) {
log.showInfo('fold device');
return;
}
this.updateDropFrameStyle(screenProperty);
log.showInfo(`onScreenPropertyChange ${screenProperty.width} ${screenProperty.height}`);
}
private updateDropFrameStyle(screenProperty: SCBScreenProperty): void {
let result = LayoutViewModel.getInstance().calculateAppCenter(
screenProperty.screenId, false, screenProperty.width, screenProperty.height);
this.appCenterBubbleMarginTop = result.mAppCenterBubbleMarginTop;
this.appCenterBubbleMarginBottom = result.mAppCenterBubbleMarginBottom;
this.dropFrameOuterHeight = result.mAppCenterItemHeight ?? 0;
this.dropFrameOuterWidth = result.mAppCenterItemWidth ?? 0;
this.dropFrameInnerHeight = result.mIconSize ?? 0;
this.dropFrameInnerWidth = result.mIconSize ?? 0;
this.dropFrameRadius = result.mIconRadius ?? 0;
this.currentScreenHeight = screenProperty.height;
this.currentScreenWidth = screenProperty.width;
log.showInfo(`updateDropFrameStyle result: ${JSON.stringify(result)}`);
}
private onStartSqueezedEvent(translatePosition: number[][]): void {
if (!this.isPosChanged(translatePosition) && this.mShowWhiteBox) {
this.mWhiteBoxX = translatePosition[1][0];
this.mWhiteBoxY = translatePosition[1][1];
return;
}
this.currentTranslatePosition = translatePosition[1];
// 连续拖拽时落位框动效场景
if (this.isDragScene) {
this.animateToMove(translatePosition[1][0], translatePosition[1][1], false, true);
} else {
this.animateToMove(translatePosition[1][0], translatePosition[1][1], true, false);
this.isDragScene = true;
}
}
private isPosChanged(translatePosition: number[][]): boolean {
if (!this.appCenterDragMgr.isInDragging()) {
log.showInfo('isPosChanged not in drag');
return false;
}
// 移到空白页oldPostion和newPostion相同
if (translatePosition[0][0] === translatePosition[1][0] &&
translatePosition[0][1] === translatePosition[1][1]) {
log.showInfo('isPosChanged move to blank page');
return true;
}
// 位置没有变化
if (translatePosition[0].length === 0 && this.mShowWhiteBox) {
log.showInfo('isPosChanged position not change 0');
return false;
}
// 发生挤位
let dragItemInfo = this.appCenterDragMgr.getDragItemInfo() as TranslateItem;
if (dragItemInfo.row === dragItemInfo.newRow &&
dragItemInfo.column === dragItemInfo.newColumn &&
dragItemInfo.page === dragItemInfo.newPage) {
log.showInfo(`isPosChanged position not change from,
x: ${this.mWhiteBoxX} to ${translatePosition[1][0]}, y: ${this.mWhiteBoxY} to ${translatePosition[1][1]}`);
return false;
}
log.showInfo(`isPosChanged move from row:${dragItemInfo.row}, column:${dragItemInfo.column}, page:${dragItemInfo.page} to
newRow:${dragItemInfo.newRow}, newColumn:${dragItemInfo.newColumn}, newPage:${dragItemInfo.newPage}`);
return true;
}
animateToMove(x: number, y: number, isShow: boolean, isDragScene: boolean): void {
this.mWhiteBoxX = x;
this.mWhiteBoxY = y;
animateTo({
duration: isDragScene ? 1 : 250,
curve: Curve.Sharp,
onFinish: () => {
if (isDragScene && this.appCenterDragMgr.isInDragging()) {
animateTo({
duration: 250,
curve: Curve.Sharp
}, () => {
this.mShowWhiteBox = true;
})
}
}
}, () => {
this.mShowWhiteBox = isShow;
});
}
private onPageChange(): void {
if (this.appCenterDragMgr.isAnimating()) {
return;
}
log.showInfo('onPageChange');
this.mShowWhiteBox = this.appCenterDragMgr.getCurrentIsInAppCenterWorkBench(this.screenProp.screenId) &&
!AppGridLayoutCacheManager.getInstance().isFullPage(this.pageIndex);
if (this.mShowWhiteBox) {
this.mWhiteBoxX = this.currentTranslatePosition[0];
this.mWhiteBoxY = this.currentTranslatePosition[1];
return;
}
const appGridList: AppItemInfo[] =
AppGridLayoutCacheManager.getInstance().getAppGridLayoutItemsByPage(this.pageIndex);
let position: number[] | undefined = [];
const length: number = appGridList.length;
if (length === 0) {
position = AppGridLayoutUtil.getGridItemPosition(0, 0, undefined, this.screenProp.screenId);
} else if (this.appCenterDragMgr.getStartDragPage() === this.pageIndex) {
position = this.startTranslatePosition;
this.mShowWhiteBox = true;
} else {
const grid: number[] | undefined =
AppGridLayoutCacheManager.getInstance().getNextPosition(false, appGridList[length - 1].row,
appGridList[length - 1].column);
if (grid !== undefined) {
position = AppGridLayoutUtil.getGridItemPosition(grid[0], grid[1], undefined, this.screenProp.screenId);
}
}
if (position !== undefined) {
this.mWhiteBoxX = position[0];
this.mWhiteBoxY = position[1];
}
}
private initDragEventCallback(): void {
this.mDragEventCallback = {
onItemDragStartEvent: (): void => {
this.startTranslatePosition = this.appCenterDragMgr.getStartDragPos(this.screenProp.screenId);
this.mWhiteBoxX = this.startTranslatePosition?.[0];
this.mWhiteBoxY = this.startTranslatePosition?.[1];
this.mShowWhiteBox = true;
log.showInfo(`onItemDragStartEvent startTranslatePosition: ${this.mWhiteBoxX}, ${this.mWhiteBoxY}`);
},
onItemDropStartEvent: (): void => {
log.showInfo('onItemDropStartEvent');
},
onItemDropEndEvent: (): void => {
log.showInfo('onItemDropEndEvent');
this.mShowWhiteBox = false;
this.startTranslatePosition = [];
this.currentTranslatePosition = [];
this.isDragScene = false;
},
onStartSqueezedEvent: (translatePosition: number[][]): void => {
log.showInfo('onStartSqueezedEvent');
this.onStartSqueezedEvent(translatePosition);
},
onCancelSqueezedEvent: (hasAnimation: boolean): void => {
log.showInfo(`onCancelSqueezedEvent-${AppCenterDragMgr.getInstance().getStartDragPage()},startAimationDrop:${ this.startAimationDrop()}`);
if (this.needMoveBack()) {
this.animateToMove(this.startTranslatePosition[0], this.startTranslatePosition[1], true, false);
} else {
this.mShowWhiteBox = false;
}
log.showInfo('onCancelSqueezedEvent');
}
}
}
private needMoveBack(): boolean {
if (FoldedDeviceAcViewModel.getInstance().isFoldedDevice() && this.startAimationDrop()) {
return this.appCenterDragMgr.getStartDragPage() !== undefined;
} else {
return this.appCenterDragMgr.getStartDragPage() === this.pageIndex;
}
}
// 针对不在拖拽图标不在应用中心工作区,且hopper设备不在统一屏幕的B,C屏,返回false,不显示图标原位置落位框
private startAimationDrop(): boolean {
if (!FoldedDeviceAcViewModel.getInstance().isFoldedDevice()) {
return true;
}
return Math.floor((this.appCenterDragMgr.getStartDragPage() ?? 0) / 2)=== Math.floor(this.pageIndex / 2);
}
build() {
Column() {
Row() {
}
.backgroundColor($r('sys.color.ohos_id_color_component_normal'))
.width(this.dropFrameInnerWidth)
.height(this.dropFrameInnerHeight)
.borderWidth(1)
.borderColor('#33FFFFFF')
.borderRadius(this.dropFrameRadius)
.outlineWidth(1)
.outlineColor('#1A000000')
.outlineRadius(this.dropFrameRadius)
.margin({
top: this.appCenterBubbleMarginTop,
bottom: this.appCenterBubbleMarginBottom
})
}
.width(this.dropFrameOuterWidth)
.height(this.dropFrameOuterHeight)
.key(TAG)
.position({ x: this.mWhiteBoxX, y: this.mWhiteBoxY })
.opacity(this.mShowWhiteBox && this.showDropFrame ? 1 : 0)
}
}