Merge "Allow floating tasks to be shown as bubbles (behind a sysui flag)" into tm-qpr-dev
This commit is contained in:
@@ -59,6 +59,8 @@ import java.util.concurrent.Executor;
|
|||||||
public class Bubble implements BubbleViewProvider {
|
public class Bubble implements BubbleViewProvider {
|
||||||
private static final String TAG = "Bubble";
|
private static final String TAG = "Bubble";
|
||||||
|
|
||||||
|
public static final String KEY_APP_BUBBLE = "key_app_bubble";
|
||||||
|
|
||||||
private final String mKey;
|
private final String mKey;
|
||||||
@Nullable
|
@Nullable
|
||||||
private final String mGroupKey;
|
private final String mGroupKey;
|
||||||
@@ -163,6 +165,14 @@ public class Bubble implements BubbleViewProvider {
|
|||||||
@Nullable
|
@Nullable
|
||||||
private PendingIntent mDeleteIntent;
|
private PendingIntent mDeleteIntent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used only for a special bubble in the stack that has the key {@link #KEY_APP_BUBBLE}.
|
||||||
|
* There can only be one of these bubbles in the stack and this intent will be populated for
|
||||||
|
* that bubble.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
private Intent mAppIntent;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a bubble with limited information based on given {@link ShortcutInfo}.
|
* Create a bubble with limited information based on given {@link ShortcutInfo}.
|
||||||
* Note: Currently this is only being used when the bubble is persisted to disk.
|
* Note: Currently this is only being used when the bubble is persisted to disk.
|
||||||
@@ -192,6 +202,22 @@ public class Bubble implements BubbleViewProvider {
|
|||||||
mBubbleMetadataFlagListener = listener;
|
mBubbleMetadataFlagListener = listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Bubble(Intent intent,
|
||||||
|
UserHandle user,
|
||||||
|
Executor mainExecutor) {
|
||||||
|
mKey = KEY_APP_BUBBLE;
|
||||||
|
mGroupKey = null;
|
||||||
|
mLocusId = null;
|
||||||
|
mFlags = 0;
|
||||||
|
mUser = user;
|
||||||
|
mShowBubbleUpdateDot = false;
|
||||||
|
mMainExecutor = mainExecutor;
|
||||||
|
mTaskId = INVALID_TASK_ID;
|
||||||
|
mAppIntent = intent;
|
||||||
|
mDesiredHeight = Integer.MAX_VALUE;
|
||||||
|
mPackageName = intent.getPackage();
|
||||||
|
}
|
||||||
|
|
||||||
@VisibleForTesting(visibility = PRIVATE)
|
@VisibleForTesting(visibility = PRIVATE)
|
||||||
public Bubble(@NonNull final BubbleEntry entry,
|
public Bubble(@NonNull final BubbleEntry entry,
|
||||||
final Bubbles.BubbleMetadataFlagListener listener,
|
final Bubbles.BubbleMetadataFlagListener listener,
|
||||||
@@ -417,6 +443,9 @@ public class Bubble implements BubbleViewProvider {
|
|||||||
|
|
||||||
mShortcutInfo = info.shortcutInfo;
|
mShortcutInfo = info.shortcutInfo;
|
||||||
mAppName = info.appName;
|
mAppName = info.appName;
|
||||||
|
if (mTitle == null) {
|
||||||
|
mTitle = mAppName;
|
||||||
|
}
|
||||||
mFlyoutMessage = info.flyoutMessage;
|
mFlyoutMessage = info.flyoutMessage;
|
||||||
|
|
||||||
mBadgeBitmap = info.badgeBitmap;
|
mBadgeBitmap = info.badgeBitmap;
|
||||||
@@ -520,7 +549,7 @@ public class Bubble implements BubbleViewProvider {
|
|||||||
* @return the last time this bubble was updated or accessed, whichever is most recent.
|
* @return the last time this bubble was updated or accessed, whichever is most recent.
|
||||||
*/
|
*/
|
||||||
long getLastActivity() {
|
long getLastActivity() {
|
||||||
return Math.max(mLastUpdated, mLastAccessed);
|
return isAppBubble() ? Long.MAX_VALUE : Math.max(mLastUpdated, mLastAccessed);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -719,6 +748,15 @@ public class Bubble implements BubbleViewProvider {
|
|||||||
return mDeleteIntent;
|
return mDeleteIntent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
Intent getAppBubbleIntent() {
|
||||||
|
return mAppIntent;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isAppBubble() {
|
||||||
|
return KEY_APP_BUBBLE.equals(mKey);
|
||||||
|
}
|
||||||
|
|
||||||
Intent getSettingsIntent(final Context context) {
|
Intent getSettingsIntent(final Context context) {
|
||||||
final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_BUBBLE_SETTINGS);
|
final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_BUBBLE_SETTINGS);
|
||||||
intent.putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName());
|
intent.putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName());
|
||||||
|
|||||||
@@ -1016,6 +1016,20 @@ public class BubbleController implements ConfigurationChangeListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a bubble for a specific intent. These bubbles are <b>not</b> backed by a notification
|
||||||
|
* and remain until the user dismisses the bubble or bubble stack. Only one intent bubble
|
||||||
|
* is supported at a time.
|
||||||
|
*
|
||||||
|
* @param intent the intent to display in the bubble expanded view.
|
||||||
|
*/
|
||||||
|
public void addAppBubble(Intent intent) {
|
||||||
|
if (intent == null || intent.getPackage() == null) return;
|
||||||
|
Bubble b = new Bubble(intent, UserHandle.of(mCurrentUserId), mMainExecutor);
|
||||||
|
b.setShouldAutoExpand(true);
|
||||||
|
inflateAndAdd(b, /* suppressFlyout= */ true, /* showInShade= */ false);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fills the overflow bubbles by loading them from disk.
|
* Fills the overflow bubbles by loading them from disk.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -224,15 +224,23 @@ public class BubbleExpandedView extends LinearLayout {
|
|||||||
try {
|
try {
|
||||||
options.setTaskAlwaysOnTop(true);
|
options.setTaskAlwaysOnTop(true);
|
||||||
options.setLaunchedFromBubble(true);
|
options.setLaunchedFromBubble(true);
|
||||||
if (!mIsOverflow && mBubble.hasMetadataShortcutId()) {
|
|
||||||
|
Intent fillInIntent = new Intent();
|
||||||
|
// Apply flags to make behaviour match documentLaunchMode=always.
|
||||||
|
fillInIntent.addFlags(FLAG_ACTIVITY_NEW_DOCUMENT);
|
||||||
|
fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
|
||||||
|
|
||||||
|
if (mBubble.isAppBubble()) {
|
||||||
|
PendingIntent pi = PendingIntent.getActivity(mContext, 0,
|
||||||
|
mBubble.getAppBubbleIntent(),
|
||||||
|
PendingIntent.FLAG_MUTABLE,
|
||||||
|
null);
|
||||||
|
mTaskView.startActivity(pi, fillInIntent, options, launchBounds);
|
||||||
|
} else if (!mIsOverflow && mBubble.hasMetadataShortcutId()) {
|
||||||
options.setApplyActivityFlagsForBubbles(true);
|
options.setApplyActivityFlagsForBubbles(true);
|
||||||
mTaskView.startShortcutActivity(mBubble.getShortcutInfo(),
|
mTaskView.startShortcutActivity(mBubble.getShortcutInfo(),
|
||||||
options, launchBounds);
|
options, launchBounds);
|
||||||
} else {
|
} else {
|
||||||
Intent fillInIntent = new Intent();
|
|
||||||
// Apply flags to make behaviour match documentLaunchMode=always.
|
|
||||||
fillInIntent.addFlags(FLAG_ACTIVITY_NEW_DOCUMENT);
|
|
||||||
fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
|
|
||||||
if (mBubble != null) {
|
if (mBubble != null) {
|
||||||
mBubble.setIntentActive();
|
mBubble.setIntentActive();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -591,6 +591,7 @@ public abstract class WMShellBaseModule {
|
|||||||
ShellInit shellInit,
|
ShellInit shellInit,
|
||||||
ShellController shellController,
|
ShellController shellController,
|
||||||
ShellCommandHandler shellCommandHandler,
|
ShellCommandHandler shellCommandHandler,
|
||||||
|
Optional<BubbleController> bubbleController,
|
||||||
WindowManager windowManager,
|
WindowManager windowManager,
|
||||||
ShellTaskOrganizer organizer,
|
ShellTaskOrganizer organizer,
|
||||||
TaskViewTransitions taskViewTransitions,
|
TaskViewTransitions taskViewTransitions,
|
||||||
@@ -602,6 +603,7 @@ public abstract class WMShellBaseModule {
|
|||||||
shellInit,
|
shellInit,
|
||||||
shellController,
|
shellController,
|
||||||
shellCommandHandler,
|
shellCommandHandler,
|
||||||
|
bubbleController,
|
||||||
windowManager,
|
windowManager,
|
||||||
organizer,
|
organizer,
|
||||||
taskViewTransitions,
|
taskViewTransitions,
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ import androidx.annotation.VisibleForTesting;
|
|||||||
import com.android.internal.protolog.common.ProtoLog;
|
import com.android.internal.protolog.common.ProtoLog;
|
||||||
import com.android.wm.shell.ShellTaskOrganizer;
|
import com.android.wm.shell.ShellTaskOrganizer;
|
||||||
import com.android.wm.shell.TaskViewTransitions;
|
import com.android.wm.shell.TaskViewTransitions;
|
||||||
|
import com.android.wm.shell.bubbles.BubbleController;
|
||||||
import com.android.wm.shell.common.RemoteCallable;
|
import com.android.wm.shell.common.RemoteCallable;
|
||||||
import com.android.wm.shell.common.ShellExecutor;
|
import com.android.wm.shell.common.ShellExecutor;
|
||||||
import com.android.wm.shell.common.SyncTransactionQueue;
|
import com.android.wm.shell.common.SyncTransactionQueue;
|
||||||
@@ -54,6 +55,7 @@ import com.android.wm.shell.sysui.ShellInit;
|
|||||||
|
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Entry point for creating and managing floating tasks.
|
* Entry point for creating and managing floating tasks.
|
||||||
@@ -70,6 +72,8 @@ public class FloatingTasksController implements RemoteCallable<FloatingTasksCont
|
|||||||
|
|
||||||
public static final boolean FLOATING_TASKS_ENABLED =
|
public static final boolean FLOATING_TASKS_ENABLED =
|
||||||
SystemProperties.getBoolean("persist.wm.debug.floating_tasks", false);
|
SystemProperties.getBoolean("persist.wm.debug.floating_tasks", false);
|
||||||
|
public static final boolean SHOW_FLOATING_TASKS_AS_BUBBLES =
|
||||||
|
SystemProperties.getBoolean("persist.wm.debug.floating_tasks_as_bubbles", false);
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
static final int SMALLEST_SCREEN_WIDTH_DP_TO_BE_TABLET = 600;
|
static final int SMALLEST_SCREEN_WIDTH_DP_TO_BE_TABLET = 600;
|
||||||
@@ -82,6 +86,7 @@ public class FloatingTasksController implements RemoteCallable<FloatingTasksCont
|
|||||||
private Context mContext;
|
private Context mContext;
|
||||||
private ShellController mShellController;
|
private ShellController mShellController;
|
||||||
private ShellCommandHandler mShellCommandHandler;
|
private ShellCommandHandler mShellCommandHandler;
|
||||||
|
private @Nullable BubbleController mBubbleController;
|
||||||
private WindowManager mWindowManager;
|
private WindowManager mWindowManager;
|
||||||
private ShellTaskOrganizer mTaskOrganizer;
|
private ShellTaskOrganizer mTaskOrganizer;
|
||||||
private TaskViewTransitions mTaskViewTransitions;
|
private TaskViewTransitions mTaskViewTransitions;
|
||||||
@@ -111,6 +116,7 @@ public class FloatingTasksController implements RemoteCallable<FloatingTasksCont
|
|||||||
ShellInit shellInit,
|
ShellInit shellInit,
|
||||||
ShellController shellController,
|
ShellController shellController,
|
||||||
ShellCommandHandler shellCommandHandler,
|
ShellCommandHandler shellCommandHandler,
|
||||||
|
Optional<BubbleController> bubbleController,
|
||||||
WindowManager windowManager,
|
WindowManager windowManager,
|
||||||
ShellTaskOrganizer organizer,
|
ShellTaskOrganizer organizer,
|
||||||
TaskViewTransitions transitions,
|
TaskViewTransitions transitions,
|
||||||
@@ -120,6 +126,7 @@ public class FloatingTasksController implements RemoteCallable<FloatingTasksCont
|
|||||||
mContext = context;
|
mContext = context;
|
||||||
mShellController = shellController;
|
mShellController = shellController;
|
||||||
mShellCommandHandler = shellCommandHandler;
|
mShellCommandHandler = shellCommandHandler;
|
||||||
|
mBubbleController = bubbleController.get();
|
||||||
mWindowManager = windowManager;
|
mWindowManager = windowManager;
|
||||||
mTaskOrganizer = organizer;
|
mTaskOrganizer = organizer;
|
||||||
mTaskViewTransitions = transitions;
|
mTaskViewTransitions = transitions;
|
||||||
@@ -187,6 +194,22 @@ public class FloatingTasksController implements RemoteCallable<FloatingTasksCont
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Returns true if the task was or should be shown as a bubble. */
|
||||||
|
private boolean maybeShowTaskAsBubble(Intent intent) {
|
||||||
|
if (SHOW_FLOATING_TASKS_AS_BUBBLES && mBubbleController != null) {
|
||||||
|
removeFloatingLayer();
|
||||||
|
if (intent.getPackage() != null) {
|
||||||
|
mBubbleController.addAppBubble(intent);
|
||||||
|
ProtoLog.d(WM_SHELL_FLOATING_APPS, "showing floating task as bubble: %s", intent);
|
||||||
|
} else {
|
||||||
|
ProtoLog.d(WM_SHELL_FLOATING_APPS,
|
||||||
|
"failed to show floating task as bubble: %s; unknown package", intent);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shows, stashes, or un-stashes the floating task depending on state:
|
* Shows, stashes, or un-stashes the floating task depending on state:
|
||||||
* - If there is no floating task for this intent, it shows this the provided task.
|
* - If there is no floating task for this intent, it shows this the provided task.
|
||||||
@@ -195,6 +218,7 @@ public class FloatingTasksController implements RemoteCallable<FloatingTasksCont
|
|||||||
*/
|
*/
|
||||||
public void showOrSetStashed(Intent intent) {
|
public void showOrSetStashed(Intent intent) {
|
||||||
if (!canShowTask(intent)) return;
|
if (!canShowTask(intent)) return;
|
||||||
|
if (maybeShowTaskAsBubble(intent)) return;
|
||||||
|
|
||||||
addFloatingLayer();
|
addFloatingLayer();
|
||||||
|
|
||||||
@@ -215,6 +239,7 @@ public class FloatingTasksController implements RemoteCallable<FloatingTasksCont
|
|||||||
*/
|
*/
|
||||||
public void showTask(Intent intent) {
|
public void showTask(Intent intent) {
|
||||||
if (!canShowTask(intent)) return;
|
if (!canShowTask(intent)) return;
|
||||||
|
if (maybeShowTaskAsBubble(intent)) return;
|
||||||
|
|
||||||
addFloatingLayer();
|
addFloatingLayer();
|
||||||
|
|
||||||
|
|||||||
@@ -62,6 +62,8 @@ import org.mockito.Captor;
|
|||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.MockitoAnnotations;
|
import org.mockito.MockitoAnnotations;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for the floating tasks controller.
|
* Tests for the floating tasks controller.
|
||||||
*/
|
*/
|
||||||
@@ -123,6 +125,7 @@ public class FloatingTasksControllerTest extends ShellTestCase {
|
|||||||
mShellInit,
|
mShellInit,
|
||||||
mShellController,
|
mShellController,
|
||||||
mock(ShellCommandHandler.class),
|
mock(ShellCommandHandler.class),
|
||||||
|
Optional.empty(),
|
||||||
mWindowManager,
|
mWindowManager,
|
||||||
mTaskOrganizer,
|
mTaskOrganizer,
|
||||||
mock(TaskViewTransitions.class),
|
mock(TaskViewTransitions.class),
|
||||||
|
|||||||
@@ -238,6 +238,10 @@ public class Flags {
|
|||||||
public static final SysPropBooleanFlag FLOATING_TASKS_ENABLED =
|
public static final SysPropBooleanFlag FLOATING_TASKS_ENABLED =
|
||||||
new SysPropBooleanFlag(1106, "persist.wm.debug.floating_tasks", false);
|
new SysPropBooleanFlag(1106, "persist.wm.debug.floating_tasks", false);
|
||||||
|
|
||||||
|
@Keep
|
||||||
|
public static final SysPropBooleanFlag SHOW_FLOATING_TASKS_AS_BUBBLES =
|
||||||
|
new SysPropBooleanFlag(1107, "persist.wm.debug.floating_tasks_as_bubbles", false);
|
||||||
|
|
||||||
// 1200 - predictive back
|
// 1200 - predictive back
|
||||||
@Keep
|
@Keep
|
||||||
public static final SysPropBooleanFlag WM_ENABLE_PREDICTIVE_BACK = new SysPropBooleanFlag(
|
public static final SysPropBooleanFlag WM_ENABLE_PREDICTIVE_BACK = new SysPropBooleanFlag(
|
||||||
|
|||||||
Reference in New Issue
Block a user