Merge "Allow user to select second split app from Taskbar" into tm-qpr-dev am: d5525c3bda

Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/apps/Launcher3/+/20365694

Change-Id: I8d1e153a3d9f2b04293a41a0e008cc348fe028d9
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Jeremy Sim
2022-11-10 19:25:02 +00:00
committed by Automerger Merge Worker
6 changed files with 154 additions and 48 deletions

View File

@@ -41,8 +41,7 @@ public class FallbackTaskbarUIController extends TaskbarUIController {
animateToRecentsState(toState);
// Handle tapping on live tile.
RecentsView recentsView = mRecentsActivity.getOverviewPanel();
recentsView.setTaskLaunchListener(toState == RecentsState.DEFAULT
getRecentsView().setTaskLaunchListener(toState == RecentsState.DEFAULT
? (() -> animateToRecentsState(RecentsState.BACKGROUND_APP)) : null);
}
};
@@ -88,4 +87,9 @@ public class FallbackTaskbarUIController extends TaskbarUIController {
anim.start();
}
}
@Override
public RecentsView getRecentsView() {
return mRecentsActivity.getOverviewPanel();
}
}

View File

@@ -47,6 +47,7 @@ import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.OnboardingPrefs;
import com.android.quickstep.AnimatedFloat;
import com.android.quickstep.RecentsAnimationCallbacks;
import com.android.quickstep.views.RecentsView;
import java.io.PrintWriter;
import java.util.Arrays;
@@ -391,4 +392,9 @@ public class LauncherTaskbarUIController extends TaskbarUIController {
mTaskbarLauncherStateController.dumpLogs(prefix + "\t", pw);
}
@Override
public RecentsView getRecentsView() {
return mLauncher.getOverviewPanel();
}
}

View File

