Merge changes I8beec37e,I0955772d into udc-dev
* changes: Create BubbleTaskViewHelper to be shared with new bubble expanded view Create interface and data to pass bubble updates to launcher
This commit is contained in:
@@ -46,6 +46,8 @@ filegroup {
|
|||||||
"src/com/android/wm/shell/common/split/SplitScreenConstants.java",
|
"src/com/android/wm/shell/common/split/SplitScreenConstants.java",
|
||||||
"src/com/android/wm/shell/sysui/ShellSharedConstants.java",
|
"src/com/android/wm/shell/sysui/ShellSharedConstants.java",
|
||||||
"src/com/android/wm/shell/common/TransactionPool.java",
|
"src/com/android/wm/shell/common/TransactionPool.java",
|
||||||
|
"src/com/android/wm/shell/common/bubbles/*.java",
|
||||||
|
"src/com/android/wm/shell/common/TriangleShape.java",
|
||||||
"src/com/android/wm/shell/animation/Interpolators.java",
|
"src/com/android/wm/shell/animation/Interpolators.java",
|
||||||
"src/com/android/wm/shell/pip/PipContentOverlay.java",
|
"src/com/android/wm/shell/pip/PipContentOverlay.java",
|
||||||
"src/com/android/wm/shell/startingsurface/SplashScreenExitAnimationUtils.java",
|
"src/com/android/wm/shell/startingsurface/SplashScreenExitAnimationUtils.java",
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ import android.util.Log;
|
|||||||
|
|
||||||
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
import com.android.internal.logging.InstanceId;
|
import com.android.internal.logging.InstanceId;
|
||||||
|
import com.android.wm.shell.common.bubbles.BubbleInfo;
|
||||||
|
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -244,6 +245,16 @@ public class Bubble implements BubbleViewProvider {
|
|||||||
setEntry(entry);
|
setEntry(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Converts this bubble into a {@link BubbleInfo} object to be shared with external callers. */
|
||||||
|
public BubbleInfo asBubbleBarBubble() {
|
||||||
|
return new BubbleInfo(getKey(),
|
||||||
|
getFlags(),
|
||||||
|
getShortcutInfo().getId(),
|
||||||
|
getIcon(),
|
||||||
|
getUser().getIdentifier(),
|
||||||
|
getPackageName());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getKey() {
|
public String getKey() {
|
||||||
return mKey;
|
return mKey;
|
||||||
@@ -545,8 +556,13 @@ public class Bubble implements BubbleViewProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the icon set on BubbleMetadata, if it exists. This is only non-null for bubbles
|
||||||
|
* created via a PendingIntent. This is null for bubbles created by a shortcut, as we use the
|
||||||
|
* icon from the shortcut.
|
||||||
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
Icon getIcon() {
|
public Icon getIcon() {
|
||||||
return mIcon;
|
return mIcon;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,9 @@ import static com.android.wm.shell.bubbles.Bubbles.DISMISS_NO_LONGER_BUBBLE;
|
|||||||
import static com.android.wm.shell.bubbles.Bubbles.DISMISS_PACKAGE_REMOVED;
|
import static com.android.wm.shell.bubbles.Bubbles.DISMISS_PACKAGE_REMOVED;
|
||||||
import static com.android.wm.shell.bubbles.Bubbles.DISMISS_SHORTCUT_REMOVED;
|
import static com.android.wm.shell.bubbles.Bubbles.DISMISS_SHORTCUT_REMOVED;
|
||||||
import static com.android.wm.shell.bubbles.Bubbles.DISMISS_USER_CHANGED;
|
import static com.android.wm.shell.bubbles.Bubbles.DISMISS_USER_CHANGED;
|
||||||
|
import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_BUBBLES;
|
||||||
|
|
||||||
|
import android.annotation.BinderThread;
|
||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
import android.annotation.UserIdInt;
|
import android.annotation.UserIdInt;
|
||||||
import android.app.ActivityManager;
|
import android.app.ActivityManager;
|
||||||
@@ -59,6 +61,7 @@ import android.graphics.PixelFormat;
|
|||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.graphics.drawable.Icon;
|
import android.graphics.drawable.Icon;
|
||||||
import android.os.Binder;
|
import android.os.Binder;
|
||||||
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.os.ServiceManager;
|
import android.os.ServiceManager;
|
||||||
@@ -88,13 +91,17 @@ import com.android.wm.shell.ShellTaskOrganizer;
|
|||||||
import com.android.wm.shell.TaskViewTransitions;
|
import com.android.wm.shell.TaskViewTransitions;
|
||||||
import com.android.wm.shell.WindowManagerShellWrapper;
|
import com.android.wm.shell.WindowManagerShellWrapper;
|
||||||
import com.android.wm.shell.common.DisplayController;
|
import com.android.wm.shell.common.DisplayController;
|
||||||
|
import com.android.wm.shell.common.ExternalInterfaceBinder;
|
||||||
import com.android.wm.shell.common.FloatingContentCoordinator;
|
import com.android.wm.shell.common.FloatingContentCoordinator;
|
||||||
|
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.SingleInstanceRemoteListener;
|
||||||
import com.android.wm.shell.common.SyncTransactionQueue;
|
import com.android.wm.shell.common.SyncTransactionQueue;
|
||||||
import com.android.wm.shell.common.TaskStackListenerCallback;
|
import com.android.wm.shell.common.TaskStackListenerCallback;
|
||||||
import com.android.wm.shell.common.TaskStackListenerImpl;
|
import com.android.wm.shell.common.TaskStackListenerImpl;
|
||||||
import com.android.wm.shell.common.annotations.ShellBackgroundThread;
|
import com.android.wm.shell.common.annotations.ShellBackgroundThread;
|
||||||
import com.android.wm.shell.common.annotations.ShellMainThread;
|
import com.android.wm.shell.common.annotations.ShellMainThread;
|
||||||
|
import com.android.wm.shell.common.bubbles.BubbleBarUpdate;
|
||||||
import com.android.wm.shell.draganddrop.DragAndDropController;
|
import com.android.wm.shell.draganddrop.DragAndDropController;
|
||||||
import com.android.wm.shell.onehanded.OneHandedController;
|
import com.android.wm.shell.onehanded.OneHandedController;
|
||||||
import com.android.wm.shell.onehanded.OneHandedTransitionCallback;
|
import com.android.wm.shell.onehanded.OneHandedTransitionCallback;
|
||||||
@@ -123,7 +130,8 @@ import java.util.function.IntConsumer;
|
|||||||
*
|
*
|
||||||
* The controller manages addition, removal, and visible state of bubbles on screen.
|
* The controller manages addition, removal, and visible state of bubbles on screen.
|
||||||
*/
|
*/
|
||||||
public class BubbleController implements ConfigurationChangeListener {
|
public class BubbleController implements ConfigurationChangeListener,
|
||||||
|
RemoteCallable<BubbleController> {
|
||||||
|
|
||||||
private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleController" : TAG_BUBBLES;
|
private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleController" : TAG_BUBBLES;
|
||||||
|
|
||||||
@@ -248,6 +256,8 @@ public class BubbleController implements ConfigurationChangeListener {
|
|||||||
private Optional<OneHandedController> mOneHandedOptional;
|
private Optional<OneHandedController> mOneHandedOptional;
|
||||||
/** Drag and drop controller to register listener for onDragStarted. */
|
/** Drag and drop controller to register listener for onDragStarted. */
|
||||||
private DragAndDropController mDragAndDropController;
|
private DragAndDropController mDragAndDropController;
|
||||||
|
/** Used to send bubble events to launcher. */
|
||||||
|
private Bubbles.BubbleStateListener mBubbleStateListener;
|
||||||
|
|
||||||
public BubbleController(Context context,
|
public BubbleController(Context context,
|
||||||
ShellInit shellInit,
|
ShellInit shellInit,
|
||||||
@@ -458,9 +468,15 @@ public class BubbleController implements ConfigurationChangeListener {
|
|||||||
mCurrentProfiles = userProfiles;
|
mCurrentProfiles = userProfiles;
|
||||||
|
|
||||||
mShellController.addConfigurationChangeListener(this);
|
mShellController.addConfigurationChangeListener(this);
|
||||||
|
mShellController.addExternalInterface(KEY_EXTRA_SHELL_BUBBLES,
|
||||||
|
this::createExternalInterface, this);
|
||||||
mShellCommandHandler.addDumpCallback(this::dump, this);
|
mShellCommandHandler.addDumpCallback(this::dump, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ExternalInterfaceBinder createExternalInterface() {
|
||||||
|
return new BubbleController.IBubblesImpl(this);
|
||||||
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
public Bubbles asBubbles() {
|
public Bubbles asBubbles() {
|
||||||
return mImpl;
|
return mImpl;
|
||||||
@@ -475,6 +491,48 @@ public class BubbleController implements ConfigurationChangeListener {
|
|||||||
return mMainExecutor;
|
return mMainExecutor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Context getContext() {
|
||||||
|
return mContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ShellExecutor getRemoteCallExecutor() {
|
||||||
|
return mMainExecutor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a listener to be notified of bubble updates. This is used by launcher so that
|
||||||
|
* it may render bubbles in itself. Only one listener is supported.
|
||||||
|
*/
|
||||||
|
public void registerBubbleStateListener(Bubbles.BubbleStateListener listener) {
|
||||||
|
if (isShowingAsBubbleBar()) {
|
||||||
|
// Only set the listener if bubble bar is showing.
|
||||||
|
mBubbleStateListener = listener;
|
||||||
|
sendInitialListenerUpdate();
|
||||||
|
} else {
|
||||||
|
mBubbleStateListener = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregisters the {@link Bubbles.BubbleStateListener}.
|
||||||
|
*/
|
||||||
|
public void unregisterBubbleStateListener() {
|
||||||
|
mBubbleStateListener = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If a {@link Bubbles.BubbleStateListener} is present, this will send the current bubble
|
||||||
|
* state to it.
|
||||||
|
*/
|
||||||
|
private void sendInitialListenerUpdate() {
|
||||||
|
if (mBubbleStateListener != null) {
|
||||||
|
BubbleBarUpdate update = mBubbleData.getInitialStateForBubbleBar();
|
||||||
|
mBubbleStateListener.onBubbleStateChange(update);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hides the current input method, wherever it may be focused, via InputMethodManagerInternal.
|
* Hides the current input method, wherever it may be focused, via InputMethodManagerInternal.
|
||||||
*/
|
*/
|
||||||
@@ -1722,6 +1780,73 @@ public class BubbleController implements ConfigurationChangeListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The interface for calls from outside the host process.
|
||||||
|
*/
|
||||||
|
@BinderThread
|
||||||
|
private class IBubblesImpl extends IBubbles.Stub implements ExternalInterfaceBinder {
|
||||||
|
private BubbleController mController;
|
||||||
|
private final SingleInstanceRemoteListener<BubbleController, IBubblesListener> mListener;
|
||||||
|
private final Bubbles.BubbleStateListener mBubbleListener =
|
||||||
|
new Bubbles.BubbleStateListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBubbleStateChange(BubbleBarUpdate update) {
|
||||||
|
Bundle b = new Bundle();
|
||||||
|
b.setClassLoader(BubbleBarUpdate.class.getClassLoader());
|
||||||
|
b.putParcelable(BubbleBarUpdate.BUNDLE_KEY, update);
|
||||||
|
mListener.call(l -> l.onBubbleStateChange(b));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
IBubblesImpl(BubbleController controller) {
|
||||||
|
mController = controller;
|
||||||
|
mListener = new SingleInstanceRemoteListener<>(mController,
|
||||||
|
c -> c.registerBubbleStateListener(mBubbleListener),
|
||||||
|
c -> c.unregisterBubbleStateListener());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invalidates this instance, preventing future calls from updating the controller.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void invalidate() {
|
||||||
|
mController = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void registerBubbleListener(IBubblesListener listener) {
|
||||||
|
mMainExecutor.execute(() -> {
|
||||||
|
mListener.register(listener);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void unregisterBubbleListener(IBubblesListener listener) {
|
||||||
|
mMainExecutor.execute(() -> mListener.unregister());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void showBubble(String key, boolean onLauncherHome) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeBubble(String key, int reason) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void collapseBubbles() {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTaskbarStateChanged(int newState) {
|
||||||
|
// TODO (b/269670598)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private class BubblesImpl implements Bubbles {
|
private class BubblesImpl implements Bubbles {
|
||||||
// Up-to-date cached state of bubbles data for SysUI to query from the calling thread
|
// Up-to-date cached state of bubbles data for SysUI to query from the calling thread
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
@@ -1835,6 +1960,17 @@ public class BubbleController implements ConfigurationChangeListener {
|
|||||||
|
|
||||||
private CachedState mCachedState = new CachedState();
|
private CachedState mCachedState = new CachedState();
|
||||||
|
|
||||||
|
private IBubblesImpl mIBubbles;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IBubbles createExternalInterface() {
|
||||||
|
if (mIBubbles != null) {
|
||||||
|
mIBubbles.invalidate();
|
||||||
|
}
|
||||||
|
mIBubbles = new IBubblesImpl(BubbleController.this);
|
||||||
|
return mIBubbles;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isBubbleNotificationSuppressedFromShade(String key, String groupKey) {
|
public boolean isBubbleNotificationSuppressedFromShade(String key, String groupKey) {
|
||||||
return mCachedState.isBubbleNotificationSuppressedFromShade(key, groupKey);
|
return mCachedState.isBubbleNotificationSuppressedFromShade(key, groupKey);
|
||||||
|
|||||||
@@ -40,6 +40,8 @@ import com.android.internal.annotations.VisibleForTesting;
|
|||||||
import com.android.internal.util.FrameworkStatsLog;
|
import com.android.internal.util.FrameworkStatsLog;
|
||||||
import com.android.wm.shell.R;
|
import com.android.wm.shell.R;
|
||||||
import com.android.wm.shell.bubbles.Bubbles.DismissReason;
|
import com.android.wm.shell.bubbles.Bubbles.DismissReason;
|
||||||
|
import com.android.wm.shell.common.bubbles.BubbleBarUpdate;
|
||||||
|
import com.android.wm.shell.common.bubbles.RemovedBubble;
|
||||||
|
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -113,6 +115,61 @@ public class BubbleData {
|
|||||||
void bubbleRemoved(Bubble bubbleToRemove, @DismissReason int reason) {
|
void bubbleRemoved(Bubble bubbleToRemove, @DismissReason int reason) {
|
||||||
removedBubbles.add(new Pair<>(bubbleToRemove, reason));
|
removedBubbles.add(new Pair<>(bubbleToRemove, reason));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the update to a {@link BubbleBarUpdate} which contains updates relevant
|
||||||
|
* to the bubble bar. Only used when {@link BubbleController#isShowingAsBubbleBar()} is
|
||||||
|
* true.
|
||||||
|
*/
|
||||||
|
BubbleBarUpdate toBubbleBarUpdate() {
|
||||||
|
BubbleBarUpdate bubbleBarUpdate = new BubbleBarUpdate();
|
||||||
|
|
||||||
|
bubbleBarUpdate.expandedChanged = expandedChanged;
|
||||||
|
bubbleBarUpdate.expanded = expanded;
|
||||||
|
if (selectionChanged) {
|
||||||
|
bubbleBarUpdate.selectedBubbleKey = selectedBubble != null
|
||||||
|
? selectedBubble.getKey()
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
bubbleBarUpdate.addedBubble = addedBubble != null
|
||||||
|
? addedBubble.asBubbleBarBubble()
|
||||||
|
: null;
|
||||||
|
// TODO(b/269670235): We need to handle updates better, I think for the bubble bar only
|
||||||
|
// certain updates need to be sent instead of any updatedBubble.
|
||||||
|
bubbleBarUpdate.updatedBubble = updatedBubble != null
|
||||||
|
? updatedBubble.asBubbleBarBubble()
|
||||||
|
: null;
|
||||||
|
bubbleBarUpdate.suppressedBubbleKey = suppressedBubble != null
|
||||||
|
? suppressedBubble.getKey()
|
||||||
|
: null;
|
||||||
|
bubbleBarUpdate.unsupressedBubbleKey = unsuppressedBubble != null
|
||||||
|
? unsuppressedBubble.getKey()
|
||||||
|
: null;
|
||||||
|
for (int i = 0; i < removedBubbles.size(); i++) {
|
||||||
|
Pair<Bubble, Integer> pair = removedBubbles.get(i);
|
||||||
|
bubbleBarUpdate.removedBubbles.add(
|
||||||
|
new RemovedBubble(pair.first.getKey(), pair.second));
|
||||||
|
}
|
||||||
|
if (orderChanged) {
|
||||||
|
// Include the new order
|
||||||
|
for (int i = 0; i < bubbles.size(); i++) {
|
||||||
|
bubbleBarUpdate.bubbleKeysInOrder.add(bubbles.get(i).getKey());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bubbleBarUpdate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the current state of active bubbles and populates the update with that. Only
|
||||||
|
* used when {@link BubbleController#isShowingAsBubbleBar()} is true.
|
||||||
|
*/
|
||||||
|
BubbleBarUpdate getInitialState() {
|
||||||
|
BubbleBarUpdate bubbleBarUpdate = new BubbleBarUpdate();
|
||||||
|
for (int i = 0; i < bubbles.size(); i++) {
|
||||||
|
bubbleBarUpdate.currentBubbleList.add(bubbles.get(i).asBubbleBarBubble());
|
||||||
|
}
|
||||||
|
return bubbleBarUpdate;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -190,6 +247,13 @@ public class BubbleData {
|
|||||||
mMaxOverflowBubbles = mContext.getResources().getInteger(R.integer.bubbles_max_overflow);
|
mMaxOverflowBubbles = mContext.getResources().getInteger(R.integer.bubbles_max_overflow);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a bubble bar update populated with the current list of active bubbles.
|
||||||
|
*/
|
||||||
|
public BubbleBarUpdate getInitialStateForBubbleBar() {
|
||||||
|
return mStateChange.getInitialState();
|
||||||
|
}
|
||||||
|
|
||||||
public void setSuppressionChangedListener(Bubbles.BubbleMetadataFlagListener listener) {
|
public void setSuppressionChangedListener(Bubbles.BubbleMetadataFlagListener listener) {
|
||||||
mBubbleMetadataFlagListener = listener;
|
mBubbleMetadataFlagListener = listener;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,282 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 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.wm.shell.bubbles;
|
||||||
|
|
||||||
|
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
|
||||||
|
import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
|
||||||
|
import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
|
||||||
|
|
||||||
|
import static com.android.wm.shell.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_EXPANDED_VIEW;
|
||||||
|
|
||||||
|
import android.app.ActivityOptions;
|
||||||
|
import android.app.ActivityTaskManager;
|
||||||
|
import android.app.PendingIntent;
|
||||||
|
import android.content.ComponentName;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.graphics.Rect;
|
||||||
|
import android.os.RemoteException;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import com.android.wm.shell.TaskView;
|
||||||
|
import com.android.wm.shell.TaskViewTaskController;
|
||||||
|
import com.android.wm.shell.common.ShellExecutor;
|
||||||
|
import com.android.wm.shell.common.annotations.ShellMainThread;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles creating and updating the {@link TaskView} associated with a {@link Bubble}.
|
||||||
|
*/
|
||||||
|
public class BubbleTaskViewHelper {
|
||||||
|
|
||||||
|
private static final String TAG = BubbleTaskViewHelper.class.getSimpleName();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listener for users of {@link BubbleTaskViewHelper} to use to be notified of events
|
||||||
|
* on the task.
|
||||||
|
*/
|
||||||
|
public interface Listener {
|
||||||
|
|
||||||
|
/** Called when the task is first created. */
|
||||||
|
void onTaskCreated();
|
||||||
|
|
||||||
|
/** Called when the visibility of the task changes. */
|
||||||
|
void onContentVisibilityChanged(boolean visible);
|
||||||
|
|
||||||
|
/** Called when back is pressed on the task root. */
|
||||||
|
void onBackPressed();
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Context mContext;
|
||||||
|
private final BubbleController mController;
|
||||||
|
private final @ShellMainThread ShellExecutor mMainExecutor;
|
||||||
|
private final BubbleTaskViewHelper.Listener mListener;
|
||||||
|
private final View mParentView;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private Bubble mBubble;
|
||||||
|
@Nullable
|
||||||
|
private PendingIntent mPendingIntent;
|
||||||
|
private TaskViewTaskController mTaskViewTaskController;
|
||||||
|
@Nullable
|
||||||
|
private TaskView mTaskView;
|
||||||
|
private int mTaskId = INVALID_TASK_ID;
|
||||||
|
|
||||||
|
private final TaskView.Listener mTaskViewListener = new TaskView.Listener() {
|
||||||
|
private boolean mInitialized = false;
|
||||||
|
private boolean mDestroyed = false;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onInitialized() {
|
||||||
|
if (DEBUG_BUBBLE_EXPANDED_VIEW) {
|
||||||
|
Log.d(TAG, "onInitialized: destroyed=" + mDestroyed
|
||||||
|
+ " initialized=" + mInitialized
|
||||||
|
+ " bubble=" + getBubbleKey());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mDestroyed || mInitialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Custom options so there is no activity transition animation
|
||||||
|
ActivityOptions options = ActivityOptions.makeCustomAnimation(mContext,
|
||||||
|
0 /* enterResId */, 0 /* exitResId */);
|
||||||
|
|
||||||
|
Rect launchBounds = new Rect();
|
||||||
|
mTaskView.getBoundsOnScreen(launchBounds);
|
||||||
|
|
||||||
|
// TODO: I notice inconsistencies in lifecycle
|
||||||
|
// Post to keep the lifecycle normal
|
||||||
|
mParentView.post(() -> {
|
||||||
|
if (DEBUG_BUBBLE_EXPANDED_VIEW) {
|
||||||
|
Log.d(TAG, "onInitialized: calling startActivity, bubble="
|
||||||
|
+ getBubbleKey());
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
options.setTaskAlwaysOnTop(true);
|
||||||
|
options.setLaunchedFromBubble(true);
|
||||||
|
|
||||||
|
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 (mBubble.hasMetadataShortcutId()) {
|
||||||
|
options.setApplyActivityFlagsForBubbles(true);
|
||||||
|
mTaskView.startShortcutActivity(mBubble.getShortcutInfo(),
|
||||||
|
options, launchBounds);
|
||||||
|
} else {
|
||||||
|
if (mBubble != null) {
|
||||||
|
mBubble.setIntentActive();
|
||||||
|
}
|
||||||
|
mTaskView.startActivity(mPendingIntent, fillInIntent, options,
|
||||||
|
launchBounds);
|
||||||
|
}
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
// If there's a runtime exception here then there's something
|
||||||
|
// wrong with the intent, we can't really recover / try to populate
|
||||||
|
// the bubble again so we'll just remove it.
|
||||||
|
Log.w(TAG, "Exception while displaying bubble: " + getBubbleKey()
|
||||||
|
+ ", " + e.getMessage() + "; removing bubble");
|
||||||
|
mController.removeBubble(getBubbleKey(), Bubbles.DISMISS_INVALID_INTENT);
|
||||||
|
}
|
||||||
|
mInitialized = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onReleased() {
|
||||||
|
mDestroyed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTaskCreated(int taskId, ComponentName name) {
|
||||||
|
if (DEBUG_BUBBLE_EXPANDED_VIEW) {
|
||||||
|
Log.d(TAG, "onTaskCreated: taskId=" + taskId
|
||||||
|
+ " bubble=" + getBubbleKey());
|
||||||
|
}
|
||||||
|
// The taskId is saved to use for removeTask, preventing appearance in recent tasks.
|
||||||
|
mTaskId = taskId;
|
||||||
|
|
||||||
|
// With the task org, the taskAppeared callback will only happen once the task has
|
||||||
|
// already drawn
|
||||||
|
mListener.onTaskCreated();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTaskVisibilityChanged(int taskId, boolean visible) {
|
||||||
|
mListener.onContentVisibilityChanged(visible);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTaskRemovalStarted(int taskId) {
|
||||||
|
if (DEBUG_BUBBLE_EXPANDED_VIEW) {
|
||||||
|
Log.d(TAG, "onTaskRemovalStarted: taskId=" + taskId
|
||||||
|
+ " bubble=" + getBubbleKey());
|
||||||
|
}
|
||||||
|
if (mBubble != null) {
|
||||||
|
mController.removeBubble(mBubble.getKey(), Bubbles.DISMISS_TASK_FINISHED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBackPressedOnTaskRoot(int taskId) {
|
||||||
|
if (mTaskId == taskId && mController.isStackExpanded()) {
|
||||||
|
mListener.onBackPressed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public BubbleTaskViewHelper(Context context,
|
||||||
|
BubbleController controller,
|
||||||
|
BubbleTaskViewHelper.Listener listener,
|
||||||
|
View parent) {
|
||||||
|
mContext = context;
|
||||||
|
mController = controller;
|
||||||
|
mMainExecutor = mController.getMainExecutor();
|
||||||
|
mListener = listener;
|
||||||
|
mParentView = parent;
|
||||||
|
mTaskViewTaskController = new TaskViewTaskController(mContext,
|
||||||
|
mController.getTaskOrganizer(),
|
||||||
|
mController.getTaskViewTransitions(), mController.getSyncTransactionQueue());
|
||||||
|
mTaskView = new TaskView(mContext, mTaskViewTaskController);
|
||||||
|
mTaskView.setListener(mMainExecutor, mTaskViewListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the bubble or updates the bubble used to populate the view.
|
||||||
|
*
|
||||||
|
* @return true if the bubble is new, false if it was an update to the same bubble.
|
||||||
|
*/
|
||||||
|
public boolean update(Bubble bubble) {
|
||||||
|
boolean isNew = mBubble == null || didBackingContentChange(bubble);
|
||||||
|
mBubble = bubble;
|
||||||
|
if (isNew) {
|
||||||
|
mPendingIntent = mBubble.getBubbleIntent();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Cleans up anything related to the task and {@code TaskView}. */
|
||||||
|
public void cleanUpTaskView() {
|
||||||
|
if (DEBUG_BUBBLE_EXPANDED_VIEW) {
|
||||||
|
Log.d(TAG, "cleanUpExpandedState: bubble=" + getBubbleKey() + " task=" + mTaskId);
|
||||||
|
}
|
||||||
|
if (mTaskId != INVALID_TASK_ID) {
|
||||||
|
try {
|
||||||
|
ActivityTaskManager.getService().removeTask(mTaskId);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
Log.w(TAG, e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (mTaskView != null) {
|
||||||
|
mTaskView.release();
|
||||||
|
mTaskView = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the bubble key associated with this view. */
|
||||||
|
@Nullable
|
||||||
|
public String getBubbleKey() {
|
||||||
|
return mBubble != null ? mBubble.getKey() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the TaskView associated with this view. */
|
||||||
|
@Nullable
|
||||||
|
public TaskView getTaskView() {
|
||||||
|
return mTaskView;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the task id associated with the task in this view. If the task doesn't exist then
|
||||||
|
* {@link ActivityTaskManager#INVALID_TASK_ID}.
|
||||||
|
*/
|
||||||
|
public int getTaskId() {
|
||||||
|
return mTaskId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns whether the bubble set on the helper is valid to populate the task view. */
|
||||||
|
public boolean isValidBubble() {
|
||||||
|
return mBubble != null && (mPendingIntent != null || mBubble.hasMetadataShortcutId());
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO (b/274980695): Is this still relevant?
|
||||||
|
/**
|
||||||
|
* Bubbles are backed by a pending intent or a shortcut, once the activity is
|
||||||
|
* started we never change it / restart it on notification updates -- unless the bubble's
|
||||||
|
* backing data switches.
|
||||||
|
*
|
||||||
|
* This indicates if the new bubble is backed by a different data source than what was
|
||||||
|
* previously shown here (e.g. previously a pending intent & now a shortcut).
|
||||||
|
*
|
||||||
|
* @param newBubble the bubble this view is being updated with.
|
||||||
|
* @return true if the backing content has changed.
|
||||||
|
*/
|
||||||
|
private boolean didBackingContentChange(Bubble newBubble) {
|
||||||
|
boolean prevWasIntentBased = mBubble != null && mPendingIntent != null;
|
||||||
|
boolean newIsIntentBased = newBubble.getBubbleIntent() != null;
|
||||||
|
return prevWasIntentBased != newIsIntentBased;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -39,6 +39,7 @@ import androidx.annotation.IntDef;
|
|||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import com.android.wm.shell.common.annotations.ExternalThread;
|
import com.android.wm.shell.common.annotations.ExternalThread;
|
||||||
|
import com.android.wm.shell.common.bubbles.BubbleBarUpdate;
|
||||||
|
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
@@ -81,6 +82,11 @@ public interface Bubbles {
|
|||||||
int DISMISS_RELOAD_FROM_DISK = 15;
|
int DISMISS_RELOAD_FROM_DISK = 15;
|
||||||
int DISMISS_USER_REMOVED = 16;
|
int DISMISS_USER_REMOVED = 16;
|
||||||
|
|
||||||
|
/** Returns a binder that can be passed to an external process to manipulate Bubbles. */
|
||||||
|
default IBubbles createExternalInterface() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return {@code true} if there is a bubble associated with the provided key and if its
|
* @return {@code true} if there is a bubble associated with the provided key and if its
|
||||||
* notification is hidden from the shade or there is a group summary associated with the
|
* notification is hidden from the shade or there is a group summary associated with the
|
||||||
@@ -277,6 +283,17 @@ public interface Bubbles {
|
|||||||
*/
|
*/
|
||||||
void onUserRemoved(int removedUserId);
|
void onUserRemoved(int removedUserId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A listener to be notified of bubble state changes, used by launcher to render bubbles in
|
||||||
|
* its process.
|
||||||
|
*/
|
||||||
|
interface BubbleStateListener {
|
||||||
|
/**
|
||||||
|
* Called when the bubbles state changes.
|
||||||
|
*/
|
||||||
|
void onBubbleStateChange(BubbleBarUpdate update);
|
||||||
|
}
|
||||||
|
|
||||||
/** Listener to find out about stack expansion / collapse events. */
|
/** Listener to find out about stack expansion / collapse events. */
|
||||||
interface BubbleExpandListener {
|
interface BubbleExpandListener {
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 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.wm.shell.bubbles;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import com.android.wm.shell.bubbles.IBubblesListener;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface that is exposed to remote callers (launcher) to manipulate the bubbles feature when
|
||||||
|
* showing in the bubble bar.
|
||||||
|
*/
|
||||||
|
interface IBubbles {
|
||||||
|
|
||||||
|
oneway void registerBubbleListener(in IBubblesListener listener) = 1;
|
||||||
|
|
||||||
|
oneway void unregisterBubbleListener(in IBubblesListener listener) = 2;
|
||||||
|
|
||||||
|
oneway void showBubble(in String key, in boolean onLauncherHome) = 3;
|
||||||
|
|
||||||
|
oneway void removeBubble(in String key, in int reason) = 4;
|
||||||
|
|
||||||
|
oneway void collapseBubbles() = 5;
|
||||||
|
|
||||||
|
oneway void onTaskbarStateChanged(in int newState) = 6;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 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.wm.shell.bubbles;
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listener interface that Launcher attaches to SystemUI to get bubbles callbacks.
|
||||||
|
*/
|
||||||
|
oneway interface IBubblesListener {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the bubbles state changes.
|
||||||
|
*/
|
||||||
|
void onBubbleStateChange(in Bundle update);
|
||||||
|
}
|
||||||
@@ -0,0 +1,137 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 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.wm.shell.common.bubbles;
|
||||||
|
|
||||||
|
import android.annotation.NonNull;
|
||||||
|
import android.annotation.Nullable;
|
||||||
|
import android.os.Parcel;
|
||||||
|
import android.os.Parcelable;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an update to bubbles state. This is passed through
|
||||||
|
* {@link com.android.wm.shell.bubbles.IBubblesListener} to launcher so that taskbar may render
|
||||||
|
* bubbles. This should be kept this as minimal as possible in terms of data.
|
||||||
|
*/
|
||||||
|
public class BubbleBarUpdate implements Parcelable {
|
||||||
|
|
||||||
|
public static final String BUNDLE_KEY = "update";
|
||||||
|
|
||||||
|
public boolean expandedChanged;
|
||||||
|
public boolean expanded;
|
||||||
|
@Nullable
|
||||||
|
public String selectedBubbleKey;
|
||||||
|
@Nullable
|
||||||
|
public BubbleInfo addedBubble;
|
||||||
|
@Nullable
|
||||||
|
public BubbleInfo updatedBubble;
|
||||||
|
@Nullable
|
||||||
|
public String suppressedBubbleKey;
|
||||||
|
@Nullable
|
||||||
|
public String unsupressedBubbleKey;
|
||||||
|
|
||||||
|
// This is only populated if bubbles have been removed.
|
||||||
|
public List<RemovedBubble> removedBubbles = new ArrayList<>();
|
||||||
|
|
||||||
|
// This is only populated if the order of the bubbles has changed.
|
||||||
|
public List<String> bubbleKeysInOrder = new ArrayList<>();
|
||||||
|
|
||||||
|
// This is only populated the first time a listener is connected so it gets the current state.
|
||||||
|
public List<BubbleInfo> currentBubbleList = new ArrayList<>();
|
||||||
|
|
||||||
|
public BubbleBarUpdate() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public BubbleBarUpdate(Parcel parcel) {
|
||||||
|
expandedChanged = parcel.readBoolean();
|
||||||
|
expanded = parcel.readBoolean();
|
||||||
|
selectedBubbleKey = parcel.readString();
|
||||||
|
addedBubble = parcel.readParcelable(BubbleInfo.class.getClassLoader(),
|
||||||
|
BubbleInfo.class);
|
||||||
|
updatedBubble = parcel.readParcelable(BubbleInfo.class.getClassLoader(),
|
||||||
|
BubbleInfo.class);
|
||||||
|
suppressedBubbleKey = parcel.readString();
|
||||||
|
unsupressedBubbleKey = parcel.readString();
|
||||||
|
removedBubbles = parcel.readParcelableList(new ArrayList<>(),
|
||||||
|
RemovedBubble.class.getClassLoader());
|
||||||
|
parcel.readStringList(bubbleKeysInOrder);
|
||||||
|
currentBubbleList = parcel.readParcelableList(new ArrayList<>(),
|
||||||
|
BubbleInfo.class.getClassLoader());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether anything has changed in this update.
|
||||||
|
*/
|
||||||
|
public boolean anythingChanged() {
|
||||||
|
return expandedChanged
|
||||||
|
|| selectedBubbleKey != null
|
||||||
|
|| addedBubble != null
|
||||||
|
|| updatedBubble != null
|
||||||
|
|| !removedBubbles.isEmpty()
|
||||||
|
|| !bubbleKeysInOrder.isEmpty()
|
||||||
|
|| suppressedBubbleKey != null
|
||||||
|
|| unsupressedBubbleKey != null
|
||||||
|
|| !currentBubbleList.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "BubbleBarUpdate{ expandedChanged=" + expandedChanged
|
||||||
|
+ " expanded=" + expanded
|
||||||
|
+ " selectedBubbleKey=" + selectedBubbleKey
|
||||||
|
+ " addedBubble=" + addedBubble
|
||||||
|
+ " updatedBubble=" + updatedBubble
|
||||||
|
+ " suppressedBubbleKey=" + suppressedBubbleKey
|
||||||
|
+ " unsuppressedBubbleKey=" + unsupressedBubbleKey
|
||||||
|
+ " removedBubbles=" + removedBubbles
|
||||||
|
+ " bubbles=" + bubbleKeysInOrder
|
||||||
|
+ " currentBubbleList=" + currentBubbleList
|
||||||
|
+ " }";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int describeContents() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeToParcel(Parcel parcel, int flags) {
|
||||||
|
parcel.writeBoolean(expandedChanged);
|
||||||
|
parcel.writeBoolean(expanded);
|
||||||
|
parcel.writeString(selectedBubbleKey);
|
||||||
|
parcel.writeParcelable(addedBubble, flags);
|
||||||
|
parcel.writeParcelable(updatedBubble, flags);
|
||||||
|
parcel.writeString(suppressedBubbleKey);
|
||||||
|
parcel.writeString(unsupressedBubbleKey);
|
||||||
|
parcel.writeParcelableList(removedBubbles, flags);
|
||||||
|
parcel.writeStringList(bubbleKeysInOrder);
|
||||||
|
parcel.writeParcelableList(currentBubbleList, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static final Creator<BubbleBarUpdate> CREATOR =
|
||||||
|
new Creator<BubbleBarUpdate>() {
|
||||||
|
public BubbleBarUpdate createFromParcel(Parcel source) {
|
||||||
|
return new BubbleBarUpdate(source);
|
||||||
|
}
|
||||||
|
public BubbleBarUpdate[] newArray(int size) {
|
||||||
|
return new BubbleBarUpdate[size];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,154 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 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.wm.shell.common.bubbles;
|
||||||
|
|
||||||
|
import android.annotation.NonNull;
|
||||||
|
import android.annotation.Nullable;
|
||||||
|
import android.app.Notification;
|
||||||
|
import android.graphics.drawable.Icon;
|
||||||
|
import android.os.Parcel;
|
||||||
|
import android.os.Parcelable;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains information necessary to present a bubble.
|
||||||
|
*/
|
||||||
|
public class BubbleInfo implements Parcelable {
|
||||||
|
|
||||||
|
// TODO(b/269672147): needs a title string for a11y & that comes from notification
|
||||||
|
// TODO(b/269671451): needs whether the bubble is an 'important person' or not
|
||||||
|
|
||||||
|
private String mKey; // Same key as the Notification
|
||||||
|
private int mFlags; // Flags from BubbleMetadata
|
||||||
|
private String mShortcutId;
|
||||||
|
private int mUserId;
|
||||||
|
private String mPackageName;
|
||||||
|
/**
|
||||||
|
* All notification bubbles require a shortcut to be set on the notification, however, the
|
||||||
|
* app could still specify an Icon and PendingIntent to use for the bubble. In that case
|
||||||
|
* this icon will be populated. If the bubble is entirely shortcut based, this will be null.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
private Icon mIcon;
|
||||||
|
|
||||||
|
public BubbleInfo(String key, int flags, String shortcutId, @Nullable Icon icon,
|
||||||
|
int userId, String packageName) {
|
||||||
|
mKey = key;
|
||||||
|
mFlags = flags;
|
||||||
|
mShortcutId = shortcutId;
|
||||||
|
mIcon = icon;
|
||||||
|
mUserId = userId;
|
||||||
|
mPackageName = packageName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BubbleInfo(Parcel source) {
|
||||||
|
mKey = source.readString();
|
||||||
|
mFlags = source.readInt();
|
||||||
|
mShortcutId = source.readString();
|
||||||
|
mIcon = source.readTypedObject(Icon.CREATOR);
|
||||||
|
mUserId = source.readInt();
|
||||||
|
mPackageName = source.readString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getKey() {
|
||||||
|
return mKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getShortcutId() {
|
||||||
|
return mShortcutId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Icon getIcon() {
|
||||||
|
return mIcon;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getFlags() {
|
||||||
|
return mFlags;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getUserId() {
|
||||||
|
return mUserId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPackageName() {
|
||||||
|
return mPackageName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether this bubble is currently being hidden from the stack.
|
||||||
|
*/
|
||||||
|
public boolean isBubbleSuppressed() {
|
||||||
|
return (mFlags & Notification.BubbleMetadata.FLAG_SUPPRESS_BUBBLE) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether this bubble is able to be suppressed (i.e. has the developer opted into the API
|
||||||
|
* to
|
||||||
|
* hide the bubble when in the same content).
|
||||||
|
*/
|
||||||
|
public boolean isBubbleSuppressable() {
|
||||||
|
return (mFlags & Notification.BubbleMetadata.FLAG_SUPPRESSABLE_BUBBLE) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the notification for this bubble is hidden from the shade.
|
||||||
|
*/
|
||||||
|
public boolean isNotificationSuppressed() {
|
||||||
|
return (mFlags & Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (!(o instanceof BubbleInfo)) return false;
|
||||||
|
BubbleInfo bubble = (BubbleInfo) o;
|
||||||
|
return Objects.equals(mKey, bubble.mKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return mKey.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int describeContents() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeToParcel(Parcel parcel, int flags) {
|
||||||
|
parcel.writeString(mKey);
|
||||||
|
parcel.writeInt(mFlags);
|
||||||
|
parcel.writeString(mShortcutId);
|
||||||
|
parcel.writeTypedObject(mIcon, flags);
|
||||||
|
parcel.writeInt(mUserId);
|
||||||
|
parcel.writeString(mPackageName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static final Creator<BubbleInfo> CREATOR =
|
||||||
|
new Creator<BubbleInfo>() {
|
||||||
|
public BubbleInfo createFromParcel(Parcel source) {
|
||||||
|
return new BubbleInfo(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BubbleInfo[] newArray(int size) {
|
||||||
|
return new BubbleInfo[size];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 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.wm.shell.common.bubbles;
|
||||||
|
|
||||||
|
import android.annotation.NonNull;
|
||||||
|
import android.os.Parcel;
|
||||||
|
import android.os.Parcelable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a removed bubble, defining the key and reason the bubble was removed.
|
||||||
|
*/
|
||||||
|
public class RemovedBubble implements Parcelable {
|
||||||
|
|
||||||
|
private final String mKey;
|
||||||
|
private final int mRemovalReason;
|
||||||
|
|
||||||
|
public RemovedBubble(String key, int removalReason) {
|
||||||
|
mKey = key;
|
||||||
|
mRemovalReason = removalReason;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RemovedBubble(Parcel parcel) {
|
||||||
|
mKey = parcel.readString();
|
||||||
|
mRemovalReason = parcel.readInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getKey() {
|
||||||
|
return mKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getRemovalReason() {
|
||||||
|
return mRemovalReason;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int describeContents() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeToParcel(Parcel dest, int flags) {
|
||||||
|
dest.writeString(mKey);
|
||||||
|
dest.writeInt(mRemovalReason);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static final Creator<RemovedBubble> CREATOR =
|
||||||
|
new Creator<RemovedBubble>() {
|
||||||
|
public RemovedBubble createFromParcel(Parcel source) {
|
||||||
|
return new RemovedBubble(source);
|
||||||
|
}
|
||||||
|
public RemovedBubble[] newArray(int size) {
|
||||||
|
return new RemovedBubble[size];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -22,6 +22,8 @@ package com.android.wm.shell.sysui;
|
|||||||
public class ShellSharedConstants {
|
public class ShellSharedConstants {
|
||||||
// See IPip.aidl
|
// See IPip.aidl
|
||||||
public static final String KEY_EXTRA_SHELL_PIP = "extra_shell_pip";
|
public static final String KEY_EXTRA_SHELL_PIP = "extra_shell_pip";
|
||||||
|
// See IBubbles.aidl
|
||||||
|
public static final String KEY_EXTRA_SHELL_BUBBLES = "extra_shell_bubbles";
|
||||||
// See ISplitScreen.aidl
|
// See ISplitScreen.aidl
|
||||||
public static final String KEY_EXTRA_SHELL_SPLIT_SCREEN = "extra_shell_split_screen";
|
public static final String KEY_EXTRA_SHELL_SPLIT_SCREEN = "extra_shell_split_screen";
|
||||||
// See IOneHanded.aidl
|
// See IOneHanded.aidl
|
||||||
|
|||||||
Reference in New Issue
Block a user