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
@@ -16,30 +16,24 @@
|
||||
package com.android.launcher3.taskbar.bubbles;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.app.Notification;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.PointF;
|
||||
import android.graphics.Outline;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
import android.view.View;
|
||||
import android.view.ViewOutlineProvider;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.icons.DotRenderer;
|
||||
import com.android.wm.shell.shared.animation.Interpolators;
|
||||
import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
|
||||
import com.android.wm.shell.shared.bubbles.BubbleInfo;
|
||||
|
||||
import com.android.launcher3.icons.IconNormalizer;
|
||||
import com.android.wm.shell.animation.Interpolators;
|
||||
import com.patrykmichalik.opto.core.PreferenceExtensionsKt;
|
||||
import app.lawnchair.preferences2.PreferenceManager2;
|
||||
import app.lawnchair.theme.color.ColorOption;
|
||||
@@ -49,18 +43,34 @@ import java.util.EnumSet;
|
||||
// TODO: (b/276978250) This is will be similar to WMShell's BadgedImageView, it'd be nice to share.
|
||||
|
||||
/**
|
||||
* View that displays a bubble icon, along with an app badge on either the left or
|
||||
* View that displays a bubble icon, along with an app badge on either the left
|
||||
* or
|
||||
* right side of the view.
|
||||
*/
|
||||
public class BubbleView extends ConstraintLayout {
|
||||
|
||||
public static final int DEFAULT_PATH_SIZE = 100;
|
||||
/** Duration for animating the scale of the dot and badge. */
|
||||
private static final int SCALE_ANIMATION_DURATION_MS = 200;
|
||||
|
||||
/**
|
||||
* Flags that suppress the visibility of the 'new' dot or the app badge, for one
|
||||
* reason or
|
||||
* another. If any of these flags are set, the dot will not be shown.
|
||||
* If {@link SuppressionFlag#BEHIND_STACK} then the app badge will not be shown.
|
||||
*/
|
||||
enum SuppressionFlag {
|
||||
// TODO: (b/277815200) implement flyout
|
||||
// Suppressed because the flyout is visible - it will morph into the dot via
|
||||
// animation.
|
||||
FLYOUT_VISIBLE,
|
||||
// Suppressed because this bubble is behind others in the collapsed stack.
|
||||
BEHIND_STACK,
|
||||
}
|
||||
|
||||
private final EnumSet<SuppressionFlag> mSuppressionFlags = EnumSet.noneOf(SuppressionFlag.class);
|
||||
|
||||
private final ImageView mBubbleIcon;
|
||||
private final ImageView mAppIcon;
|
||||
private int mBubbleSize;
|
||||
private final int mBubbleSize;
|
||||
|
||||
private float mDragTranslationX;
|
||||
private float mOffsetX;
|
||||
@@ -76,22 +86,12 @@ public class BubbleView extends ConstraintLayout {
|
||||
private float mAnimatingToDotScale;
|
||||
// The current scale value of the dot
|
||||
private float mDotScale;
|
||||
private boolean mDotSuppressedForBubbleUpdate = false;
|
||||
|
||||
// TODO: (b/273310265) handle RTL
|
||||
// Whether the bubbles are positioned on the left or right side of the screen
|
||||
private boolean mOnLeft = false;
|
||||
|
||||
private BubbleBarItem mBubble;
|
||||
private boolean mIsOverflow;
|
||||
|
||||
private Bitmap mIcon;
|
||||
|
||||
@Nullable
|
||||
private Controller mController;
|
||||
|
||||
@Nullable
|
||||
private BubbleBarBubbleIconsFactory mIconFactory = null;
|
||||
|
||||
PreferenceManager2 preferenceManager2;
|
||||
|
||||
@@ -114,6 +114,8 @@ public class BubbleView extends ConstraintLayout {
|
||||
setLayoutDirection(LAYOUT_DIRECTION_LTR);
|
||||
|
||||
LayoutInflater.from(context).inflate(R.layout.bubble_view, this);
|
||||
|
||||
mBubbleSize = getResources().getDimensionPixelSize(R.dimen.bubblebar_icon_size);
|
||||
mBubbleIcon = findViewById(R.id.icon_view);
|
||||
mAppIcon = findViewById(R.id.app_icon_view);
|
||||
|
||||
@@ -123,26 +125,24 @@ public class BubbleView extends ConstraintLayout {
|
||||
|
||||
setFocusable(true);
|
||||
setClickable(true);
|
||||
|
||||
// We manage the shadow ourselves when creating the bitmap
|
||||
setOutlineAmbientShadowColor(Color.TRANSPARENT);
|
||||
setOutlineSpotShadowColor(Color.TRANSPARENT);
|
||||
setOutlineProvider(new ViewOutlineProvider() {
|
||||
@Override
|
||||
public void getOutline(View view, Outline outline) {
|
||||
BubbleView.this.getOutline(outline);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void updateBubbleSizeAndDotRender() {
|
||||
int updatedBubbleSize = Math.min(getWidth(), getHeight());
|
||||
if (updatedBubbleSize == mBubbleSize) return;
|
||||
mBubbleSize = updatedBubbleSize;
|
||||
mIconFactory = new BubbleBarBubbleIconsFactory(mContext, mBubbleSize);
|
||||
updateBubbleIcon();
|
||||
if (mBubble == null || mBubble instanceof BubbleBarOverflow) return;
|
||||
Path dotPath = ((BubbleBarBubble) mBubble).getDotPath();
|
||||
mDotRenderer = new DotRenderer(mBubbleSize, dotPath, DEFAULT_PATH_SIZE);
|
||||
private void getOutline(Outline outline) {
|
||||
final int normalizedSize = IconNormalizer.getNormalizedCircleSize(mBubbleSize);
|
||||
final int inset = (mBubbleSize - normalizedSize) / 2;
|
||||
outline.setOval(inset, inset, inset + normalizedSize, inset + normalizedSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set translation-x while this bubble is being dragged.
|
||||
* Translation applied to the view is a sum of {@code translationX} and offset defined by
|
||||
* Translation applied to the view is a sum of {@code translationX} and offset
|
||||
* defined by
|
||||
* {@link #setOffsetX(float)}.
|
||||
*/
|
||||
public void setDragTranslationX(float translationX) {
|
||||
@@ -159,9 +159,11 @@ public class BubbleView extends ConstraintLayout {
|
||||
|
||||
/**
|
||||
* Set offset on x-axis while dragging.
|
||||
* Used to counter parent translation in order to keep the dragged view at the current position
|
||||
* Used to counter parent translation in order to keep the dragged view at the
|
||||
* current position
|
||||
* on screen.
|
||||
* Translation applied to the view is a sum of {@code offsetX} and translation defined by
|
||||
* Translation applied to the view is a sum of {@code offsetX} and translation
|
||||
* defined by
|
||||
* {@link #setDragTranslationX(float)}
|
||||
*/
|
||||
public void setOffsetX(float offsetX) {
|
||||
@@ -173,12 +175,6 @@ public class BubbleView extends ConstraintLayout {
|
||||
setTranslationX(mDragTranslationX + mOffsetX);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
||||
super.onLayout(changed, left, top, right, bottom);
|
||||
updateBubbleSizeAndDotRender();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispatchDraw(Canvas canvas) {
|
||||
super.dispatchDraw(canvas);
|
||||
@@ -189,78 +185,19 @@ public class BubbleView extends ConstraintLayout {
|
||||
|
||||
getDrawingRect(mTempBounds);
|
||||
|
||||
mDrawParams.dotColor = mDotColor;
|
||||
mDrawParams.appColor = mDotColor;
|
||||
mDrawParams.iconBounds = mTempBounds;
|
||||
mDrawParams.leftAlign = mOnLeft;
|
||||
mDrawParams.scale = mDotScale;
|
||||
|
||||
mDotRenderer.draw(canvas, mDrawParams);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
|
||||
super.onInitializeAccessibilityNodeInfoInternal(info);
|
||||
info.addAction(AccessibilityNodeInfo.ACTION_COLLAPSE);
|
||||
if (mBubble instanceof BubbleBarBubble) {
|
||||
info.addAction(AccessibilityNodeInfo.ACTION_DISMISS);
|
||||
}
|
||||
if (mController != null) {
|
||||
if (mController.getBubbleBarLocation().isOnLeft(isLayoutRtl())) {
|
||||
info.addAction(new AccessibilityNodeInfo.AccessibilityAction(R.id.action_move_right,
|
||||
getResources().getString(R.string.bubble_bar_action_move_right)));
|
||||
} else {
|
||||
info.addAction(new AccessibilityNodeInfo.AccessibilityAction(R.id.action_move_left,
|
||||
getResources().getString(R.string.bubble_bar_action_move_left)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
|
||||
if (super.performAccessibilityActionInternal(action, arguments)) {
|
||||
return true;
|
||||
}
|
||||
if (action == AccessibilityNodeInfo.ACTION_COLLAPSE) {
|
||||
if (mController != null) {
|
||||
mController.collapse();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (action == AccessibilityNodeInfo.ACTION_DISMISS) {
|
||||
if (mController != null) {
|
||||
mController.dismiss(this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (action == R.id.action_move_left) {
|
||||
if (mController != null) {
|
||||
mController.updateBubbleBarLocation(BubbleBarLocation.LEFT,
|
||||
BubbleBarLocation.UpdateSource.A11Y_ACTION_BUBBLE);
|
||||
}
|
||||
}
|
||||
if (action == R.id.action_move_right) {
|
||||
if (mController != null) {
|
||||
mController.updateBubbleBarLocation(BubbleBarLocation.RIGHT,
|
||||
BubbleBarLocation.UpdateSource.A11Y_ACTION_BUBBLE);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void setController(@Nullable Controller controller) {
|
||||
mController = controller;
|
||||
mDotRenderer.draw(canvas, mDrawParams, -1);
|
||||
}
|
||||
|
||||
/** Sets the bubble being rendered in this view. */
|
||||
public void setBubble(BubbleBarBubble bubble) {
|
||||
mBubble = bubble;
|
||||
mIcon = bubble.getIcon();
|
||||
updateBubbleIcon();
|
||||
if (bubble.getInfo().showAppBadge()) {
|
||||
mAppIcon.setImageBitmap(bubble.getBadge());
|
||||
} else {
|
||||
mAppIcon.setVisibility(GONE);
|
||||
}
|
||||
mBubbleIcon.setImageBitmap(bubble.getIcon());
|
||||
mAppIcon.setImageBitmap(bubble.getBadge());
|
||||
mDotColor = bubble.getDotColor();
|
||||
ColorOption dotColorOption = PreferenceExtensionsKt.firstBlocking(preferenceManager2.getNotificationDotColor());
|
||||
int dotColor = dotColorOption.getColorPreferenceEntry().getLightColor().invoke(getContext());
|
||||
@@ -281,53 +218,32 @@ public class BubbleView extends ConstraintLayout {
|
||||
setContentDescription(contentDesc);
|
||||
}
|
||||
|
||||
private void updateBubbleIcon() {
|
||||
Bitmap icon = null;
|
||||
if (mIcon != null) {
|
||||
icon = mIcon;
|
||||
if (mIconFactory != null) {
|
||||
BitmapDrawable iconDrawable = new BitmapDrawable(getResources(), icon);
|
||||
icon = mIconFactory.createShadowedIconBitmap(iconDrawable, /* scale = */ 1f);
|
||||
}
|
||||
}
|
||||
mBubbleIcon.setImageBitmap(icon);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets that this bubble represents the overflow. The overflow appears in the list of bubbles
|
||||
* but does not represent app content, instead it shows recent bubbles that couldn't fit into
|
||||
* the list of bubbles. It doesn't show an app icon because it is part of system UI / doesn't
|
||||
* Sets that this bubble represents the overflow. The overflow appears in the
|
||||
* list of bubbles
|
||||
* but does not represent app content, instead it shows recent bubbles that
|
||||
* couldn't fit into
|
||||
* the list of bubbles. It doesn't show an app icon because it is part of system
|
||||
* UI / doesn't
|
||||
* come from an app.
|
||||
*/
|
||||
public void setOverflow(BubbleBarOverflow overflow, Bitmap bitmap) {
|
||||
mBubble = overflow;
|
||||
mIsOverflow = true;
|
||||
mIcon = bitmap;
|
||||
updateBubbleIcon();
|
||||
mBubbleIcon.setImageBitmap(bitmap);
|
||||
mAppIcon.setVisibility(GONE); // Overflow doesn't show the app badge
|
||||
setContentDescription(getResources().getString(R.string.bubble_bar_overflow_description));
|
||||
}
|
||||
|
||||
/** Whether this view represents the overflow button. */
|
||||
public boolean isOverflow() {
|
||||
return mIsOverflow;
|
||||
}
|
||||
|
||||
/** Returns the bubble being rendered in this view. */
|
||||
@Nullable
|
||||
public BubbleBarItem getBubble() {
|
||||
return mBubble;
|
||||
}
|
||||
|
||||
/** Updates the dot visibility if it's not suppressed based on whether it has unseen content. */
|
||||
public void updateDotVisibility(boolean animate) {
|
||||
if (mDotSuppressedForBubbleUpdate) {
|
||||
// if the dot is suppressed for an update, there's nothing to do
|
||||
return;
|
||||
}
|
||||
final float targetScale = hasUnseenContent() ? 1f : 0f;
|
||||
void updateDotVisibility(boolean animate) {
|
||||
final float targetScale = shouldDrawDot() ? 1f : 0f;
|
||||
if (animate) {
|
||||
animateDotScale(targetScale);
|
||||
animateDotScale();
|
||||
} else {
|
||||
mDotScale = targetScale;
|
||||
mAnimatingToDotScale = targetScale;
|
||||
@@ -335,132 +251,77 @@ public class BubbleView extends ConstraintLayout {
|
||||
}
|
||||
}
|
||||
|
||||
void setBadgeScale(float fraction) {
|
||||
if (hasBadge()) {
|
||||
mAppIcon.setScaleX(fraction);
|
||||
mAppIcon.setScaleY(fraction);
|
||||
}
|
||||
}
|
||||
|
||||
void showBadge() {
|
||||
animateBadgeScale(1);
|
||||
}
|
||||
|
||||
void hideBadge() {
|
||||
animateBadgeScale(0);
|
||||
}
|
||||
|
||||
private boolean hasBadge() {
|
||||
return mAppIcon.getVisibility() == VISIBLE;
|
||||
}
|
||||
|
||||
private void animateBadgeScale(float scale) {
|
||||
if (!hasBadge()) {
|
||||
void updateBadgeVisibility() {
|
||||
if (mBubble instanceof BubbleBarOverflow) {
|
||||
// The overflow bubble does not have a badge, so just bail.
|
||||
return;
|
||||
}
|
||||
mAppIcon.clearAnimation();
|
||||
mAppIcon.animate()
|
||||
.setDuration(SCALE_ANIMATION_DURATION_MS)
|
||||
.setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
|
||||
.scaleX(scale)
|
||||
.scaleY(scale)
|
||||
.start();
|
||||
BubbleBarBubble bubble = (BubbleBarBubble) mBubble;
|
||||
Bitmap appBadgeBitmap = bubble.getBadge();
|
||||
int translationX = mOnLeft
|
||||
? -(bubble.getIcon().getWidth() - appBadgeBitmap.getWidth())
|
||||
: 0;
|
||||
mAppIcon.setTranslationX(translationX);
|
||||
mAppIcon.setVisibility(isBehindStack() ? GONE : VISIBLE);
|
||||
}
|
||||
|
||||
/** Suppresses drawing the dot due to an update for this bubble. */
|
||||
public void suppressDotForBubbleUpdate() {
|
||||
mDotSuppressedForBubbleUpdate = true;
|
||||
setDotScale(0);
|
||||
/** Sets whether this bubble is in the stack & not the first bubble. **/
|
||||
void setBehindStack(boolean behindStack, boolean animate) {
|
||||
if (behindStack) {
|
||||
mSuppressionFlags.add(SuppressionFlag.BEHIND_STACK);
|
||||
} else {
|
||||
mSuppressionFlags.remove(SuppressionFlag.BEHIND_STACK);
|
||||
}
|
||||
updateDotVisibility(animate);
|
||||
updateBadgeVisibility();
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsuppresses the dot after the bubble update finished animating.
|
||||
*
|
||||
* @param animate whether or not to animate the dot back in
|
||||
*/
|
||||
public void unsuppressDotForBubbleUpdate(boolean animate) {
|
||||
mDotSuppressedForBubbleUpdate = false;
|
||||
showDotIfNeeded(animate);
|
||||
/** Whether this bubble is in the stack & not the first bubble. **/
|
||||
boolean isBehindStack() {
|
||||
return mSuppressionFlags.contains(SuppressionFlag.BEHIND_STACK);
|
||||
}
|
||||
|
||||
boolean hasUnseenContent() {
|
||||
return mBubble != null
|
||||
&& mBubble instanceof BubbleBarBubble
|
||||
&& !((BubbleBarBubble) mBubble).getInfo().isNotificationSuppressed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to determine if we can skip drawing frames.
|
||||
*
|
||||
* <p>Generally we should draw the dot when it is requested to be shown and there is unseen
|
||||
* content. But when the dot is removed, we still want to draw frames so that it can be scaled
|
||||
* out.
|
||||
*/
|
||||
/** Whether the dot indicating unseen content in a bubble should be shown. */
|
||||
private boolean shouldDrawDot() {
|
||||
// if there's no dot there's nothing to draw, unless the dot was removed and we're in the
|
||||
// middle of removing it
|
||||
return hasUnseenContent() || mDotIsAnimating;
|
||||
boolean bubbleHasUnseenContent = mBubble != null
|
||||
&& mBubble instanceof BubbleBarBubble
|
||||
&& mSuppressionFlags.isEmpty()
|
||||
&& !((BubbleBarBubble) mBubble).getInfo().isNotificationSuppressed();
|
||||
|
||||
// Always render the dot if it's animating, since it could be animating out.
|
||||
// Otherwise, show
|
||||
// it if the bubble wants to show it, and we aren't suppressing it.
|
||||
return bubbleHasUnseenContent || mDotIsAnimating;
|
||||
}
|
||||
|
||||
/** Updates the dot scale to the specified fraction from 0 to 1. */
|
||||
/** How big the dot should be, fraction from 0 to 1. */
|
||||
private void setDotScale(float fraction) {
|
||||
if (!shouldDrawDot()) {
|
||||
return;
|
||||
}
|
||||
mDotScale = fraction;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
void showDotIfNeeded(float fraction) {
|
||||
if (!hasUnseenContent()) {
|
||||
return;
|
||||
}
|
||||
setDotScale(fraction);
|
||||
}
|
||||
|
||||
void showDotIfNeeded(boolean animate) {
|
||||
// only show the dot if we have unseen content and it's not suppressed
|
||||
if (!hasUnseenContent() || mDotSuppressedForBubbleUpdate) {
|
||||
return;
|
||||
}
|
||||
if (animate) {
|
||||
animateDotScale(1f);
|
||||
} else {
|
||||
setDotScale(1f);
|
||||
}
|
||||
}
|
||||
|
||||
void hideDot() {
|
||||
animateDotScale(0f);
|
||||
}
|
||||
|
||||
/** Marks this bubble such that it no longer has unseen content, and hides the dot. */
|
||||
void markSeen() {
|
||||
if (mBubble instanceof BubbleBarBubble bubble) {
|
||||
BubbleInfo info = bubble.getInfo();
|
||||
info.setFlags(
|
||||
info.getFlags() | Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION);
|
||||
hideDot();
|
||||
}
|
||||
}
|
||||
|
||||
/** Animates the dot to the given scale. */
|
||||
private void animateDotScale(float toScale) {
|
||||
boolean isDotScaleChanging = Float.compare(mDotScale, toScale) != 0;
|
||||
|
||||
// Don't restart the animation if we're already animating to the given value or if the dot
|
||||
// scale is not changing
|
||||
if ((mDotIsAnimating && mAnimatingToDotScale == toScale) || !isDotScaleChanging) {
|
||||
return;
|
||||
}
|
||||
/**
|
||||
* Animates the dot to the given scale.
|
||||
*/
|
||||
private void animateDotScale() {
|
||||
float toScale = shouldDrawDot() ? 1f : 0f;
|
||||
mDotIsAnimating = true;
|
||||
|
||||
// Don't restart the animation if we're already animating to the given value.
|
||||
if (mAnimatingToDotScale == toScale || !shouldDrawDot()) {
|
||||
mDotIsAnimating = false;
|
||||
return;
|
||||
}
|
||||
|
||||
mAnimatingToDotScale = toScale;
|
||||
|
||||
final boolean showDot = toScale > 0f;
|
||||
|
||||
// Do NOT wait until after animation ends to setShowDot
|
||||
// to avoid overriding more recent showDot states.
|
||||
clearAnimation();
|
||||
animate()
|
||||
.setDuration(SCALE_ANIMATION_DURATION_MS)
|
||||
.setDuration(200)
|
||||
.setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
|
||||
.setUpdateListener((valueAnimator) -> {
|
||||
float fraction = valueAnimator.getAnimatedFraction();
|
||||
@@ -472,42 +333,9 @@ public class BubbleView extends ConstraintLayout {
|
||||
}).start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the distance from the top left corner of this bubble view to the center of its dot.
|
||||
*/
|
||||
public PointF getDotCenter() {
|
||||
float[] dotPosition =
|
||||
mOnLeft ? mDotRenderer.getLeftDotPosition() : mDotRenderer.getRightDotPosition();
|
||||
getDrawingRect(mTempBounds);
|
||||
float dotCenterX = mTempBounds.width() * dotPosition[0];
|
||||
float dotCenterY = mTempBounds.height() * dotPosition[1];
|
||||
return new PointF(dotCenterX, dotCenterY);
|
||||
}
|
||||
|
||||
/** Returns the dot color. */
|
||||
public int getDotColor() {
|
||||
return mDotColor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String toString = mBubble != null ? mBubble.getKey() : "null";
|
||||
return "BubbleView{" + toString + "}";
|
||||
}
|
||||
|
||||
/** Interface for BubbleView to communicate with its controller */
|
||||
public interface Controller {
|
||||
/** Get current bubble bar {@link BubbleBarLocation} */
|
||||
BubbleBarLocation getBubbleBarLocation();
|
||||
|
||||
/** This bubble should be dismissed */
|
||||
void dismiss(BubbleView bubble);
|
||||
|
||||
/** Collapse the bubble bar */
|
||||
void collapse();
|
||||
|
||||
/** Request bubble bar location to be updated to the given location */
|
||||
void updateBubbleBarLocation(BubbleBarLocation location,
|
||||
@BubbleBarLocation.UpdateSource int source);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user