@@ -91,6 +91,7 @@ import com.android.launcher3.util.SettingsCache;
import com.android.launcher3.util.TraceHelper;
import com.android.launcher3.util.ViewCache;
import com.android.launcher3.views.ActivityContext;
import com.android.quickstep.views.RecentsView;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.rotation.RotationButtonController;
import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -133,6 +134,7 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
private final boolean mIsUserSetupComplete;
private final boolean mIsNavBarForceVisible;
private final boolean mIsNavBarKidsMode;
private boolean mIsDestroyed = false;
// The flag to know if the window is excluded from magnification region computation.
private boolean mIsExcludeFromMagnificationRegion = false;
@@ -757,42 +759,63 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
if (info.isDisabled()) {
ItemClickHandler.handleDisabledItemClicked(info, this);
} else {
Intent intent = new Intent(info.getIntent())
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
try {
if (mIsSafeModeEnabled && !PackageManagerHelper.isSystemApp(this, intent)) {
Toast.makeText(this, R.string.safemode_shortcut_error,
Toast.LENGTH_SHORT).show();
} else if (info.isPromise()) {
TestLogging.recordEvent(
TestProtocol.SEQUENCE_MAIN, "start: taskbarPromiseIcon");
intent = new PackageManagerHelper(this)
.getMarketIntent(info.getTargetPackage())
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
TaskbarUIController taskbarUIController = mControllers.uiController;
RecentsView recents = taskbarUIController.getRecentsView();
if (recents != null
&& taskbarUIController.getRecentsView().isSplitSelectionActive()) {
// If we are selecting a second app for split, launch the split tasks
taskbarUIController.triggerSecondAppForSplit(info, info.intent, view);
} else {
// Else launch the selected task
Intent intent = new Intent(info.getIntent())
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
try {
if (mIsSafeModeEnabled && !PackageManagerHelper.isSystemApp(this, intent)) {
Toast.makeText(this, R.string.safemode_shortcut_error,
Toast.LENGTH_SHORT).show();
} else if (info.isPromise()) {
TestLogging.recordEvent(
TestProtocol.SEQUENCE_MAIN, "start: taskbarPromiseIcon");
intent = new PackageManagerHelper(this)
.getMarketIntent(info.getTargetPackage())
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
} else if (info.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
TestLogging.recordEvent(
TestProtocol.SEQUENCE_MAIN, "start: taskbarDeepShortcut");
String id = info.getDeepShortcutId();
String packageName = intent.getPackage();
getSystemService(LauncherApps.class)
.startShortcut(packageName, id, null, null, info.user);
} else {
startItemInfoActivity(info);
} else if (info.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
TestLogging.recordEvent(
TestProtocol.SEQUENCE_MAIN, "start: taskbarDeepShortcut");
String id = info.getDeepShortcutId();
String packageName = intent.getPackage();
getSystemService(LauncherApps.class)
.startShortcut(packageName, id, null, null, info.user);
} else {
startItemInfoActivity(info);
}
mControllers.uiController.onTaskbarIconLaunched(info);
} catch (NullPointerException
| ActivityNotFoundException
| SecurityException e) {
Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT)
.show();
Log.e(TAG, "Unable to launch. tag=" + info + " intent=" + intent, e);
}
mControllers.uiController.onTaskbarIconLaunched(info);
mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true);
} catch (NullPointerException | ActivityNotFoundException | SecurityException e) {
Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT)
.show();
Log.e(TAG, "Unable to launch. tag=" + info + " intent=" + intent, e);
}
mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true);
}
} else if (tag instanceof AppInfo) {
startItemInfoActivity((AppInfo) tag);
mControllers.uiController.onTaskbarIconLaunched((AppInfo) tag);
AppInfo info = (AppInfo) tag;
TaskbarUIController taskbarUIController = mControllers.uiController;
RecentsView recents = taskbarUIController.getRecentsView();
if (recents != null
&& taskbarUIController.getRecentsView().isSplitSelectionActive()) {
// If we are selecting a second app for split, launch the split tasks
taskbarUIController.triggerSecondAppForSplit(info, info.intent, view);
} else {
// Else launch the selected task
startItemInfoActivity((AppInfo) tag);
mControllers.uiController.onTaskbarIconLaunched((AppInfo) tag);
}
mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true);
} else {
Log.e(TAG, "Unknown type clicked: " + tag);

View File

@@ -15,13 +15,18 @@
*/
package com.android.launcher3.taskbar;
import android.content.Intent;
import android.graphics.drawable.BitmapDrawable;
import android.view.MotionEvent;
import android.view.View;
import androidx.annotation.CallSuper;
import androidx.annotation.Nullable;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
import java.io.PrintWriter;
import java.util.stream.Stream;
@@ -135,4 +140,38 @@ public class TaskbarUIController {
prefix,
getClass().getSimpleName()));
}
/**
* Returns RecentsView. Overwritten in LauncherTaskbarUIController and
* FallbackTaskbarUIController with Launcher-specific implementations. Returns null for other
* UI controllers (like DesktopTaskbarUIController) that don't have a RecentsView.
*/
public @Nullable RecentsView getRecentsView() {
return null;
}
/**
* Uses the clicked Taskbar icon to launch a second app for splitscreen.
*/
public void triggerSecondAppForSplit(ItemInfoWithIcon info, Intent intent, View startingView) {
RecentsView recents = getRecentsView();
TaskView foundTaskView = recents.getTaskViewByComponentName(info.getTargetComponent());
if (foundTaskView != null) {
recents.confirmSplitSelect(
foundTaskView,
foundTaskView.getTask(),
foundTaskView.getIconView().getDrawable(),
foundTaskView.getThumbnail(),
foundTaskView.getThumbnail().getThumbnail(),
/* intent */ null);
} else {
recents.confirmSplitSelect(
/* containerTaskView */ null,
/* task */ null,
new BitmapDrawable(info.bitmap.icon),
startingView,
/* thumbnail */ null,
intent);
}
}
}

View File

