blob: f7d8e8c0d1347dc696dd7651127728b163ad06da [file] [log] [blame]
/*
* Copyright (C) 2021 The Android Open Source Project
*
* 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.
*/
package com.android.launcher3.folder;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_CONVERTED_TO_ICON;
import android.content.Context;
import android.graphics.Rect;
import android.graphics.RectF;
import android.view.MotionEvent;
import android.view.View;
import androidx.annotation.Nullable;
import com.android.launcher3.CellLayout;
import com.android.launcher3.DragSource;
import com.android.launcher3.DropTarget;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.logging.InstanceId;
import com.android.launcher3.logging.StatsLogManager.StatsLogger;
import com.android.launcher3.model.ModelWriter;
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.views.BaseDragLayer;
import com.android.launcher3.views.BaseDragLayer.LayoutParams;
import com.android.launcher3.widget.LocalColorExtractor;
import java.util.Arrays;
import java.util.Optional;
import java.util.function.Consumer;
/**
* Wrapper around Launcher methods to allow folders in non-launcher context
*/
public class LauncherDelegate {
private final Launcher mLauncher;
private final Rect mTempRect = new Rect();
private final RectF mTempRectF = new RectF();
private LauncherDelegate(Launcher launcher) {
mLauncher = launcher;
}
void init(Folder folder, FolderIcon icon) {
folder.setDragController(mLauncher.getDragController());
icon.setOnFocusChangeListener(mLauncher.getFocusHandler());
}
boolean isDraggingEnabled() {
return mLauncher.isDraggingEnabled();
}
void beginDragShared(View child, DragSource source, DragOptions options) {
mLauncher.getWorkspace().beginDragShared(child, source, options);
}
ModelWriter getModelWriter() {
return mLauncher.getModelWriter();
}
void forEachVisibleWorkspacePage(Consumer<View> callback) {
mLauncher.getWorkspace().forEachVisiblePage(callback);
}
@Nullable
Launcher getLauncher() {
return mLauncher;
}
void addRectForColorExtraction(BaseDragLayer.LayoutParams lp, LocalColorExtractor target) {
mTempRect.set(lp.x, lp.y, lp.x + lp.width, lp.y + lp.height);
target.getExtractedRectForViewRect(mLauncher,
mLauncher.getWorkspace().getCurrentPage(), mTempRect, mTempRectF);
if (!mTempRectF.isEmpty()) {
target.addLocation(Arrays.asList(mTempRectF));
}
}
void replaceFolderWithFinalItem(Folder folder) {
// Add the last remaining child to the workspace in place of the folder
Runnable onCompleteRunnable = new Runnable() {
@Override
public void run() {
int itemCount = folder.getItemCount();
FolderInfo info = folder.mInfo;
if (itemCount <= 1) {
View newIcon = null;
WorkspaceItemInfo finalItem = null;
if (itemCount == 1) {
// Move the item from the folder to the workspace, in the position of the
// folder
CellLayout cellLayout = mLauncher.getCellLayout(info.container,
info.screenId);
finalItem = info.contents.remove(0);
newIcon = mLauncher.createShortcut(cellLayout, finalItem);
mLauncher.getModelWriter().addOrMoveItemInDatabase(finalItem,
info.container, info.screenId, info.cellX, info.cellY);
}
// Remove the folder
mLauncher.removeItem(folder.mFolderIcon, info, true /* deleteFromDb */);
if (folder.mFolderIcon instanceof DropTarget) {
folder.mDragController.removeDropTarget((DropTarget) folder.mFolderIcon);
}
if (newIcon != null) {
// We add the child after removing the folder to prevent both from existing
// at the same time in the CellLayout. We need to add the new item with
// addInScreenFromBind() to ensure that hotseat items are placed correctly.
mLauncher.getWorkspace().addInScreenFromBind(newIcon, info);
// Focus the newly created child
newIcon.requestFocus();
}
if (finalItem != null) {
StatsLogger logger = mLauncher.getStatsLogManager().logger()
.withItemInfo(finalItem);
((Optional<InstanceId>) folder.mDragController.getLogInstanceId())
.map(logger::withInstanceId)
.orElse(logger)
.log(LAUNCHER_FOLDER_CONVERTED_TO_ICON);
}
}
}
};
View finalChild = folder.mContent.getLastItem();
if (finalChild != null) {
folder.mFolderIcon.performDestroyAnimation(onCompleteRunnable);
} else {
onCompleteRunnable.run();
}
}
boolean interceptOutsideTouch(MotionEvent ev, BaseDragLayer dl, Folder folder) {
if (mLauncher.getAccessibilityDelegate().isInAccessibleDrag()) {
// Do not close the container if in drag and drop.
if (!dl.isEventOverView(mLauncher.getDropTargetBar(), ev)) {
return true;
}
} else {
// TODO: add ww log if need to gather tap outside to close folder
folder.close(true);
return true;
}
return false;
}
private static class FallbackDelegate extends LauncherDelegate {
private final ActivityContext mContext;
private ModelWriter mWriter;
FallbackDelegate(ActivityContext context) {
super(null);
mContext = context;
}
@Override
void init(Folder folder, FolderIcon icon) {
folder.setDragController(mContext.getDragController());
}
@Override
boolean isDraggingEnabled() {
return false;
}
@Override
void beginDragShared(View child, DragSource source, DragOptions options) { }
@Override
ModelWriter getModelWriter() {
if (mWriter == null) {
mWriter = LauncherAppState.getInstance((Context) mContext).getModel()
.getWriter(false, false);
}
return mWriter;
}
@Override
void forEachVisibleWorkspacePage(Consumer<View> callback) { }
@Override
Launcher getLauncher() {
return null;
}
@Override
void replaceFolderWithFinalItem(Folder folder) { }
@Override
boolean interceptOutsideTouch(MotionEvent ev, BaseDragLayer dl, Folder folder) {
folder.close(true);
return true;
}
@Override
void addRectForColorExtraction(LayoutParams lp, LocalColorExtractor target) { }
}
static LauncherDelegate from(ActivityContext context) {
return context instanceof Launcher
? new LauncherDelegate((Launcher) context)
: new FallbackDelegate(context);
}
}