feat: Add option to clear home screen in settings (#6125)
Signed-off-by: abhixv <abhi.sharma1@hotmail.com>
This commit is contained in:
committed by
Pun Butrach
parent
9898749619
commit
5f3a03f4fb
@@ -15,11 +15,17 @@
|
||||
*/
|
||||
package com.android.launcher3.taskbar.bubbles;
|
||||
|
||||
import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_GET_PERSONS_DATA;
|
||||
import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_CACHED;
|
||||
import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC;
|
||||
import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED_BY_ANY_LAUNCHER;
|
||||
import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
|
||||
|
||||
import static com.android.launcher3.icons.FastBitmapDrawable.WHITE_SCRIM_ALPHA;
|
||||
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
|
||||
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING;
|
||||
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_VISIBLE;
|
||||
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING;
|
||||
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SWITCHER_SHOWING;
|
||||
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
|
||||
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
|
||||
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING;
|
||||
@@ -29,42 +35,65 @@ import static java.util.stream.Collectors.toList;
|
||||
|
||||
import android.annotation.BinderThread;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.Notification;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.LauncherApps;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ShortcutInfo;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.drawable.AdaptiveIconDrawable;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.InsetDrawable;
|
||||
import android.os.Bundle;
|
||||
import android.os.Process;
|
||||
import android.os.SystemProperties;
|
||||
import android.os.UserHandle;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.Log;
|
||||
import android.util.PathParser;
|
||||
import android.view.LayoutInflater;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.content.res.AppCompatResources;
|
||||
|
||||
import com.android.launcher3.taskbar.TaskbarSharedState;
|
||||
import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController;
|
||||
import com.android.internal.graphics.ColorUtils;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.icons.BitmapInfo;
|
||||
import com.android.launcher3.icons.BubbleIconFactory;
|
||||
import com.android.launcher3.shortcuts.ShortcutRequest;
|
||||
import com.android.launcher3.util.Executors.SimpleThreadFactory;
|
||||
import com.android.quickstep.SystemUiProxy;
|
||||
import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
|
||||
import com.android.wm.shell.Flags;
|
||||
import com.android.wm.shell.bubbles.IBubblesListener;
|
||||
import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
|
||||
import com.android.wm.shell.shared.bubbles.BubbleBarUpdate;
|
||||
import com.android.wm.shell.shared.bubbles.BubbleInfo;
|
||||
import com.android.wm.shell.shared.bubbles.RemovedBubble;
|
||||
import com.android.wm.shell.common.bubbles.BubbleBarLocation;
|
||||
import com.android.wm.shell.common.bubbles.BubbleBarUpdate;
|
||||
import com.android.wm.shell.common.bubbles.BubbleInfo;
|
||||
import com.android.wm.shell.common.bubbles.RemovedBubble;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
/**
|
||||
* This registers a listener with SysUIProxy to get information about changes to the bubble
|
||||
* stack state from WMShell (SysUI). The controller is also responsible for loading the necessary
|
||||
* This registers a listener with SysUIProxy to get information about changes to
|
||||
* the bubble
|
||||
* stack state from WMShell (SysUI). The controller is also responsible for
|
||||
* loading the necessary
|
||||
* information to render each of the bubbles & dispatches changes to
|
||||
* {@link BubbleBarViewController} which will then update {@link BubbleBarView} as needed.
|
||||
* {@link BubbleBarViewController} which will then update {@link BubbleBarView}
|
||||
* as needed.
|
||||
*
|
||||
* <p>For details around the behavior of the bubble bar, see {@link BubbleBarView}.
|
||||
* <p>
|
||||
* For details around the behavior of the bubble bar, see {@link BubbleBarView}.
|
||||
*/
|
||||
public class BubbleBarController extends IBubblesListener.Stub {
|
||||
|
||||
@@ -72,7 +101,8 @@ public class BubbleBarController extends IBubblesListener.Stub {
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
/**
|
||||
* Determines whether bubbles can be shown in the bubble bar. This value updates when the
|
||||
* Determines whether bubbles can be shown in the bubble bar. This value updates
|
||||
* when the
|
||||
* taskbar is recreated.
|
||||
*
|
||||
* @see #onTaskbarRecreated()
|
||||
@@ -85,7 +115,10 @@ public class BubbleBarController extends IBubblesListener.Stub {
|
||||
return sBubbleBarEnabled;
|
||||
}
|
||||
|
||||
/** Re-reads the value of the flag from SystemProperties when taskbar is recreated. */
|
||||
/**
|
||||
* Re-reads the value of the flag from SystemProperties when taskbar is
|
||||
* recreated.
|
||||
*/
|
||||
public static void onTaskbarRecreated() {
|
||||
sBubbleBarEnabled = Flags.enableBubbleBar()
|
||||
|| SystemProperties.getBoolean("persist.wm.debug.bubble_bar", false);
|
||||
@@ -94,9 +127,10 @@ public class BubbleBarController extends IBubblesListener.Stub {
|
||||
private static final long MASK_HIDE_BUBBLE_BAR = SYSUI_STATE_BOUNCER_SHOWING
|
||||
| SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING
|
||||
| SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED
|
||||
| SYSUI_STATE_IME_VISIBLE
|
||||
| SYSUI_STATE_IME_SHOWING
|
||||
| SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED
|
||||
| SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
|
||||
| SYSUI_STATE_QUICK_SETTINGS_EXPANDED
|
||||
| SYSUI_STATE_IME_SWITCHER_SHOWING;
|
||||
|
||||
private static final long MASK_HIDE_HANDLE_VIEW = SYSUI_STATE_BOUNCER_SHOWING
|
||||
| SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING
|
||||
@@ -109,29 +143,28 @@ public class BubbleBarController extends IBubblesListener.Stub {
|
||||
private final Context mContext;
|
||||
private final BubbleBarView mBarView;
|
||||
private final ArrayMap<String, BubbleBarBubble> mBubbles = new ArrayMap<>();
|
||||
private final ArrayMap<String, BubbleBarBubble> mSuppressedBubbles = new ArrayMap<>();
|
||||
|
||||
private static final Executor BUBBLE_STATE_EXECUTOR = Executors.newSingleThreadExecutor(
|
||||
new SimpleThreadFactory("BubbleStateUpdates-", THREAD_PRIORITY_BACKGROUND));
|
||||
private final LauncherApps mLauncherApps;
|
||||
private final BubbleIconFactory mIconFactory;
|
||||
private final SystemUiProxy mSystemUiProxy;
|
||||
|
||||
private BubbleBarItem mSelectedBubble;
|
||||
private BubbleBarOverflow mOverflowBubble;
|
||||
|
||||
private TaskbarSharedState mSharedState;
|
||||
private ImeVisibilityChecker mImeVisibilityChecker;
|
||||
private BubbleBarViewController mBubbleBarViewController;
|
||||
private BubbleStashController mBubbleStashController;
|
||||
private Optional<BubbleStashedHandleViewController> mBubbleStashedHandleViewController;
|
||||
private BubbleStashedHandleViewController mBubbleStashedHandleViewController;
|
||||
private BubblePinController mBubblePinController;
|
||||
private BubbleCreator mBubbleCreator;
|
||||
private BubbleBarLocationListener mBubbleBarLocationListener;
|
||||
|
||||
// Cache last sent top coordinate to avoid sending duplicate updates to shell
|
||||
private int mLastSentBubbleBarTop;
|
||||
|
||||
private boolean mIsImeVisible = false;
|
||||
|
||||
/**
|
||||
* Similar to {@link BubbleBarUpdate} but rather than {@link BubbleInfo}s it uses
|
||||
* Similar to {@link BubbleBarUpdate} but rather than {@link BubbleInfo}s it
|
||||
* uses
|
||||
* {@link BubbleBarBubble}s so that it can be used to update the views.
|
||||
*/
|
||||
private static class BubbleBarViewUpdate {
|
||||
@@ -146,8 +179,6 @@ public class BubbleBarController extends IBubblesListener.Stub {
|
||||
List<RemovedBubble> removedBubbles;
|
||||
List<String> bubbleKeysInOrder;
|
||||
Point expandedViewDropTargetSize;
|
||||
boolean showOverflow;
|
||||
boolean showOverflowChanged;
|
||||
|
||||
// These need to be loaded in the background
|
||||
BubbleBarBubble addedBubble;
|
||||
@@ -166,8 +197,6 @@ public class BubbleBarController extends IBubblesListener.Stub {
|
||||
removedBubbles = update.removedBubbles;
|
||||
bubbleKeysInOrder = update.bubbleKeysInOrder;
|
||||
expandedViewDropTargetSize = update.expandedViewDropTargetSize;
|
||||
showOverflow = update.showOverflow;
|
||||
showOverflowChanged = update.showOverflowChanged;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,74 +205,81 @@ public class BubbleBarController extends IBubblesListener.Stub {
|
||||
mBarView = bubbleView; // Need the view for inflating bubble views.
|
||||
|
||||
mSystemUiProxy = SystemUiProxy.INSTANCE.get(context);
|
||||
|
||||
if (sBubbleBarEnabled) {
|
||||
mSystemUiProxy.setBubblesListener(this);
|
||||
}
|
||||
mLauncherApps = context.getSystemService(LauncherApps.class);
|
||||
mIconFactory = new BubbleIconFactory(context,
|
||||
context.getResources().getDimensionPixelSize(R.dimen.bubblebar_icon_size),
|
||||
context.getResources().getDimensionPixelSize(R.dimen.bubblebar_badge_size),
|
||||
context.getResources().getColor(R.color.important_conversation),
|
||||
context.getResources().getDimensionPixelSize(
|
||||
com.android.internal.R.dimen.importance_ring_stroke_width));
|
||||
}
|
||||
|
||||
public void onDestroy() {
|
||||
mSystemUiProxy.setBubblesListener(null);
|
||||
// Saves bubble bar state
|
||||
BubbleInfo[] bubbleInfoItems = new BubbleInfo[mBubbles.size()];
|
||||
mBubbles.values().forEach(bubbleBarBubble -> {
|
||||
int index = mBubbleBarViewController.bubbleViewIndex(bubbleBarBubble.getView());
|
||||
if (index < 0 || index >= bubbleInfoItems.length) {
|
||||
Log.e(TAG, "Found improper index: " + index + " for " + bubbleBarBubble);
|
||||
} else {
|
||||
bubbleInfoItems[index] = bubbleBarBubble.getInfo();
|
||||
}
|
||||
});
|
||||
mSharedState.bubbleInfoItems = Arrays.asList(bubbleInfoItems);
|
||||
mSharedState.suppressedBubbleInfoItems = new ArrayList<>(mSuppressedBubbles.size());
|
||||
for (int i = 0; i < mSuppressedBubbles.size(); i++) {
|
||||
mSharedState.suppressedBubbleInfoItems.add(mSuppressedBubbles.valueAt(i).getInfo());
|
||||
}
|
||||
}
|
||||
|
||||
/** Initializes controllers. */
|
||||
public void init(BubbleControllers bubbleControllers,
|
||||
BubbleBarLocationListener bubbleBarLocationListener,
|
||||
TaskbarSharedState sharedState) {
|
||||
mSharedState = sharedState;
|
||||
ImeVisibilityChecker imeVisibilityChecker) {
|
||||
mImeVisibilityChecker = imeVisibilityChecker;
|
||||
mBubbleBarViewController = bubbleControllers.bubbleBarViewController;
|
||||
mBubbleStashController = bubbleControllers.bubbleStashController;
|
||||
mBubbleStashedHandleViewController = bubbleControllers.bubbleStashedHandleViewController;
|
||||
mBubblePinController = bubbleControllers.bubblePinController;
|
||||
mBubbleCreator = bubbleControllers.bubbleCreator;
|
||||
mBubbleBarLocationListener = bubbleBarLocationListener;
|
||||
|
||||
bubbleControllers.runAfterInit(() -> {
|
||||
restoreSavedState(sharedState);
|
||||
mBubbleBarViewController.setHiddenForBubbles(
|
||||
!sBubbleBarEnabled || mBubbles.isEmpty());
|
||||
mBubbleStashedHandleViewController.ifPresent(
|
||||
controller -> controller.setHiddenForBubbles(
|
||||
!sBubbleBarEnabled || mBubbles.isEmpty()));
|
||||
mBubbleStashedHandleViewController.setHiddenForBubbles(
|
||||
!sBubbleBarEnabled || mBubbles.isEmpty());
|
||||
mBubbleBarViewController.setUpdateSelectedBubbleAfterCollapse(
|
||||
key -> setSelectedBubbleInternal(mBubbles.get(key)));
|
||||
mBubbleBarViewController.setBoundsChangeListener(this::onBubbleBarBoundsChanged);
|
||||
mBubbleBarLocationListener.onBubbleBarLocationUpdated(
|
||||
mBubbleBarViewController.getBubbleBarLocation());
|
||||
if (sBubbleBarEnabled) {
|
||||
mSystemUiProxy.setBubblesListener(this);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the bubble bar, handle bar, and stash controllers based on sysui state flags.
|
||||
* Creates and adds the overflow bubble to the bubble bar if it hasn't been
|
||||
* created yet.
|
||||
*
|
||||
* <p>
|
||||
* This should be called on the {@link #BUBBLE_STATE_EXECUTOR} executor to avoid
|
||||
* inflating
|
||||
* the overflow multiple times.
|
||||
*/
|
||||
private void createAndAddOverflowIfNeeded() {
|
||||
if (mOverflowBubble == null) {
|
||||
BubbleBarOverflow overflow = createOverflow(mContext);
|
||||
MAIN_EXECUTOR.execute(() -> {
|
||||
// we're on the main executor now, so check that the overflow hasn't been
|
||||
// created
|
||||
// again to avoid races.
|
||||
if (mOverflowBubble == null) {
|
||||
mBubbleBarViewController.addBubble(
|
||||
overflow, /* isExpanding= */ false, /* suppressAnimation= */ true);
|
||||
mOverflowBubble = overflow;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the bubble bar, handle bar, and stash controllers based on sysui
|
||||
* state flags.
|
||||
*/
|
||||
public void updateStateForSysuiFlags(@SystemUiStateFlags long flags) {
|
||||
boolean hideBubbleBar = (flags & MASK_HIDE_BUBBLE_BAR) != 0;
|
||||
mBubbleBarViewController.setHiddenForSysui(hideBubbleBar);
|
||||
|
||||
boolean hideHandleView = (flags & MASK_HIDE_HANDLE_VIEW) != 0;
|
||||
mBubbleStashedHandleViewController.ifPresent(
|
||||
controller -> controller.setHiddenForSysui(hideHandleView));
|
||||
mBubbleStashedHandleViewController.setHiddenForSysui(hideHandleView);
|
||||
|
||||
boolean sysuiLocked = (flags & MASK_SYSUI_LOCKED) != 0;
|
||||
mBubbleStashController.setSysuiLocked(sysuiLocked);
|
||||
mIsImeVisible = (flags & SYSUI_STATE_IME_VISIBLE) != 0;
|
||||
if (mIsImeVisible) {
|
||||
mBubbleBarViewController.onImeVisible();
|
||||
}
|
||||
mBubbleStashController.onSysuiLockedStateChange(sysuiLocked);
|
||||
}
|
||||
|
||||
//
|
||||
@@ -261,24 +297,20 @@ public class BubbleBarController extends IBubblesListener.Stub {
|
||||
|| !update.currentBubbleList.isEmpty()) {
|
||||
// We have bubbles to load
|
||||
BUBBLE_STATE_EXECUTOR.execute(() -> {
|
||||
createAndAddOverflowIfNeeded();
|
||||
if (update.addedBubble != null) {
|
||||
viewUpdate.addedBubble = mBubbleCreator.populateBubble(mContext,
|
||||
update.addedBubble,
|
||||
mBarView,
|
||||
viewUpdate.addedBubble = populateBubble(mContext, update.addedBubble, mBarView,
|
||||
null /* existingBubble */);
|
||||
}
|
||||
if (update.updatedBubble != null) {
|
||||
BubbleBarBubble existingBubble = mBubbles.get(update.updatedBubble.getKey());
|
||||
viewUpdate.updatedBubble =
|
||||
mBubbleCreator.populateBubble(mContext, update.updatedBubble,
|
||||
mBarView,
|
||||
existingBubble);
|
||||
viewUpdate.updatedBubble = populateBubble(mContext, update.updatedBubble, mBarView,
|
||||
existingBubble);
|
||||
}
|
||||
if (update.currentBubbleList != null && !update.currentBubbleList.isEmpty()) {
|
||||
List<BubbleBarBubble> currentBubbles = new ArrayList<>();
|
||||
for (int i = 0; i < update.currentBubbleList.size(); i++) {
|
||||
BubbleBarBubble b = mBubbleCreator.populateBubble(mContext,
|
||||
update.currentBubbleList.get(i), mBarView,
|
||||
BubbleBarBubble b = populateBubble(mContext, update.currentBubbleList.get(i), mBarView,
|
||||
null /* existingBubble */);
|
||||
currentBubbles.add(b);
|
||||
}
|
||||
@@ -293,61 +325,92 @@ public class BubbleBarController extends IBubblesListener.Stub {
|
||||
}
|
||||
}
|
||||
|
||||
private void restoreSavedState(TaskbarSharedState sharedState) {
|
||||
if (sharedState.bubbleBarLocation != null) {
|
||||
updateBubbleBarLocationInternal(sharedState.bubbleBarLocation);
|
||||
}
|
||||
restoreSavedBubbles(sharedState.bubbleInfoItems);
|
||||
restoreSuppressed(sharedState.suppressedBubbleInfoItems);
|
||||
}
|
||||
|
||||
private void restoreSavedBubbles(List<BubbleInfo> bubbleInfos) {
|
||||
if (bubbleInfos == null || bubbleInfos.isEmpty()) return;
|
||||
// Iterate in reverse because new bubbles are added in front and the list is in order.
|
||||
for (int i = bubbleInfos.size() - 1; i >= 0; i--) {
|
||||
BubbleBarBubble bubble = mBubbleCreator.populateBubble(mContext,
|
||||
bubbleInfos.get(i), mBarView, /* existingBubble = */ null);
|
||||
if (bubble == null) {
|
||||
Log.e(TAG, "Could not instantiate BubbleBarBubble for " + bubbleInfos.get(i));
|
||||
continue;
|
||||
}
|
||||
addBubbleInternally(bubble, /* isExpanding= */ false, /* suppressAnimation= */ true);
|
||||
}
|
||||
}
|
||||
|
||||
private void restoreSuppressed(List<BubbleInfo> bubbleInfos) {
|
||||
if (bubbleInfos == null || bubbleInfos.isEmpty()) return;
|
||||
for (BubbleInfo bubbleInfo : bubbleInfos.reversed()) {
|
||||
BubbleBarBubble bb = mBubbleCreator.populateBubble(mContext, bubbleInfo,
|
||||
mBarView, /* existingBubble= */
|
||||
null);
|
||||
if (bb != null) {
|
||||
mSuppressedBubbles.put(bb.getKey(), bb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void applyViewChanges(BubbleBarViewUpdate update) {
|
||||
final boolean isCollapsed = (update.expandedChanged && !update.expanded)
|
||||
|| (!update.expandedChanged && !mBubbleBarViewController.isExpanded());
|
||||
final boolean isExpanding = update.expandedChanged && update.expanded;
|
||||
// don't animate bubbles if this is the initial state because we may be unfolding or
|
||||
// enabling gesture nav. also suppress animation if the bubble bar is hidden for sysui e.g.
|
||||
// don't animate bubbles if this is the initial state because we may be
|
||||
// unfolding or
|
||||
// enabling gesture nav. also suppress animation if the bubble bar is hidden for
|
||||
// sysui e.g.
|
||||
// the shade is open, or we're locked.
|
||||
final boolean suppressAnimation =
|
||||
update.initialState || mBubbleBarViewController.isHiddenForSysui() || mIsImeVisible;
|
||||
final boolean suppressAnimation = update.initialState || mBubbleBarViewController.isHiddenForSysui()
|
||||
|| mImeVisibilityChecker.isImeVisible();
|
||||
|
||||
if (update.initialState && mSharedState.hasSavedBubbles()) {
|
||||
// clear restored state
|
||||
mBubbleBarViewController.removeAllBubbles();
|
||||
mBubbles.clear();
|
||||
mBubbleBarViewController.showOverflow(update.showOverflow);
|
||||
BubbleBarBubble bubbleToSelect = null;
|
||||
if (!update.removedBubbles.isEmpty()) {
|
||||
for (int i = 0; i < update.removedBubbles.size(); i++) {
|
||||
RemovedBubble removedBubble = update.removedBubbles.get(i);
|
||||
BubbleBarBubble bubble = mBubbles.remove(removedBubble.getKey());
|
||||
if (bubble != null) {
|
||||
mBubbleBarViewController.removeBubble(bubble);
|
||||
} else {
|
||||
Log.w(TAG, "trying to remove bubble that doesn't exist: "
|
||||
+ removedBubble.getKey());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (update.addedBubble != null) {
|
||||
mBubbles.put(update.addedBubble.getKey(), update.addedBubble);
|
||||
mBubbleBarViewController.addBubble(update.addedBubble, isExpanding, suppressAnimation);
|
||||
if (isCollapsed) {
|
||||
// If we're collapsed, the most recently added bubble will be selected.
|
||||
bubbleToSelect = update.addedBubble;
|
||||
}
|
||||
|
||||
}
|
||||
if (update.currentBubbles != null && !update.currentBubbles.isEmpty()) {
|
||||
// Iterate in reverse because new bubbles are added in front and the list is in
|
||||
// order.
|
||||
for (int i = update.currentBubbles.size() - 1; i >= 0; i--) {
|
||||
BubbleBarBubble bubble = update.currentBubbles.get(i);
|
||||
if (bubble != null) {
|
||||
mBubbles.put(bubble.getKey(), bubble);
|
||||
mBubbleBarViewController.addBubble(bubble, isExpanding, suppressAnimation);
|
||||
if (isCollapsed) {
|
||||
// If we're collapsed, the most recently added bubble will be selected.
|
||||
bubbleToSelect = bubble;
|
||||
}
|
||||
} else {
|
||||
Log.w(TAG, "trying to add bubble but null after loading! "
|
||||
+ update.addedBubble.getKey());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Adds and removals have happened, update visibility before any other visual
|
||||
// changes.
|
||||
mBubbleBarViewController.setHiddenForBubbles(mBubbles.isEmpty());
|
||||
mBubbleStashedHandleViewController.setHiddenForBubbles(mBubbles.isEmpty());
|
||||
|
||||
if (mBubbles.isEmpty()) {
|
||||
// all bubbles were removed. clear the selected bubble
|
||||
mSelectedBubble = null;
|
||||
}
|
||||
|
||||
if (update.updatedBubble != null) {
|
||||
// Updates mean the dot state may have changed; any other changes were updated
|
||||
// in
|
||||
// the populateBubble step.
|
||||
BubbleBarBubble bb = mBubbles.get(update.updatedBubble.getKey());
|
||||
// If we're not stashed, we're visible so animate
|
||||
bb.getView().updateDotVisibility(!mBubbleStashController.isStashed() /* animate */);
|
||||
mBubbleBarViewController.animateBubbleNotification(bb, /* isExpanding= */ false);
|
||||
}
|
||||
if (update.bubbleKeysInOrder != null && !update.bubbleKeysInOrder.isEmpty()) {
|
||||
// Create the new list
|
||||
List<BubbleBarBubble> newOrder = update.bubbleKeysInOrder.stream()
|
||||
.map(mBubbles::get).filter(Objects::nonNull).collect(toList());
|
||||
if (!newOrder.isEmpty()) {
|
||||
mBubbleBarViewController.reorderBubbles(newOrder);
|
||||
}
|
||||
}
|
||||
if (update.suppressedBubbleKey != null) {
|
||||
// TODO: (b/273316505) handle suppression
|
||||
}
|
||||
if (update.unsuppressedBubbleKey != null) {
|
||||
// TODO: (b/273316505) handle suppression
|
||||
}
|
||||
BubbleBarBubble bubbleToSelect = null;
|
||||
if (update.selectedBubbleKey != null) {
|
||||
if (mSelectedBubble == null
|
||||
|| !update.selectedBubbleKey.equals(mSelectedBubble.getKey())) {
|
||||
@@ -360,139 +423,6 @@ public class BubbleBarController extends IBubblesListener.Stub {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Flags.enableOptionalBubbleOverflow()
|
||||
&& update.showOverflowChanged && !update.showOverflow && update.addedBubble != null
|
||||
&& update.removedBubbles.isEmpty()
|
||||
&& !mBubbles.isEmpty()) {
|
||||
// A bubble was added from the overflow (& now it's empty / not showing)
|
||||
mBubbleBarViewController.removeOverflowAndAddBubble(update.addedBubble, bubbleToSelect);
|
||||
} else if (update.addedBubble != null && update.removedBubbles.size() == 1) {
|
||||
// we're adding and removing a bubble at the same time. handle this as a single update.
|
||||
RemovedBubble removedBubble = update.removedBubbles.get(0);
|
||||
BubbleBarBubble bubbleToRemove = mBubbles.remove(removedBubble.getKey());
|
||||
boolean showOverflow = update.showOverflowChanged && update.showOverflow;
|
||||
if (bubbleToRemove != null) {
|
||||
mBubbleBarViewController.addBubbleAndRemoveBubble(update.addedBubble,
|
||||
bubbleToRemove, bubbleToSelect, isExpanding, suppressAnimation,
|
||||
showOverflow);
|
||||
} else {
|
||||
mBubbleBarViewController.addBubble(update.addedBubble, isExpanding,
|
||||
suppressAnimation, bubbleToSelect);
|
||||
Log.w(TAG, "trying to remove bubble that doesn't exist: " + removedBubble.getKey());
|
||||
}
|
||||
} else {
|
||||
boolean overflowNeedsToBeAdded = Flags.enableOptionalBubbleOverflow()
|
||||
&& update.showOverflowChanged && update.showOverflow;
|
||||
if (!update.removedBubbles.isEmpty()) {
|
||||
for (int i = 0; i < update.removedBubbles.size(); i++) {
|
||||
RemovedBubble removedBubble = update.removedBubbles.get(i);
|
||||
BubbleBarBubble bubble = mBubbles.remove(removedBubble.getKey());
|
||||
if (bubble != null && overflowNeedsToBeAdded) {
|
||||
// First removal, show the overflow
|
||||
overflowNeedsToBeAdded = false;
|
||||
mBubbleBarViewController.addOverflowAndRemoveBubble(bubble, bubbleToSelect);
|
||||
} else if (bubble != null) {
|
||||
mBubbleBarViewController.removeBubble(bubble);
|
||||
} else {
|
||||
Log.w(TAG, "trying to remove bubble that doesn't exist: "
|
||||
+ removedBubble.getKey());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (update.addedBubble != null) {
|
||||
mBubbleBarViewController.addBubble(update.addedBubble, isExpanding,
|
||||
suppressAnimation, bubbleToSelect);
|
||||
}
|
||||
if (Flags.enableOptionalBubbleOverflow()
|
||||
&& update.showOverflowChanged
|
||||
&& update.showOverflow != mBubbleBarViewController.isOverflowAdded()) {
|
||||
mBubbleBarViewController.showOverflow(update.showOverflow);
|
||||
}
|
||||
}
|
||||
|
||||
// if a bubble was updated upstream, but removed before the update was received, add it back
|
||||
if (update.updatedBubble != null && !mBubbles.containsKey(update.updatedBubble.getKey())) {
|
||||
addBubbleInternally(update.updatedBubble, isExpanding, suppressAnimation);
|
||||
}
|
||||
|
||||
if (update.addedBubble != null && isCollapsed && bubbleToSelect == null) {
|
||||
// If we're collapsed, the most recently added bubble will be selected.
|
||||
bubbleToSelect = update.addedBubble;
|
||||
}
|
||||
|
||||
if (update.currentBubbles != null && !update.currentBubbles.isEmpty()) {
|
||||
// Iterate in reverse because new bubbles are added in front and the list is in order.
|
||||
for (int i = update.currentBubbles.size() - 1; i >= 0; i--) {
|
||||
BubbleBarBubble bubble = update.currentBubbles.get(i);
|
||||
if (bubble != null) {
|
||||
addBubbleInternally(bubble, isExpanding, suppressAnimation);
|
||||
if (isCollapsed && bubbleToSelect == null) {
|
||||
// If we're collapsed, the most recently added bubble will be selected.
|
||||
bubbleToSelect = bubble;
|
||||
}
|
||||
} else {
|
||||
Log.w(TAG, "trying to add bubble but null after loading! "
|
||||
+ update.addedBubble.getKey());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Flags.enableOptionalBubbleOverflow() && update.initialState && update.showOverflow) {
|
||||
mBubbleBarViewController.showOverflow(true);
|
||||
}
|
||||
|
||||
if (update.suppressedBubbleKey != null) {
|
||||
BubbleBarBubble bb = mBubbles.remove(update.suppressedBubbleKey);
|
||||
if (bb != null) {
|
||||
mSuppressedBubbles.put(update.suppressedBubbleKey, bb);
|
||||
mBubbleBarViewController.removeBubble(bb);
|
||||
}
|
||||
}
|
||||
if (update.unsuppressedBubbleKey != null) {
|
||||
BubbleBarBubble bb = mSuppressedBubbles.remove(update.unsuppressedBubbleKey);
|
||||
if (bb != null) {
|
||||
// Unsuppressing an existing bubble should not cause the bar to expand or animate
|
||||
addBubbleInternally(bb, /* isExpanding= */ false, /* suppressAnimation= */ true);
|
||||
if (mBubbleBarViewController.isHiddenForNoBubbles()) {
|
||||
mBubbleBarViewController.setHiddenForBubbles(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update the visibility if this is the initial state, if there are no bubbles, or if the
|
||||
// animation is suppressed.
|
||||
// If this is the initial bubble, the bubble bar will become visible as part of the
|
||||
// animation.
|
||||
if (update.initialState || mBubbles.isEmpty() || suppressAnimation) {
|
||||
mBubbleBarViewController.setHiddenForBubbles(mBubbles.isEmpty());
|
||||
}
|
||||
mBubbleStashedHandleViewController.ifPresent(
|
||||
controller -> controller.setHiddenForBubbles(mBubbles.isEmpty()));
|
||||
|
||||
if (mBubbles.isEmpty()) {
|
||||
// all bubbles were removed. clear the selected bubble
|
||||
mSelectedBubble = null;
|
||||
}
|
||||
|
||||
if (update.updatedBubble != null) {
|
||||
// Updates mean the dot state may have changed; any other changes were updated in
|
||||
// the populateBubble step.
|
||||
BubbleBarBubble bb = mBubbles.get(update.updatedBubble.getKey());
|
||||
if (suppressAnimation) {
|
||||
// since we're not animating this update, we should update the dot visibility here.
|
||||
bb.getView().updateDotVisibility(/* animate= */ false);
|
||||
} else {
|
||||
mBubbleBarViewController.animateBubbleNotification(
|
||||
bb, /* isExpanding= */ false, /* isUpdate= */ true);
|
||||
}
|
||||
}
|
||||
if (update.bubbleKeysInOrder != null && !update.bubbleKeysInOrder.isEmpty()) {
|
||||
// Create the new list
|
||||
List<BubbleBarBubble> newOrder = update.bubbleKeysInOrder.stream()
|
||||
.map(mBubbles::get).filter(Objects::nonNull).toList();
|
||||
if (!newOrder.isEmpty()) {
|
||||
mBubbleBarViewController.reorderBubbles(newOrder);
|
||||
}
|
||||
}
|
||||
if (bubbleToSelect != null) {
|
||||
setSelectedBubbleInternal(bubbleToSelect);
|
||||
}
|
||||
@@ -507,7 +437,6 @@ public class BubbleBarController extends IBubblesListener.Stub {
|
||||
}
|
||||
}
|
||||
if (update.bubbleBarLocation != null) {
|
||||
mSharedState.bubbleBarLocation = update.bubbleBarLocation;
|
||||
if (update.bubbleBarLocation != mBubbleBarViewController.getBubbleBarLocation()) {
|
||||
updateBubbleBarLocationInternal(update.bubbleBarLocation);
|
||||
}
|
||||
@@ -517,16 +446,19 @@ public class BubbleBarController extends IBubblesListener.Stub {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the given bubble from the backing list of bubbles after it was dismissed by the user.
|
||||
*/
|
||||
public void onBubbleDismissed(BubbleView bubble) {
|
||||
mBubbles.remove(bubble.getBubble().getKey());
|
||||
}
|
||||
|
||||
/** Tells WMShell to show the currently selected bubble. */
|
||||
public void showSelectedBubble() {
|
||||
if (getSelectedBubbleKey() != null) {
|
||||
if (mSelectedBubble instanceof BubbleBarBubble) {
|
||||
// Because we've visited this bubble, we should suppress the notification.
|
||||
// This is updated on WMShell side when we show the bubble, but that update
|
||||
// isn't
|
||||
// passed to launcher, instead we apply it directly here.
|
||||
BubbleInfo info = ((BubbleBarBubble) mSelectedBubble).getInfo();
|
||||
info.setFlags(
|
||||
info.getFlags() | Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION);
|
||||
mSelectedBubble.getView().updateDotVisibility(true /* animate */);
|
||||
}
|
||||
mLastSentBubbleBarTop = mBarView.getRestingTopPositionOnScreen();
|
||||
mSystemUiProxy.showBubble(getSelectedBubbleKey(), mLastSentBubbleBarTop);
|
||||
} else {
|
||||
@@ -534,21 +466,27 @@ public class BubbleBarController extends IBubblesListener.Stub {
|
||||
}
|
||||
}
|
||||
|
||||
/** Updates the currently selected bubble for launcher views and tells WMShell to show it. */
|
||||
/**
|
||||
* Updates the currently selected bubble for launcher views and tells WMShell to
|
||||
* show it.
|
||||
*/
|
||||
public void showAndSelectBubble(BubbleBarItem b) {
|
||||
if (DEBUG) Log.w(TAG, "showingSelectedBubble: " + b.getKey());
|
||||
if (DEBUG)
|
||||
Log.w(TAG, "showingSelectedBubble: " + b.getKey());
|
||||
setSelectedBubbleInternal(b);
|
||||
showSelectedBubble();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the bubble that should be selected. This notifies the views, it does not notify
|
||||
* Sets the bubble that should be selected. This notifies the views, it does not
|
||||
* notify
|
||||
* WMShell that the selection has changed, that should go through either
|
||||
* {@link #showSelectedBubble()} or {@link #showAndSelectBubble(BubbleBarItem)}.
|
||||
*/
|
||||
private void setSelectedBubbleInternal(BubbleBarItem b) {
|
||||
if (!Objects.equals(b, mSelectedBubble)) {
|
||||
if (DEBUG) Log.w(TAG, "selectingBubble: " + b.getKey());
|
||||
if (DEBUG)
|
||||
Log.w(TAG, "selectingBubble: " + b.getKey());
|
||||
mSelectedBubble = b;
|
||||
mBubbleBarViewController.updateSelectedBubble(mSelectedBubble);
|
||||
}
|
||||
@@ -570,57 +508,153 @@ public class BubbleBarController extends IBubblesListener.Stub {
|
||||
* <p>
|
||||
* Updates the value locally in Launcher and in WMShell.
|
||||
*/
|
||||
public void updateBubbleBarLocation(BubbleBarLocation location,
|
||||
@BubbleBarLocation.UpdateSource int source) {
|
||||
public void updateBubbleBarLocation(BubbleBarLocation location) {
|
||||
updateBubbleBarLocationInternal(location);
|
||||
mSystemUiProxy.setBubbleBarLocation(location, source);
|
||||
mSystemUiProxy.setBubbleBarLocation(location);
|
||||
}
|
||||
|
||||
private void updateBubbleBarLocationInternal(BubbleBarLocation location) {
|
||||
mBubbleBarViewController.setBubbleBarLocation(location);
|
||||
mBubbleStashController.setBubbleBarLocation(location);
|
||||
mBubbleBarLocationListener.onBubbleBarLocationUpdated(location);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void animateBubbleBarLocation(BubbleBarLocation bubbleBarLocation) {
|
||||
MAIN_EXECUTOR.execute(
|
||||
() -> {
|
||||
mBubbleBarViewController.animateBubbleBarLocation(bubbleBarLocation);
|
||||
mBubbleBarLocationListener.onBubbleBarLocationAnimated(bubbleBarLocation);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDragItemOverBubbleBarDragZone(@NonNull BubbleBarLocation bubbleBarLocation) {
|
||||
MAIN_EXECUTOR.execute(() -> {
|
||||
mBubbleBarViewController.onDragItemOverBubbleBarDragZone(bubbleBarLocation);
|
||||
if (mBubbleBarViewController.isLocationUpdatedForDropTarget()) {
|
||||
mBubbleBarLocationListener.onBubbleBarLocationAnimated(bubbleBarLocation);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemDraggedOutsideBubbleBarDropZone() {
|
||||
MAIN_EXECUTOR.execute(() -> {
|
||||
if (mBubbleBarViewController.isLocationUpdatedForDropTarget()) {
|
||||
BubbleBarLocation original = mBubbleBarViewController.getBubbleBarLocation();
|
||||
mBubbleBarLocationListener.onBubbleBarLocationAnimated(original);
|
||||
}
|
||||
mBubbleBarViewController.onItemDraggedOutsideBubbleBarDropZone();
|
||||
});
|
||||
}
|
||||
|
||||
/** Notifies WMShell to show the expanded view. */
|
||||
void showExpandedView() {
|
||||
mSystemUiProxy.showExpandedView();
|
||||
() -> mBubbleBarViewController.animateBubbleBarLocation(bubbleBarLocation));
|
||||
}
|
||||
|
||||
//
|
||||
// Loading data for the bubbles
|
||||
//
|
||||
|
||||
@Nullable
|
||||
private BubbleBarBubble populateBubble(Context context, BubbleInfo b, BubbleBarView bbv,
|
||||
@Nullable BubbleBarBubble existingBubble) {
|
||||
String appName;
|
||||
Bitmap badgeBitmap;
|
||||
Bitmap bubbleBitmap;
|
||||
Path dotPath;
|
||||
int dotColor;
|
||||
|
||||
boolean isImportantConvo = b.isImportantConversation();
|
||||
|
||||
ShortcutRequest.QueryResult result = new ShortcutRequest(context,
|
||||
new UserHandle(b.getUserId()))
|
||||
.forPackage(b.getPackageName(), b.getShortcutId())
|
||||
.query(FLAG_MATCH_DYNAMIC
|
||||
| FLAG_MATCH_PINNED_BY_ANY_LAUNCHER
|
||||
| FLAG_MATCH_CACHED
|
||||
| FLAG_GET_PERSONS_DATA);
|
||||
|
||||
ShortcutInfo shortcutInfo = result.size() > 0 ? result.get(0) : null;
|
||||
if (shortcutInfo == null) {
|
||||
Log.w(TAG, "No shortcutInfo found for bubble: " + b.getKey()
|
||||
+ " with shortcutId: " + b.getShortcutId());
|
||||
}
|
||||
|
||||
ApplicationInfo appInfo;
|
||||
try {
|
||||
appInfo = mLauncherApps.getApplicationInfo(
|
||||
b.getPackageName(),
|
||||
0,
|
||||
new UserHandle(b.getUserId()));
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
// If we can't find package... don't think we should show the bubble.
|
||||
Log.w(TAG, "Unable to find packageName: " + b.getPackageName());
|
||||
return null;
|
||||
}
|
||||
if (appInfo == null) {
|
||||
Log.w(TAG, "Unable to find appInfo: " + b.getPackageName());
|
||||
return null;
|
||||
}
|
||||
PackageManager pm = context.getPackageManager();
|
||||
appName = String.valueOf(appInfo.loadLabel(pm));
|
||||
Drawable appIcon = appInfo.loadUnbadgedIcon(pm);
|
||||
Drawable badgedIcon = pm.getUserBadgedIcon(appIcon, new UserHandle(b.getUserId()));
|
||||
|
||||
// Badged bubble image
|
||||
Drawable bubbleDrawable = mIconFactory.getBubbleDrawable(context, shortcutInfo,
|
||||
b.getIcon());
|
||||
if (bubbleDrawable == null) {
|
||||
// Default to app icon
|
||||
bubbleDrawable = appIcon;
|
||||
}
|
||||
|
||||
BitmapInfo badgeBitmapInfo = mIconFactory.getBadgeBitmap(badgedIcon, isImportantConvo);
|
||||
badgeBitmap = badgeBitmapInfo.icon;
|
||||
|
||||
float[] bubbleBitmapScale = new float[1];
|
||||
bubbleBitmap = mIconFactory.getBubbleBitmap(bubbleDrawable, bubbleBitmapScale);
|
||||
|
||||
// Dot color & placement
|
||||
Path iconPath = PathParser.createPathFromPathData(
|
||||
context.getResources().getString(
|
||||
com.android.internal.R.string.config_icon_mask));
|
||||
Matrix matrix = new Matrix();
|
||||
float scale = bubbleBitmapScale[0];
|
||||
float radius = BubbleView.DEFAULT_PATH_SIZE / 2f;
|
||||
matrix.setScale(scale /* x scale */, scale /* y scale */, radius /* pivot x */,
|
||||
radius /* pivot y */);
|
||||
iconPath.transform(matrix);
|
||||
dotPath = iconPath;
|
||||
dotColor = ColorUtils.blendARGB(badgeBitmapInfo.color,
|
||||
Color.WHITE, WHITE_SCRIM_ALPHA / 255f);
|
||||
|
||||
if (existingBubble == null) {
|
||||
LayoutInflater inflater = LayoutInflater.from(context);
|
||||
BubbleView bubbleView = (BubbleView) inflater.inflate(
|
||||
R.layout.bubblebar_item_view, bbv, false /* attachToRoot */);
|
||||
|
||||
BubbleBarBubble bubble = new BubbleBarBubble(b, bubbleView,
|
||||
badgeBitmap, bubbleBitmap, dotColor, dotPath, appName);
|
||||
bubbleView.setBubble(bubble);
|
||||
return bubble;
|
||||
} else {
|
||||
// If we already have a bubble (so it already has an inflated view), update it.
|
||||
existingBubble.setInfo(b);
|
||||
existingBubble.setBadge(badgeBitmap);
|
||||
existingBubble.setIcon(bubbleBitmap);
|
||||
existingBubble.setDotColor(dotColor);
|
||||
existingBubble.setDotPath(dotPath);
|
||||
existingBubble.setAppName(appName);
|
||||
return existingBubble;
|
||||
}
|
||||
}
|
||||
|
||||
private BubbleBarOverflow createOverflow(Context context) {
|
||||
Bitmap bitmap = createOverflowBitmap(context);
|
||||
LayoutInflater inflater = LayoutInflater.from(context);
|
||||
BubbleView bubbleView = (BubbleView) inflater.inflate(
|
||||
R.layout.bubblebar_item_view, mBarView, false /* attachToRoot */);
|
||||
BubbleBarOverflow overflow = new BubbleBarOverflow(bubbleView);
|
||||
bubbleView.setOverflow(overflow, bitmap);
|
||||
return overflow;
|
||||
}
|
||||
|
||||
private Bitmap createOverflowBitmap(Context context) {
|
||||
Drawable iconDrawable = AppCompatResources.getDrawable(mContext,
|
||||
R.drawable.bubble_ic_overflow_button);
|
||||
|
||||
final TypedArray ta = mContext.obtainStyledAttributes(
|
||||
new int[] {
|
||||
com.android.internal.R.attr.materialColorOnPrimaryFixed,
|
||||
com.android.internal.R.attr.materialColorPrimaryFixed
|
||||
});
|
||||
int overflowIconColor = ta.getColor(0, Color.WHITE);
|
||||
int overflowBackgroundColor = ta.getColor(1, Color.BLACK);
|
||||
ta.recycle();
|
||||
|
||||
iconDrawable.setTint(overflowIconColor);
|
||||
|
||||
int inset = context.getResources().getDimensionPixelSize(R.dimen.bubblebar_overflow_inset);
|
||||
Drawable foreground = new InsetDrawable(iconDrawable, inset);
|
||||
Drawable drawable = new AdaptiveIconDrawable(new ColorDrawable(overflowBackgroundColor),
|
||||
foreground);
|
||||
|
||||
return mIconFactory.createBadgedIconBitmap(drawable).icon;
|
||||
}
|
||||
|
||||
private void onBubbleBarBoundsChanged() {
|
||||
int newTop = mBarView.getRestingTopPositionOnScreen();
|
||||
if (newTop != mLastSentBubbleBarTop) {
|
||||
@@ -629,20 +663,9 @@ public class BubbleBarController extends IBubblesListener.Stub {
|
||||
}
|
||||
}
|
||||
|
||||
private void addBubbleInternally(BubbleBarBubble bubble, boolean isExpanding,
|
||||
boolean suppressAnimation) {
|
||||
mBubbles.put(bubble.getKey(), bubble);
|
||||
mBubbleBarViewController.addBubble(bubble, isExpanding,
|
||||
suppressAnimation, /* bubbleToSelect = */ null);
|
||||
}
|
||||
|
||||
/** Listener of {@link BubbleBarLocation} updates. */
|
||||
public interface BubbleBarLocationListener {
|
||||
|
||||
/** Called when {@link BubbleBarLocation} is animated, but change is not yet final. */
|
||||
void onBubbleBarLocationAnimated(BubbleBarLocation location);
|
||||
|
||||
/** Called when {@link BubbleBarLocation} is updated permanently. */
|
||||
void onBubbleBarLocationUpdated(BubbleBarLocation location);
|
||||
/** Interface for checking whether the IME is visible. */
|
||||
public interface ImeVisibilityChecker {
|
||||
/** Whether the IME is visible. */
|
||||
boolean isImeVisible();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user