@@ -74,9 +74,12 @@ import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.WindowConfiguration;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.LocusId;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.BlendMode;
import android.graphics.Canvas;
import android.graphics.Color;
@@ -662,8 +665,6 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
private TaskView mSecondSplitHiddenView;
@Nullable
private SplitBounds mSplitBoundsConfig;
private final Toast mSplitToast = Toast.makeText(getContext(),
R.string.toast_split_select_app, Toast.LENGTH_SHORT);
private final Toast mSplitUnsupportedToast = Toast.makeText(getContext(),
R.string.toast_split_app_unsupported, Toast.LENGTH_SHORT);
@@ -1213,6 +1214,21 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
return null;
}
/**
* Returns a {@link TaskView} that has ComponentName matching {@code componentName} or null if
* no match.
*/
@Nullable
public TaskView getTaskViewByComponentName(ComponentName componentName) {
for (int i = 0; i < getTaskViewCount(); i++) {
TaskView taskView = requireTaskViewAt(i);
if (taskView.getTask().key.sourceComponent.equals(componentName)) {
return taskView;
}
}
return null;
}
public void setOverviewStateEnabled(boolean enabled) {
mOverviewStateEnabled = enabled;
updateTaskStackListenerState();
@@ -4237,24 +4253,39 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
* Confirms the selection of the next split task. The extra data is passed through because the
* user may be selecting a subtask in a group.
*
* @param containerTaskView If our second selected app is currently running in Recents, this is
* the "container" TaskView from Recents. If we are starting a fresh
* instance of the app from an Intent, this will be null.
* @param task The Task corresponding to our second selected app. If we are starting a fresh
* instance of the app from an Intent, this will be null.
* @param drawable The Drawable corresponding to our second selected app's icon.
* @param secondView The View representing the current space on the screen where the second app
* is (either the ThumbnailView or the tapped icon).
* @param intent If we are launching a fresh instance of the app, this is the Intent for it. If
* the second app is already running in Recents, this will be null.
* @return true if waiting for confirmation of second app or if split animations are running,
* false otherwise
*/
public boolean confirmSplitSelect(TaskView containerTaskView, Task task, IconView iconView,
TaskThumbnailView thumbnailView) {
public boolean confirmSplitSelect(TaskView containerTaskView, Task task, Drawable drawable,
View secondView, @Nullable Bitmap thumbnail, Intent intent) {
if (canLaunchFullscreenTask()) {
return false;
}
if (mSplitSelectStateController.isBothSplitAppsConfirmed()) {
return true;
}
mSplitToast.cancel();
if (!task.isDockable) {
// Task not split screen supported
mSplitUnsupportedToast.show();
return true;
// Second task is selected either as an already-running Task or an Intent
if (task != null) {
if (!task.isDockable) {
// Task does not support split screen
mSplitUnsupportedToast.show();
return true;
}
mSplitSelectStateController.setSecondTask(task);
} else {
mSplitSelectStateController.setSecondTask(intent);
}
mSplitSelectStateController.setSecondTask(task);
RectF secondTaskStartingBounds = new RectF();
Rect secondTaskEndingBounds = new Rect();
// TODO(194414938) starting bounds seem slightly off, investigate
@@ -4281,9 +4312,9 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
false /* fadeWithThumbnail */, true /* isStagedTask */);
safeRemoveDragLayerView(mSecondFloatingTaskView);
mSecondFloatingTaskView = FloatingTaskView.getFloatingTaskView(mActivity,
thumbnailView, thumbnailView.getThumbnail(),
iconView.getDrawable(), secondTaskStartingBounds);
mSecondFloatingTaskView = FloatingTaskView.getFloatingTaskView(mActivity, secondView,
thumbnail, drawable, secondTaskStartingBounds);
mSecondFloatingTaskView.setAlpha(1);
mSecondFloatingTaskView.addConfirmAnimation(pendingAnimation, secondTaskStartingBounds,
secondTaskEndingBounds, true /* fadeWithThumbnail */, false /* isStagedTask */);
@@ -4299,7 +4330,9 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
});
mSecondSplitHiddenView = containerTaskView;
mSecondSplitHiddenView.setThumbnailVisibility(INVISIBLE);
if (mSecondSplitHiddenView != null) {
mSecondSplitHiddenView.setThumbnailVisibility(INVISIBLE);
}
InteractionJankMonitorWrapper.begin(this,
InteractionJankMonitorWrapper.CUJ_SPLIT_SCREEN_ENTER, "Second tile selected");

View File

@@ -621,7 +621,8 @@ public class TaskView extends FrameLayout implements Reusable {
TaskIdAttributeContainer container = mTaskIdAttributeContainer[index];
if (container != null) {
return getRecentsView().confirmSplitSelect(this, container.getTask(),
container.getIconView(), container.getThumbnailView());
container.getIconView().getDrawable(), container.getThumbnailView(),
container.getThumbnailView().getThumbnail(), /* intent */ null);
}
return false;
}