Merge "Support splitting from workspace with Widgets" into udc-dev
This commit is contained in:
@@ -56,6 +56,10 @@ class QuickstepInteractionHandler implements RemoteViews.InteractionHandler {
|
||||
return RemoteViews.startPendingIntent(hostView, pendingIntent,
|
||||
remoteResponse.getLaunchOptions(view));
|
||||
}
|
||||
if (mLauncher.getSplitToWorkspaceController().handleSecondWidgetSelectionForSplit(view,
|
||||
pendingIntent)) {
|
||||
return true;
|
||||
}
|
||||
Pair<Intent, ActivityOptions> options = remoteResponse.getLaunchOptions(view);
|
||||
ActivityOptionsWrapper activityOptions = mLauncher.getAppTransitionManager()
|
||||
.getActivityLaunchOptions(hostView);
|
||||
|
||||
@@ -973,8 +973,8 @@ public class QuickstepLauncher extends Launcher {
|
||||
return mTaskbarUIController;
|
||||
}
|
||||
|
||||
public SplitSelectStateController getSplitSelectStateController() {
|
||||
return mSplitSelectStateController;
|
||||
public SplitToWorkspaceController getSplitToWorkspaceController() {
|
||||
return mSplitToWorkspaceController;
|
||||
}
|
||||
|
||||
public <T extends OverviewActionsView> T getActionsView() {
|
||||
|
||||
@@ -98,8 +98,15 @@ public class SplitSelectStateController {
|
||||
private UserHandle mInitialUser;
|
||||
private int mInitialTaskId = INVALID_TASK_ID;
|
||||
/** {@link #mSecondTaskIntent} and {@link #mSecondUser} (the user of the Intent) are set
|
||||
* together when split is confirmed with an Intent. */
|
||||
* together when split is confirmed with an Intent. Either this or {@link #mSecondPendingIntent}
|
||||
* will be set, but not both
|
||||
*/
|
||||
private Intent mSecondTaskIntent;
|
||||
/**
|
||||
* Set when split is confirmed via a widget. Either this or {@link #mSecondTaskIntent} will be
|
||||
* set, but not both
|
||||
*/
|
||||
private PendingIntent mSecondPendingIntent;
|
||||
private UserHandle mSecondUser;
|
||||
private int mSecondTaskId = INVALID_TASK_ID;
|
||||
private boolean mRecentsAnimationRunning;
|
||||
@@ -248,6 +255,16 @@ public class SplitSelectStateController {
|
||||
mSecondUser = user;
|
||||
}
|
||||
|
||||
/**
|
||||
* To be called as soon as user selects the second app (even if animations aren't complete)
|
||||
* Sets {@link #mSecondUser} from that of the pendingIntent
|
||||
* @param pendingIntent The second PendingIntent that will be launched.
|
||||
*/
|
||||
public void setSecondTask(PendingIntent pendingIntent) {
|
||||
mSecondPendingIntent = pendingIntent;
|
||||
mSecondUser = pendingIntent.getCreatorUserHandle();
|
||||
}
|
||||
|
||||
/**
|
||||
* To be called when we want to launch split pairs from an existing GroupedTaskView.
|
||||
*/
|
||||
@@ -292,17 +309,18 @@ public class SplitSelectStateController {
|
||||
if (freezeTaskList) {
|
||||
options1.setFreezeRecentTasksReordering();
|
||||
}
|
||||
boolean hasSecondaryPendingIntent = mSecondPendingIntent != null;
|
||||
if (TaskAnimationManager.ENABLE_SHELL_TRANSITIONS) {
|
||||
final RemoteSplitLaunchTransitionRunner animationRunner =
|
||||
new RemoteSplitLaunchTransitionRunner(taskId1, taskId2, callback);
|
||||
final RemoteTransition remoteTransition = new RemoteTransition(animationRunner,
|
||||
ActivityThread.currentActivityThread().getApplicationThread(),
|
||||
"LaunchSplitPair");
|
||||
if (intent1 == null && intent2 == null) {
|
||||
if (intent1 == null && (intent2 == null && !hasSecondaryPendingIntent)) {
|
||||
mSystemUiProxy.startTasks(taskId1, options1.toBundle(), taskId2,
|
||||
null /* options2 */, stagePosition, splitRatio, remoteTransition,
|
||||
shellInstanceId);
|
||||
} else if (intent2 == null) {
|
||||
} else if (intent2 == null && !hasSecondaryPendingIntent) {
|
||||
launchIntentOrShortcut(intent1, mInitialUser, options1, taskId2, stagePosition,
|
||||
splitRatio, remoteTransition, shellInstanceId);
|
||||
} else if (intent1 == null) {
|
||||
@@ -312,7 +330,9 @@ public class SplitSelectStateController {
|
||||
} else {
|
||||
mSystemUiProxy.startIntents(getPendingIntent(intent1, mInitialUser),
|
||||
getShortcutInfo(intent1, mInitialUser), options1.toBundle(),
|
||||
getPendingIntent(intent2, mSecondUser),
|
||||
hasSecondaryPendingIntent
|
||||
? mSecondPendingIntent
|
||||
: getPendingIntent(intent2, mSecondUser),
|
||||
getShortcutInfo(intent2, mSecondUser), null /* options2 */,
|
||||
stagePosition, splitRatio, remoteTransition, shellInstanceId);
|
||||
}
|
||||
@@ -323,11 +343,11 @@ public class SplitSelectStateController {
|
||||
animationRunner, 300, 150,
|
||||
ActivityThread.currentActivityThread().getApplicationThread());
|
||||
|
||||
if (intent1 == null && intent2 == null) {
|
||||
if (intent1 == null && (intent2 == null && !hasSecondaryPendingIntent)) {
|
||||
mSystemUiProxy.startTasksWithLegacyTransition(taskId1, options1.toBundle(),
|
||||
taskId2, null /* options2 */, stagePosition, splitRatio, adapter,
|
||||
shellInstanceId);
|
||||
} else if (intent2 == null) {
|
||||
} else if (intent2 == null && !hasSecondaryPendingIntent) {
|
||||
launchIntentOrShortcutLegacy(intent1, mInitialUser, options1, taskId2,
|
||||
stagePosition, splitRatio, adapter, shellInstanceId);
|
||||
} else if (intent1 == null) {
|
||||
@@ -338,7 +358,9 @@ public class SplitSelectStateController {
|
||||
mSystemUiProxy.startIntentsWithLegacyTransition(
|
||||
getPendingIntent(intent1, mInitialUser),
|
||||
getShortcutInfo(intent1, mInitialUser), options1.toBundle(),
|
||||
getPendingIntent(intent2, mSecondUser),
|
||||
hasSecondaryPendingIntent
|
||||
? mSecondPendingIntent
|
||||
: getPendingIntent(intent2, mSecondUser),
|
||||
getShortcutInfo(intent2, mSecondUser), null /* options2 */, stagePosition,
|
||||
splitRatio, adapter, shellInstanceId);
|
||||
}
|
||||
@@ -376,7 +398,22 @@ public class SplitSelectStateController {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* We treat launching by intents as grouped in two ways,
|
||||
* If {@param intent} represents the first app, we always convert the intent to pending intent
|
||||
* It it represents second app, either the second intent OR mSecondPendingIntent will be used
|
||||
* convert second intent to a pendingIntent OR return mSecondPendingIntent as is
|
||||
*/
|
||||
private PendingIntent getPendingIntent(Intent intent, UserHandle user) {
|
||||
boolean isParamFirstIntent = intent != null && intent == mInitialTaskIntent;
|
||||
if (!isParamFirstIntent && mSecondPendingIntent != null) {
|
||||
// Because mSecondPendingIntent and mSecondTaskIntent can't both be set, we know we need
|
||||
// to be using mSecondPendingIntent
|
||||
return mSecondPendingIntent;
|
||||
}
|
||||
|
||||
// intent param must either be mInitialTaskIntent or mSecondTaskIntent, convert either to
|
||||
// a new PendingIntent
|
||||
return intent == null ? null : (user != null
|
||||
? PendingIntent.getActivityAsUser(mContext, 0, intent,
|
||||
FLAG_MUTABLE | FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT, null /* options */, user)
|
||||
@@ -548,6 +585,7 @@ public class SplitSelectStateController {
|
||||
mSplitEvent = null;
|
||||
mAnimateCurrentTaskDismissal = false;
|
||||
mDismissingFromSplitPair = false;
|
||||
mSecondPendingIntent = null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -579,7 +617,8 @@ public class SplitSelectStateController {
|
||||
}
|
||||
|
||||
private boolean isSecondTaskIntentSet() {
|
||||
return (mSecondTaskId != INVALID_TASK_ID || mSecondTaskIntent != null);
|
||||
return (mSecondTaskId != INVALID_TASK_ID || mSecondTaskIntent != null
|
||||
|| mSecondPendingIntent != null);
|
||||
}
|
||||
|
||||
public void setFirstFloatingTaskView(FloatingTaskView floatingTaskView) {
|
||||
|
||||
@@ -21,9 +21,15 @@ import static com.android.launcher3.config.FeatureFlags.ENABLE_SPLIT_FROM_WORKSP
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.UserHandle;
|
||||
import android.view.View;
|
||||
|
||||
@@ -55,14 +61,38 @@ public class SplitToWorkspaceController {
|
||||
R.dimen.multi_window_task_divider_size) / 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles widget selection from staged split.
|
||||
* @param view Original widget view
|
||||
* @param pendingIntent Provided by widget via InteractionHandler
|
||||
* @return {@code true} if we can attempt launch the widget into split, {@code false} otherwise
|
||||
* to allow launcher to handle the click
|
||||
*/
|
||||
public boolean handleSecondWidgetSelectionForSplit(View view, PendingIntent pendingIntent) {
|
||||
if (shouldIgnoreSecondSplitLaunch()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Convert original widgetView into bitmap to use for animation
|
||||
// TODO(b/276361926) get the icon for this widget via PackageManager?
|
||||
int width = view.getWidth();
|
||||
int height = view.getHeight();
|
||||
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
|
||||
Canvas canvas = new Canvas(bitmap);
|
||||
view.draw(canvas);
|
||||
|
||||
mController.setSecondTask(pendingIntent);
|
||||
|
||||
startWorkspaceAnimation(view, bitmap, null /*icon*/);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles second app selection from stage split. If the item can't be opened in split or
|
||||
* it's not in stage split state, we pass it onto Launcher's default item click handler.
|
||||
*/
|
||||
public boolean handleSecondAppSelectionForSplit(View view) {
|
||||
if ((!ENABLE_SPLIT_FROM_FULLSCREEN_WITH_KEYBOARD_SHORTCUTS.get()
|
||||
&& !ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get())
|
||||
|| !mController.isSplitSelectActive()) {
|
||||
if (shouldIgnoreSecondSplitLaunch()) {
|
||||
return false;
|
||||
}
|
||||
Object tag = view.getTag();
|
||||
@@ -86,6 +116,12 @@ public class SplitToWorkspaceController {
|
||||
|
||||
mController.setSecondTask(intent, user);
|
||||
|
||||
startWorkspaceAnimation(view, null /*bitmap*/, bitmapInfo.newIcon(mLauncher));
|
||||
return true;
|
||||
}
|
||||
|
||||
private void startWorkspaceAnimation(@NonNull View view, @Nullable Bitmap bitmap,
|
||||
@Nullable Drawable icon) {
|
||||
boolean isTablet = mLauncher.getDeviceProfile().isTablet;
|
||||
SplitAnimationTimings timings = AnimUtils.getDeviceSplitToConfirmTimings(isTablet);
|
||||
PendingAnimation pendingAnimation = new PendingAnimation(timings.getDuration());
|
||||
@@ -107,8 +143,7 @@ public class SplitToWorkspaceController {
|
||||
false /* fadeWithThumbnail */, true /* isStagedTask */);
|
||||
|
||||
FloatingTaskView secondFloatingTaskView = FloatingTaskView.getFloatingTaskView(mLauncher,
|
||||
view, null /* thumbnail */, bitmapInfo.newIcon(mLauncher),
|
||||
secondTaskStartingBounds);
|
||||
view, bitmap, icon, secondTaskStartingBounds);
|
||||
secondFloatingTaskView.setAlpha(1);
|
||||
secondFloatingTaskView.addConfirmAnimation(pendingAnimation, secondTaskStartingBounds,
|
||||
secondTaskEndingBounds, true /* fadeWithThumbnail */, false /* isStagedTask */);
|
||||
@@ -138,6 +173,11 @@ public class SplitToWorkspaceController {
|
||||
}
|
||||
});
|
||||
pendingAnimation.buildAnim().start();
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean shouldIgnoreSecondSplitLaunch() {
|
||||
return (!ENABLE_SPLIT_FROM_FULLSCREEN_WITH_KEYBOARD_SHORTCUTS.get()
|
||||
&& !ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get())
|
||||
|| !mController.isSplitSelectActive();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
package com.android.quickstep.util
|
||||
|
||||
import android.app.ActivityManager
|
||||
import android.app.PendingIntent
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
@@ -32,6 +33,8 @@ import com.android.launcher3.statehandlers.DepthController
|
||||
import com.android.launcher3.statemanager.StateManager
|
||||
import com.android.launcher3.util.ComponentKey
|
||||
import com.android.launcher3.util.SplitConfigurationOptions
|
||||
import com.android.launcher3.util.SplitConfigurationOptions.StagePosition
|
||||
import com.android.launcher3.util.mock
|
||||
import com.android.launcher3.util.withArgCaptor
|
||||
import com.android.quickstep.RecentsModel
|
||||
import com.android.quickstep.SystemUiProxy
|
||||
@@ -59,6 +62,7 @@ class SplitSelectStateControllerTest {
|
||||
@Mock lateinit var handler: Handler
|
||||
@Mock lateinit var context: Context
|
||||
@Mock lateinit var recentsModel: RecentsModel
|
||||
@Mock lateinit var pendingIntent: PendingIntent
|
||||
|
||||
lateinit var splitSelectStateController: SplitSelectStateController
|
||||
|
||||
@@ -348,6 +352,14 @@ class SplitSelectStateControllerTest {
|
||||
assertFalse(splitSelectStateController.isSplitSelectActive)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun secondPendingIntentSet() {
|
||||
val itemInfo = ItemInfo()
|
||||
splitSelectStateController.setInitialTaskSelect(null, 0, itemInfo, null, 1)
|
||||
splitSelectStateController.setSecondTask(pendingIntent)
|
||||
assertTrue(splitSelectStateController.isBothSplitAppsConfirmed)
|
||||
}
|
||||
|
||||
// Generate GroupTask with default userId.
|
||||
private fun generateGroupTask(
|
||||
task1ComponentName: ComponentName,
|
||||
|
||||
Reference in New Issue
Block a user