to be allowed. This check existed before the user was able to rotate via the rotation button while autorotate setting is off. Test: manual Fix: 201511975 Change-Id: I920886cb42ec5b8f4d3eb0bdd80b216cd7c3c849
368 lines
14 KiB
Java
368 lines
14 KiB
Java
/*
|
|
* Copyright (C) 2018 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.quickstep;
|
|
|
|
import static android.view.Surface.ROTATION_0;
|
|
|
|
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
|
|
import static com.android.quickstep.views.OverviewActionsView.DISABLED_NO_THUMBNAIL;
|
|
import static com.android.quickstep.views.OverviewActionsView.DISABLED_ROTATED;
|
|
|
|
import android.annotation.SuppressLint;
|
|
import android.content.Context;
|
|
import android.graphics.Insets;
|
|
import android.graphics.Matrix;
|
|
import android.graphics.Rect;
|
|
import android.os.Build;
|
|
import android.view.View;
|
|
import android.widget.Toast;
|
|
|
|
import androidx.annotation.NonNull;
|
|
import androidx.annotation.RequiresApi;
|
|
|
|
import com.android.launcher3.BaseActivity;
|
|
import com.android.launcher3.BaseDraggingActivity;
|
|
import com.android.launcher3.DeviceProfile;
|
|
import com.android.launcher3.R;
|
|
import com.android.launcher3.config.FeatureFlags;
|
|
import com.android.launcher3.model.data.ItemInfo;
|
|
import com.android.launcher3.model.data.WorkspaceItemInfo;
|
|
import com.android.launcher3.popup.SystemShortcut;
|
|
import com.android.launcher3.touch.PagedOrientationHandler;
|
|
import com.android.launcher3.util.ResourceBasedOverride;
|
|
import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
|
|
import com.android.quickstep.TaskShortcutFactory.SplitSelectSystemShortcut;
|
|
import com.android.quickstep.util.RecentsOrientedState;
|
|
import com.android.quickstep.views.OverviewActionsView;
|
|
import com.android.quickstep.views.RecentsView;
|
|
import com.android.quickstep.views.TaskThumbnailView;
|
|
import com.android.quickstep.views.TaskView;
|
|
import com.android.quickstep.views.TaskView.TaskIdAttributeContainer;
|
|
import com.android.systemui.shared.recents.model.Task;
|
|
import com.android.systemui.shared.recents.model.ThumbnailData;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
|
|
/**
|
|
* Factory class to create and add an overlays on the TaskView
|
|
*/
|
|
public class TaskOverlayFactory implements ResourceBasedOverride {
|
|
|
|
public static List<SystemShortcut> getEnabledShortcuts(TaskView taskView,
|
|
DeviceProfile deviceProfile, TaskIdAttributeContainer taskContainer) {
|
|
final ArrayList<SystemShortcut> shortcuts = new ArrayList<>();
|
|
final BaseDraggingActivity activity = BaseActivity.fromContext(taskView.getContext());
|
|
boolean hasMultipleTasks = taskView.getTaskIds()[1] != -1;
|
|
for (TaskShortcutFactory menuOption : MENU_OPTIONS) {
|
|
if (hasMultipleTasks && !menuOption.showForSplitscreen()) {
|
|
continue;
|
|
}
|
|
|
|
SystemShortcut shortcut = menuOption.getShortcut(activity, taskContainer);
|
|
if (shortcut == null) {
|
|
continue;
|
|
}
|
|
|
|
if (menuOption == TaskShortcutFactory.SPLIT_SCREEN &&
|
|
FeatureFlags.ENABLE_SPLIT_SELECT.get()) {
|
|
addSplitOptions(shortcuts, activity, taskView, deviceProfile);
|
|
} else {
|
|
shortcuts.add(shortcut);
|
|
}
|
|
}
|
|
RecentsOrientedState orientedState = taskView.getRecentsView().getPagedViewOrientedState();
|
|
boolean canLauncherRotate = orientedState.isRecentsActivityRotationAllowed();
|
|
boolean isInLandscape = orientedState.getTouchRotation() != ROTATION_0;
|
|
|
|
// Add overview actions to the menu when in in-place rotate landscape mode.
|
|
if (!canLauncherRotate && isInLandscape) {
|
|
// Add screenshot action to task menu.
|
|
SystemShortcut screenshotShortcut = TaskShortcutFactory.SCREENSHOT
|
|
.getShortcut(activity, taskContainer);
|
|
if (screenshotShortcut != null) {
|
|
shortcuts.add(screenshotShortcut);
|
|
}
|
|
|
|
// Add modal action only if display orientation is the same as the device orientation.
|
|
if (orientedState.getDisplayRotation() == ROTATION_0) {
|
|
SystemShortcut modalShortcut = TaskShortcutFactory.MODAL
|
|
.getShortcut(activity, taskContainer);
|
|
if (modalShortcut != null) {
|
|
shortcuts.add(modalShortcut);
|
|
}
|
|
}
|
|
}
|
|
return shortcuts;
|
|
}
|
|
|
|
|
|
private static void addSplitOptions(List<SystemShortcut> outShortcuts,
|
|
BaseDraggingActivity activity, TaskView taskView, DeviceProfile deviceProfile) {
|
|
int[] taskViewTaskIds = taskView.getTaskIds();
|
|
boolean taskViewHasMultipleTasks = taskViewTaskIds[0] != -1 &&
|
|
taskViewTaskIds[1] != -1;
|
|
boolean notEnoughTasksToSplit = taskView.getRecentsView().getTaskViewCount() < 2;
|
|
if (taskViewHasMultipleTasks || notEnoughTasksToSplit ||
|
|
(deviceProfile.overviewShowAsGrid && taskView.isFocusedTask())) {
|
|
return;
|
|
}
|
|
|
|
PagedOrientationHandler orientationHandler =
|
|
taskView.getRecentsView().getPagedOrientationHandler();
|
|
List<SplitPositionOption> positions =
|
|
orientationHandler.getSplitPositionOptions(deviceProfile);
|
|
for (SplitPositionOption option : positions) {
|
|
outShortcuts.add(new SplitSelectSystemShortcut(activity, taskView, option));
|
|
}
|
|
}
|
|
|
|
public TaskOverlay createOverlay(TaskThumbnailView thumbnailView) {
|
|
return new TaskOverlay(thumbnailView);
|
|
}
|
|
|
|
/**
|
|
* Subclasses can attach any system listeners in this method, must be paired with
|
|
* {@link #removeListeners()}
|
|
*/
|
|
public void initListeners() { }
|
|
|
|
/**
|
|
* Subclasses should remove any system listeners in this method, must be paired with
|
|
* {@link #initListeners()}
|
|
*/
|
|
public void removeListeners() { }
|
|
|
|
/** Note that these will be shown in order from top to bottom, if available for the task. */
|
|
private static final TaskShortcutFactory[] MENU_OPTIONS = new TaskShortcutFactory[]{
|
|
TaskShortcutFactory.APP_INFO,
|
|
TaskShortcutFactory.SPLIT_SCREEN,
|
|
TaskShortcutFactory.PIN,
|
|
TaskShortcutFactory.INSTALL,
|
|
TaskShortcutFactory.FREE_FORM,
|
|
TaskShortcutFactory.WELLBEING
|
|
};
|
|
|
|
/**
|
|
* Overlay on each task handling Overview Action Buttons.
|
|
*/
|
|
public static class TaskOverlay<T extends OverviewActionsView> {
|
|
|
|
protected final Context mApplicationContext;
|
|
protected final TaskThumbnailView mThumbnailView;
|
|
|
|
private T mActionsView;
|
|
protected ImageActionsApi mImageApi;
|
|
|
|
protected TaskOverlay(TaskThumbnailView taskThumbnailView) {
|
|
mApplicationContext = taskThumbnailView.getContext().getApplicationContext();
|
|
mThumbnailView = taskThumbnailView;
|
|
mImageApi = new ImageActionsApi(
|
|
mApplicationContext, mThumbnailView::getThumbnail);
|
|
}
|
|
|
|
protected T getActionsView() {
|
|
if (mActionsView == null) {
|
|
mActionsView = BaseActivity.fromContext(mThumbnailView.getContext()).findViewById(
|
|
R.id.overview_actions_view);
|
|
}
|
|
return mActionsView;
|
|
}
|
|
|
|
/**
|
|
* Called when the current task is interactive for the user
|
|
*/
|
|
public void initOverlay(Task task, ThumbnailData thumbnail, Matrix matrix,
|
|
boolean rotated) {
|
|
getActionsView().updateDisabledFlags(DISABLED_NO_THUMBNAIL, thumbnail == null);
|
|
|
|
if (thumbnail != null) {
|
|
getActionsView().updateDisabledFlags(DISABLED_ROTATED, rotated);
|
|
boolean isAllowedByPolicy = mThumbnailView.isRealSnapshot();
|
|
getActionsView().setCallbacks(new OverlayUICallbacksImpl(isAllowedByPolicy, task));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Called when the current task's thumbnail has changed.
|
|
*/
|
|
public void refreshActionVisibility(ThumbnailData thumbnail) {
|
|
getActionsView().updateDisabledFlags(DISABLED_NO_THUMBNAIL, thumbnail == null);
|
|
}
|
|
|
|
/**
|
|
* End rendering live tile in Overview.
|
|
*
|
|
* @param callback callback to run, after switching to screenshot
|
|
*/
|
|
public void endLiveTileMode(@NonNull Runnable callback) {
|
|
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
|
|
RecentsView recentsView = mThumbnailView.getTaskView().getRecentsView();
|
|
recentsView.switchToScreenshot(
|
|
() -> recentsView.finishRecentsAnimation(true /* toRecents */,
|
|
false /* shouldPip */, callback));
|
|
} else {
|
|
callback.run();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Called to save screenshot of the task thumbnail.
|
|
*/
|
|
@SuppressLint("NewApi")
|
|
protected void saveScreenshot(Task task) {
|
|
if (mThumbnailView.isRealSnapshot()) {
|
|
mImageApi.saveScreenshot(mThumbnailView.getThumbnail(),
|
|
getTaskSnapshotBounds(), getTaskSnapshotInsets(), task.key);
|
|
} else {
|
|
showBlockedByPolicyMessage();
|
|
}
|
|
}
|
|
|
|
private void enterSplitSelect() {
|
|
RecentsView overviewPanel = mThumbnailView.getTaskView().getRecentsView();
|
|
overviewPanel.initiateSplitSelect(mThumbnailView.getTaskView());
|
|
}
|
|
|
|
/**
|
|
* Called when the overlay is no longer used.
|
|
*/
|
|
public void reset() {
|
|
}
|
|
|
|
/**
|
|
* Called when the system wants to reset the modal visuals.
|
|
*/
|
|
public void resetModalVisuals() {
|
|
}
|
|
|
|
/**
|
|
* Gets the modal state system shortcut.
|
|
*/
|
|
public SystemShortcut getModalStateSystemShortcut(WorkspaceItemInfo itemInfo) {
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Sets full screen progress to the task overlay.
|
|
*/
|
|
public void setFullscreenProgress(float progress) {
|
|
}
|
|
|
|
/**
|
|
* Gets the system shortcut for the screenshot that will be added to the task menu.
|
|
*/
|
|
public SystemShortcut getScreenshotShortcut(BaseDraggingActivity activity,
|
|
ItemInfo iteminfo) {
|
|
return new ScreenshotSystemShortcut(activity, iteminfo);
|
|
}
|
|
/**
|
|
* Gets the task snapshot as it is displayed on the screen.
|
|
*
|
|
* @return the bounds of the snapshot in screen coordinates.
|
|
*/
|
|
public Rect getTaskSnapshotBounds() {
|
|
int[] location = new int[2];
|
|
mThumbnailView.getLocationOnScreen(location);
|
|
|
|
return new Rect(location[0], location[1], mThumbnailView.getWidth() + location[0],
|
|
mThumbnailView.getHeight() + location[1]);
|
|
}
|
|
|
|
/**
|
|
* Gets the insets that the snapshot is drawn with.
|
|
*
|
|
* @return the insets in screen coordinates.
|
|
*/
|
|
@RequiresApi(api = Build.VERSION_CODES.Q)
|
|
public Insets getTaskSnapshotInsets() {
|
|
return mThumbnailView.getScaledInsets();
|
|
}
|
|
|
|
/**
|
|
* Called when the device rotated.
|
|
*/
|
|
public void updateOrientationState(RecentsOrientedState state) {
|
|
}
|
|
|
|
protected void showBlockedByPolicyMessage() {
|
|
Toast.makeText(
|
|
mThumbnailView.getContext(),
|
|
R.string.blocked_by_policy,
|
|
Toast.LENGTH_LONG).show();
|
|
}
|
|
|
|
private class ScreenshotSystemShortcut extends SystemShortcut {
|
|
|
|
private final BaseDraggingActivity mActivity;
|
|
|
|
ScreenshotSystemShortcut(BaseDraggingActivity activity, ItemInfo itemInfo) {
|
|
super(R.drawable.ic_screenshot, R.string.action_screenshot, activity, itemInfo);
|
|
mActivity = activity;
|
|
}
|
|
|
|
@Override
|
|
public void onClick(View view) {
|
|
saveScreenshot(mThumbnailView.getTaskView().getTask());
|
|
dismissTaskMenuView(mActivity);
|
|
}
|
|
}
|
|
|
|
protected class OverlayUICallbacksImpl implements OverlayUICallbacks {
|
|
protected final boolean mIsAllowedByPolicy;
|
|
protected final Task mTask;
|
|
|
|
public OverlayUICallbacksImpl(boolean isAllowedByPolicy, Task task) {
|
|
mIsAllowedByPolicy = isAllowedByPolicy;
|
|
mTask = task;
|
|
}
|
|
|
|
public void onShare() {
|
|
if (mIsAllowedByPolicy) {
|
|
endLiveTileMode(() -> mImageApi.startShareActivity(null));
|
|
} else {
|
|
showBlockedByPolicyMessage();
|
|
}
|
|
}
|
|
|
|
@SuppressLint("NewApi")
|
|
public void onScreenshot() {
|
|
endLiveTileMode(() -> saveScreenshot(mTask));
|
|
}
|
|
|
|
public void onSplit() {
|
|
endLiveTileMode(TaskOverlay.this::enterSplitSelect);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Callbacks the Ui can generate. This is the only way for a Ui to call methods on the
|
|
* controller.
|
|
*/
|
|
public interface OverlayUICallbacks {
|
|
/** User has indicated they want to share the current task. */
|
|
void onShare();
|
|
|
|
/** User has indicated they want to screenshot the current task. */
|
|
void onScreenshot();
|
|
|
|
/** User wants to start split screen with current app. */
|
|
void onSplit();
|
|
}
|
|
}
|