Better overflow button
Overflow button as BadgedImageView - BadgedImageView uses launcher’s icon factory to render overflow icon, which fixes the size and space inconsistencies between real bubbles and a bare ImageView - BadgedImageView gives us access to the existing dot drawing logic, which we can later use to draw a dot on overflow button when overflow bubbles get updates - Replace Bubble with BubbleViewProvider inside BadgedImageView so that BadgedImageView can access BubbleOverflow’s dot info UI polish - Set margins for overflow bubbles - Set padding for overflow empty state - Set overflow button and dot color to accent color from theme - Render overflow button based on theme and dark mode; update on change Bug: 149146374 Bug: 148878911 Test: manual - overflow button shape, icon color updates on theme change Test: manual - overflow button color updates on dark mode change Test: manual - overflow UI looks like mocks (specs not final) Test: atest SystemUITests Change-Id: I3d8829d56bce5c80936698a038438aff6db42d0f
This commit is contained in:
@@ -25,6 +25,7 @@
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/bubble_overflow_recycler"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:nestedScrollingEnabled="false"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
@@ -32,6 +33,8 @@
|
||||
android:id="@+id/bubble_overflow_empty_state"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingLeft="@dimen/bubble_overflow_empty_state_padding"
|
||||
android:paddingRight="@dimen/bubble_overflow_empty_state_padding"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center">
|
||||
|
||||
|
||||
@@ -14,11 +14,9 @@
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License
|
||||
-->
|
||||
<ImageView
|
||||
<com.android.systemui.bubbles.BadgedImageView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/bubble_overflow_button"
|
||||
android:layout_width="@dimen/individual_bubble_size"
|
||||
android:layout_height="@dimen/individual_bubble_size"
|
||||
android:src="@drawable/ic_bubble_overflow_button"
|
||||
android:scaleType="center"
|
||||
android:layout_gravity="end"/>
|
||||
android:src="@drawable/ic_bubble_overflow_button"/>
|
||||
|
||||
@@ -1128,8 +1128,10 @@
|
||||
<dimen name="bubble_padding_top">16dp</dimen>
|
||||
<!-- Size of individual bubbles. -->
|
||||
<dimen name="individual_bubble_size">60dp</dimen>
|
||||
<!-- Size of bubble bitmap. -->
|
||||
<dimen name="bubble_bitmap_size">52dp</dimen>
|
||||
<!-- Size of bubble icon bitmap. -->
|
||||
<dimen name="bubble_icon_bitmap_size">52dp</dimen>
|
||||
<dimen name="bubble_overflow_icon_bitmap_size">24dp</dimen>
|
||||
<!-- Extra padding added to the touchable rect for bubbles so they are easier to grab. -->
|
||||
<dimen name="bubble_touch_padding">12dp</dimen>
|
||||
<!-- Size of the circle around the bubbles when they're in the dismiss target. -->
|
||||
@@ -1141,6 +1143,12 @@
|
||||
<dimen name="bubble_expanded_view_slop">8dp</dimen>
|
||||
<!-- Default (and minimum) height of the expanded view shown when the bubble is expanded -->
|
||||
<dimen name="bubble_expanded_default_height">180dp</dimen>
|
||||
<!-- Default height of bubble overflow -->
|
||||
<dimen name="bubble_overflow_height">380dp</dimen>
|
||||
<!-- Bubble overflow padding when there are no bubbles -->
|
||||
<dimen name="bubble_overflow_empty_state_padding">16dp</dimen>
|
||||
<!-- Margin of overflow bubbles -->
|
||||
<dimen name="bubble_overflow_margin">16dp</dimen>
|
||||
<!-- Height of the triangle that points to the expanded bubble -->
|
||||
<dimen name="bubble_pointer_height">4dp</dimen>
|
||||
<!-- Width of the triangle that points to the expanded bubble -->
|
||||
|
||||
@@ -17,7 +17,6 @@ package com.android.systemui.bubbles;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.Rect;
|
||||
@@ -50,9 +49,9 @@ public class BadgedImageView extends ImageView {
|
||||
// Flyout gets shown before the dot
|
||||
private int mCurrentDotState = DOT_STATE_SUPPRESSED_FOR_FLYOUT;
|
||||
|
||||
private Bubble mBubble;
|
||||
private BubbleViewProvider mBubble;
|
||||
|
||||
private int mIconBitmapSize;
|
||||
private int mBubbleBitmapSize;
|
||||
private DotRenderer mDotRenderer;
|
||||
private DotRenderer.DrawParams mDrawParams;
|
||||
private boolean mOnLeft;
|
||||
@@ -78,18 +77,18 @@ public class BadgedImageView extends ImageView {
|
||||
public BadgedImageView(Context context, AttributeSet attrs, int defStyleAttr,
|
||||
int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
mIconBitmapSize = getResources().getDimensionPixelSize(R.dimen.bubble_icon_bitmap_size);
|
||||
mBubbleBitmapSize = getResources().getDimensionPixelSize(R.dimen.bubble_bitmap_size);
|
||||
mDrawParams = new DotRenderer.DrawParams();
|
||||
|
||||
Path iconPath = PathParser.createPathFromPathData(
|
||||
getResources().getString(com.android.internal.R.string.config_icon_mask));
|
||||
mDotRenderer = new DotRenderer(mIconBitmapSize, iconPath, DEFAULT_PATH_SIZE);
|
||||
mDotRenderer = new DotRenderer(mBubbleBitmapSize, iconPath, DEFAULT_PATH_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the view with provided info.
|
||||
*/
|
||||
public void update(Bubble bubble) {
|
||||
public void update(BubbleViewProvider bubble) {
|
||||
mBubble = bubble;
|
||||
setImageBitmap(bubble.getBadgedImage());
|
||||
setDotState(DOT_STATE_SUPPRESSED_FOR_FLYOUT);
|
||||
@@ -147,7 +146,7 @@ public class BadgedImageView extends ImageView {
|
||||
* @param iconPath The new icon path to use when calculating dot position.
|
||||
*/
|
||||
void drawDot(Path iconPath) {
|
||||
mDotRenderer = new DotRenderer(mIconBitmapSize, iconPath, DEFAULT_PATH_SIZE);
|
||||
mDotRenderer = new DotRenderer(mBubbleBitmapSize, iconPath, DEFAULT_PATH_SIZE);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
|
||||
@@ -95,6 +95,7 @@ class Bubble implements BubbleViewProvider {
|
||||
private int mDotColor;
|
||||
private Path mDotPath;
|
||||
|
||||
|
||||
public static String groupId(NotificationEntry entry) {
|
||||
UserHandle user = entry.getSbn().getUser();
|
||||
return user.getIdentifier() + "|" + entry.getSbn().getPackageName();
|
||||
@@ -111,6 +112,7 @@ class Bubble implements BubbleViewProvider {
|
||||
mSuppressionListener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKey() {
|
||||
return mKey;
|
||||
}
|
||||
@@ -127,14 +129,17 @@ class Bubble implements BubbleViewProvider {
|
||||
return mEntry.getSbn().getPackageName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bitmap getBadgedImage() {
|
||||
return mBadgedImage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDotColor() {
|
||||
return mDotColor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path getDotPath() {
|
||||
return mDotPath;
|
||||
}
|
||||
@@ -150,10 +155,12 @@ class Bubble implements BubbleViewProvider {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BadgedImageView getIconView() {
|
||||
return mIconView;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public BubbleExpandedView getExpandedView() {
|
||||
return mExpandedView;
|
||||
@@ -240,6 +247,7 @@ class Bubble implements BubbleViewProvider {
|
||||
* Note that this contents visibility doesn't affect visibility at {@link android.view.View},
|
||||
* and setting {@code false} actually means rendering the expanded view in transparent.
|
||||
*/
|
||||
@Override
|
||||
public void setContentVisibility(boolean visibility) {
|
||||
if (mExpandedView != null) {
|
||||
mExpandedView.setContentVisibility(visibility);
|
||||
@@ -333,7 +341,8 @@ class Bubble implements BubbleViewProvider {
|
||||
/**
|
||||
* Whether the bubble for this notification should show a dot indicating updated content.
|
||||
*/
|
||||
boolean showDot() {
|
||||
@Override
|
||||
public boolean showDot() {
|
||||
return mShowBubbleUpdateDot
|
||||
&& !mEntry.shouldSuppressNotificationDot()
|
||||
&& !shouldSuppressNotification();
|
||||
@@ -484,6 +493,7 @@ class Bubble implements BubbleViewProvider {
|
||||
return Objects.hash(mKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logUIEvent(int bubbleCount, int action, float normalX, float normalY, int index) {
|
||||
if (this.getEntry() == null
|
||||
|| this.getEntry().getSbn() == null) {
|
||||
|
||||
@@ -94,6 +94,7 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
|
||||
|
||||
private Point mDisplaySize;
|
||||
private int mMinHeight;
|
||||
private int mOverflowHeight;
|
||||
private int mSettingsIconHeight;
|
||||
private int mPointerWidth;
|
||||
private int mPointerHeight;
|
||||
@@ -218,6 +219,7 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
|
||||
mWindowManager.getDefaultDisplay().getRealSize(mDisplaySize);
|
||||
Resources res = getResources();
|
||||
mMinHeight = res.getDimensionPixelSize(R.dimen.bubble_expanded_default_height);
|
||||
mOverflowHeight = res.getDimensionPixelSize(R.dimen.bubble_overflow_height);
|
||||
mPointerMargin = res.getDimensionPixelSize(R.dimen.bubble_pointer_margin);
|
||||
mExpandedViewTouchSlop = res.getDimensionPixelSize(R.dimen.bubble_expanded_view_slop);
|
||||
}
|
||||
@@ -420,20 +422,19 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO(138116789) Fix overflow height.
|
||||
void updateHeight() {
|
||||
if (DEBUG_BUBBLE_EXPANDED_VIEW) {
|
||||
Log.d(TAG, "updateHeight: bubble=" + getBubbleKey());
|
||||
}
|
||||
if (usingActivityView()) {
|
||||
float desiredHeight = mMinHeight;
|
||||
float desiredHeight = mOverflowHeight;
|
||||
if (!mIsOverflow) {
|
||||
desiredHeight = Math.max(mBubble.getDesiredHeight(mContext), mMinHeight);
|
||||
}
|
||||
float height = Math.min(desiredHeight, getMaxExpandedHeight());
|
||||
height = Math.max(height, mMinHeight);
|
||||
height = Math.max(height, mIsOverflow? mOverflowHeight : mMinHeight);
|
||||
LayoutParams lp = (LayoutParams) mActivityView.getLayoutParams();
|
||||
mNeedsNewHeight = lp.height != height;
|
||||
mNeedsNewHeight = lp.height != height;
|
||||
if (!mKeyboardVisible) {
|
||||
// If the keyboard is visible... don't adjust the height because that will cause
|
||||
// a configuration change and the keyboard will be lost.
|
||||
|
||||
@@ -58,7 +58,7 @@ public class BubbleFlyoutView extends FrameLayout {
|
||||
private final int mFlyoutSpaceFromBubble;
|
||||
private final int mPointerSize;
|
||||
private final int mBubbleSize;
|
||||
private final int mBubbleIconBitmapSize;
|
||||
private final int mBubbleBitmapSize;
|
||||
private final float mBubbleIconTopPadding;
|
||||
|
||||
private final int mFlyoutElevation;
|
||||
@@ -156,13 +156,13 @@ public class BubbleFlyoutView extends FrameLayout {
|
||||
mPointerSize = res.getDimensionPixelSize(R.dimen.bubble_flyout_pointer_size);
|
||||
|
||||
mBubbleSize = res.getDimensionPixelSize(R.dimen.individual_bubble_size);
|
||||
mBubbleIconBitmapSize = res.getDimensionPixelSize(R.dimen.bubble_icon_bitmap_size);
|
||||
mBubbleIconTopPadding = (mBubbleSize - mBubbleIconBitmapSize) / 2f;
|
||||
mBubbleBitmapSize = res.getDimensionPixelSize(R.dimen.bubble_bitmap_size);
|
||||
mBubbleIconTopPadding = (mBubbleSize - mBubbleBitmapSize) / 2f;
|
||||
|
||||
mBubbleElevation = res.getDimensionPixelSize(R.dimen.bubble_elevation);
|
||||
mFlyoutElevation = res.getDimensionPixelSize(R.dimen.bubble_flyout_elevation);
|
||||
|
||||
mOriginalDotSize = SIZE_PERCENTAGE * mBubbleIconBitmapSize;
|
||||
mOriginalDotSize = SIZE_PERCENTAGE * mBubbleBitmapSize;
|
||||
mNewDotRadius = (DOT_SCALE * mOriginalDotSize) / 2f;
|
||||
mNewDotSize = mNewDotRadius * 2f;
|
||||
|
||||
|
||||
@@ -17,18 +17,23 @@
|
||||
package com.android.systemui.bubbles;
|
||||
|
||||
import static android.view.View.GONE;
|
||||
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
|
||||
|
||||
import static com.android.systemui.bubbles.BadgedImageView.DEFAULT_PATH_SIZE;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.drawable.AdaptiveIconDrawable;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.graphics.drawable.InsetDrawable;
|
||||
import android.util.PathParser;
|
||||
import android.util.TypedValue;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import com.android.systemui.R;
|
||||
@@ -37,15 +42,24 @@ import com.android.systemui.R;
|
||||
* Class for showing aged out bubbles.
|
||||
*/
|
||||
public class BubbleOverflow implements BubbleViewProvider {
|
||||
public static final String KEY = "Overflow";
|
||||
|
||||
private ImageView mOverflowBtn;
|
||||
private BadgedImageView mOverflowBtn;
|
||||
private BubbleExpandedView mOverflowExpandedView;
|
||||
private LayoutInflater mInflater;
|
||||
private Context mContext;
|
||||
private Bitmap mIcon;
|
||||
private Path mPath;
|
||||
private int mBitmapSize;
|
||||
private int mIconBitmapSize;
|
||||
private int mDotColor;
|
||||
|
||||
public BubbleOverflow(Context context) {
|
||||
mContext = context;
|
||||
mInflater = LayoutInflater.from(context);
|
||||
mBitmapSize = mContext.getResources().getDimensionPixelSize(R.dimen.bubble_bitmap_size);
|
||||
mIconBitmapSize = mContext.getResources().getDimensionPixelSize(
|
||||
R.dimen.bubble_overflow_icon_bitmap_size);
|
||||
}
|
||||
|
||||
public void setUpOverflow(ViewGroup parentViewGroup) {
|
||||
@@ -54,12 +68,49 @@ public class BubbleOverflow implements BubbleViewProvider {
|
||||
false /* attachToRoot */);
|
||||
mOverflowExpandedView.setOverflow(true);
|
||||
|
||||
mOverflowBtn = (ImageView) mInflater.inflate(R.layout.bubble_overflow_button,
|
||||
updateIcon(mContext, parentViewGroup);
|
||||
}
|
||||
|
||||
// TODO(b/149146374) Propagate theme change to bubbles in overflow.
|
||||
void updateIcon(Context context, ViewGroup parentViewGroup) {
|
||||
mInflater = LayoutInflater.from(context);
|
||||
mOverflowBtn = (BadgedImageView) mInflater.inflate(R.layout.bubble_overflow_button,
|
||||
parentViewGroup /* root */,
|
||||
false /* attachToRoot */);
|
||||
|
||||
setOverflowBtnTheme();
|
||||
TypedArray ta = mContext.obtainStyledAttributes(
|
||||
new int[]{android.R.attr.colorBackgroundFloating});
|
||||
int bgColor = ta.getColor(0, Color.WHITE /* default */);
|
||||
ta.recycle();
|
||||
|
||||
TypedValue typedValue = new TypedValue();
|
||||
context.getTheme().resolveAttribute(android.R.attr.colorAccent, typedValue, true);
|
||||
int colorAccent = mContext.getColor(typedValue.resourceId);
|
||||
mOverflowBtn.getDrawable().setTint(colorAccent);
|
||||
mDotColor = colorAccent;
|
||||
|
||||
ColorDrawable bg = new ColorDrawable(bgColor);
|
||||
InsetDrawable fg = new InsetDrawable(mOverflowBtn.getDrawable(),
|
||||
mBitmapSize - mIconBitmapSize /* inset */);
|
||||
AdaptiveIconDrawable adaptiveIconDrawable = new AdaptiveIconDrawable(bg, fg);
|
||||
|
||||
BubbleIconFactory iconFactory = new BubbleIconFactory(context);
|
||||
mIcon = iconFactory.createBadgedIconBitmap(adaptiveIconDrawable,
|
||||
null /* user */,
|
||||
true /* shrinkNonAdaptiveIcons */).icon;
|
||||
|
||||
float scale = iconFactory.getNormalizer().getScale(mOverflowBtn.getDrawable(),
|
||||
null /* outBounds */, null /* path */, null /* outMaskShape */);
|
||||
float radius = DEFAULT_PATH_SIZE / 2f;
|
||||
mPath = PathParser.createPathFromPathData(
|
||||
context.getResources().getString(com.android.internal.R.string.config_icon_mask));
|
||||
Matrix matrix = new Matrix();
|
||||
matrix.setScale(scale /* x scale */, scale /* y scale */, radius /* pivot x */,
|
||||
radius /* pivot y */);
|
||||
mPath.transform(matrix);
|
||||
|
||||
mOverflowBtn.setVisibility(GONE);
|
||||
mOverflowBtn.update(this);
|
||||
}
|
||||
|
||||
ImageView getBtn() {
|
||||
@@ -70,38 +121,49 @@ public class BubbleOverflow implements BubbleViewProvider {
|
||||
mOverflowBtn.setVisibility(visible);
|
||||
}
|
||||
|
||||
// TODO(b/149146374) Propagate theme change to bubbles in overflow.
|
||||
void setOverflowBtnTheme() {
|
||||
TypedArray ta = mContext.obtainStyledAttributes(
|
||||
new int[]{android.R.attr.colorBackgroundFloating});
|
||||
int bgColor = ta.getColor(0, Color.WHITE /* default */);
|
||||
ta.recycle();
|
||||
|
||||
InsetDrawable fg = new InsetDrawable(mOverflowBtn.getDrawable(), 28);
|
||||
ColorDrawable bg = new ColorDrawable(bgColor);
|
||||
AdaptiveIconDrawable adaptiveIcon = new AdaptiveIconDrawable(bg, fg);
|
||||
mOverflowBtn.setImageDrawable(adaptiveIcon);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public BubbleExpandedView getExpandedView() {
|
||||
return mOverflowExpandedView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDotColor() {
|
||||
return mDotColor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bitmap getBadgedImage() {
|
||||
return mIcon;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean showDot() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path getDotPath() {
|
||||
return mPath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContentVisibility(boolean visible) {
|
||||
mOverflowExpandedView.setContentVisibility(visible);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logUIEvent(int bubbleCount, int action, float normalX, float normalY,
|
||||
int index) {
|
||||
// TODO(b/149133814) Log overflow UI events.
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getIconView() {
|
||||
return mOverflowBtn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKey() {
|
||||
return BubbleOverflowActivity.KEY;
|
||||
return BubbleOverflow.KEY;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,6 @@ import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.recyclerview.widget.GridLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
@@ -47,7 +46,6 @@ import javax.inject.Inject;
|
||||
* Must be public to be accessible to androidx...AppComponentFactory
|
||||
*/
|
||||
public class BubbleOverflowActivity extends Activity {
|
||||
public static final String KEY = "Overflow";
|
||||
private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleOverflowActivity" : TAG_BUBBLES;
|
||||
|
||||
private LinearLayout mEmptyState;
|
||||
@@ -55,7 +53,6 @@ public class BubbleOverflowActivity extends Activity {
|
||||
private BubbleOverflowAdapter mAdapter;
|
||||
private RecyclerView mRecyclerView;
|
||||
private List<Bubble> mOverflowBubbles = new ArrayList<>();
|
||||
private int mMaxBubbles;
|
||||
|
||||
@Inject
|
||||
public BubbleOverflowActivity(BubbleController controller) {
|
||||
@@ -68,17 +65,16 @@ public class BubbleOverflowActivity extends Activity {
|
||||
setContentView(R.layout.bubble_overflow_activity);
|
||||
setBackgroundColor();
|
||||
|
||||
mMaxBubbles = getResources().getInteger(R.integer.bubbles_max_rendered);
|
||||
mEmptyState = findViewById(R.id.bubble_overflow_empty_state);
|
||||
mRecyclerView = findViewById(R.id.bubble_overflow_recycler);
|
||||
mRecyclerView.setLayoutManager(
|
||||
new GridLayoutManager(getApplicationContext(),
|
||||
getResources().getInteger(R.integer.bubbles_overflow_columns)));
|
||||
|
||||
int bubbleMargin = getResources().getDimensionPixelSize(R.dimen.bubble_overflow_margin);
|
||||
mAdapter = new BubbleOverflowAdapter(mOverflowBubbles,
|
||||
mBubbleController::promoteBubbleFromOverflow);
|
||||
mBubbleController::promoteBubbleFromOverflow, bubbleMargin);
|
||||
mRecyclerView.setAdapter(mAdapter);
|
||||
|
||||
onDataChanged(mBubbleController.getOverflowBubbles());
|
||||
mBubbleController.setOverflowCallback(() -> {
|
||||
onDataChanged(mBubbleController.getOverflowBubbles());
|
||||
@@ -95,11 +91,7 @@ public class BubbleOverflowActivity extends Activity {
|
||||
|
||||
void onDataChanged(List<Bubble> bubbles) {
|
||||
mOverflowBubbles.clear();
|
||||
if (bubbles.size() > mMaxBubbles) {
|
||||
mOverflowBubbles.addAll(bubbles.subList(mMaxBubbles, bubbles.size()));
|
||||
} else {
|
||||
mOverflowBubbles.addAll(bubbles);
|
||||
}
|
||||
mOverflowBubbles.addAll(bubbles);
|
||||
mAdapter.notifyDataSetChanged();
|
||||
|
||||
if (mOverflowBubbles.isEmpty()) {
|
||||
@@ -147,10 +139,13 @@ public class BubbleOverflowActivity extends Activity {
|
||||
class BubbleOverflowAdapter extends RecyclerView.Adapter<BubbleOverflowAdapter.ViewHolder> {
|
||||
private Consumer<Bubble> mPromoteBubbleFromOverflow;
|
||||
private List<Bubble> mBubbles;
|
||||
private int mBubbleMargin;
|
||||
|
||||
public BubbleOverflowAdapter(List<Bubble> list, Consumer<Bubble> promoteBubble) {
|
||||
public BubbleOverflowAdapter(List<Bubble> list, Consumer<Bubble> promoteBubble,
|
||||
int bubbleMargin) {
|
||||
mBubbles = list;
|
||||
mPromoteBubbleFromOverflow = promoteBubble;
|
||||
mBubbleMargin = bubbleMargin;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -158,6 +153,12 @@ class BubbleOverflowAdapter extends RecyclerView.Adapter<BubbleOverflowAdapter.V
|
||||
int viewType) {
|
||||
BadgedImageView view = (BadgedImageView) LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.bubble_view, parent, false);
|
||||
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||
);
|
||||
params.setMargins(mBubbleMargin, mBubbleMargin, mBubbleMargin, mBubbleMargin);
|
||||
view.setLayoutParams(params);
|
||||
return new ViewHolder(view);
|
||||
}
|
||||
|
||||
|
||||
@@ -408,13 +408,7 @@ public class BubbleStackView extends FrameLayout {
|
||||
setFocusable(true);
|
||||
mBubbleContainer.bringToFront();
|
||||
|
||||
mBubbleOverflow = new BubbleOverflow(mContext);
|
||||
if (BubbleExperimentConfig.allowBubbleOverflow(mContext)) {
|
||||
mBubbleOverflow.setUpOverflow(this);
|
||||
mBubbleContainer.addView(mBubbleOverflow.getBtn(), 0,
|
||||
new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
|
||||
|
||||
}
|
||||
setUpOverflow();
|
||||
|
||||
setOnApplyWindowInsetsListener((View view, WindowInsets insets) -> {
|
||||
if (!mIsExpanded || mIsExpansionAnimating) {
|
||||
@@ -523,14 +517,29 @@ public class BubbleStackView extends FrameLayout {
|
||||
addView(mFlyout, new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
|
||||
}
|
||||
|
||||
private void setUpOverflow() {
|
||||
if (!BubbleExperimentConfig.allowBubbleOverflow(mContext)) {
|
||||
return;
|
||||
}
|
||||
int overflowBtnIndex = 0;
|
||||
if (mBubbleOverflow == null) {
|
||||
mBubbleOverflow = new BubbleOverflow(mContext);
|
||||
mBubbleOverflow.setUpOverflow(this);
|
||||
} else {
|
||||
mBubbleContainer.removeView(mBubbleOverflow.getBtn());
|
||||
mBubbleOverflow.updateIcon(mContext, this);
|
||||
overflowBtnIndex = mBubbleContainer.getChildCount() - 1;
|
||||
}
|
||||
mBubbleContainer.addView(mBubbleOverflow.getBtn(), overflowBtnIndex,
|
||||
new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
|
||||
|
||||
}
|
||||
/**
|
||||
* Handle theme changes.
|
||||
*/
|
||||
public void onThemeChanged() {
|
||||
setUpFlyout();
|
||||
if (BubbleExperimentConfig.allowBubbleOverflow(mContext)) {
|
||||
mBubbleOverflow.setOverflowBtnTheme();
|
||||
}
|
||||
setUpOverflow();
|
||||
}
|
||||
|
||||
/** Respond to the phone being rotated by repositioning the stack and hiding any flyouts. */
|
||||
@@ -724,7 +733,7 @@ public class BubbleStackView extends FrameLayout {
|
||||
if (mExpandedBubble == null
|
||||
|| (BubbleExperimentConfig.allowBubbleOverflow(mContext)
|
||||
&& mExpandedBubble.getIconView() == mBubbleOverflow.getBtn()
|
||||
&& mExpandedBubble.getKey() == BubbleOverflowActivity.KEY)) {
|
||||
&& mExpandedBubble.getKey() == BubbleOverflow.KEY)) {
|
||||
return null;
|
||||
}
|
||||
return (Bubble) mExpandedBubble;
|
||||
@@ -1649,7 +1658,7 @@ public class BubbleStackView extends FrameLayout {
|
||||
* is between 0 and the bubble count minus 1.
|
||||
*/
|
||||
int getBubbleIndex(@Nullable BubbleViewProvider provider) {
|
||||
if (provider == null || provider.getKey() == BubbleOverflowActivity.KEY) {
|
||||
if (provider == null || provider.getKey() == BubbleOverflow.KEY) {
|
||||
return 0;
|
||||
}
|
||||
Bubble b = (Bubble) provider;
|
||||
|
||||
@@ -100,9 +100,6 @@ class BubbleTouchHandler implements View.OnTouchListener {
|
||||
&& !(mTouchedView instanceof BubbleStackView)
|
||||
&& !(mTouchedView instanceof BubbleFlyoutView)) {
|
||||
|
||||
if (mTouchedView.getId() == R.id.bubble_overflow_button) {
|
||||
mStack.showOverflow();
|
||||
}
|
||||
// Not touching anything touchable, but we shouldn't collapse (e.g. touching edge
|
||||
// of expanded view).
|
||||
resetForNextGesture();
|
||||
@@ -225,9 +222,12 @@ class BubbleTouchHandler implements View.OnTouchListener {
|
||||
mBubbleData.setExpanded(!mBubbleData.isExpanded());
|
||||
} else {
|
||||
final String key = ((BadgedImageView) mTouchedView).getKey();
|
||||
mBubbleData.setSelectedBubble(mBubbleData.getBubbleWithKey(key));
|
||||
if (key == BubbleOverflow.KEY) {
|
||||
mStack.showOverflow();
|
||||
} else {
|
||||
mBubbleData.setSelectedBubble(mBubbleData.getBubbleWithKey(key));
|
||||
}
|
||||
}
|
||||
|
||||
resetForNextGesture();
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package com.android.systemui.bubbles;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Path;
|
||||
import android.view.View;
|
||||
|
||||
/**
|
||||
@@ -23,8 +25,20 @@ import android.view.View;
|
||||
*/
|
||||
interface BubbleViewProvider {
|
||||
BubbleExpandedView getExpandedView();
|
||||
|
||||
void setContentVisibility(boolean visible);
|
||||
|
||||
View getIconView();
|
||||
|
||||
void logUIEvent(int bubbleCount, int action, float normalX, float normalY, int index);
|
||||
|
||||
String getKey();
|
||||
|
||||
Bitmap getBadgedImage();
|
||||
|
||||
int getDotColor();
|
||||
|
||||
Path getDotPath();
|
||||
|
||||
boolean showDot();
|
||||
}
|
||||
|
||||
@@ -147,7 +147,7 @@ public class StackAnimationController extends
|
||||
/** Horizontal offset of bubbles in the stack. */
|
||||
private float mStackOffset;
|
||||
/** Diameter of the bubble icon. */
|
||||
private int mBubbleIconBitmapSize;
|
||||
private int mBubbleBitmapSize;
|
||||
/** Width of the bubble (icon and padding). */
|
||||
private int mBubbleSize;
|
||||
/**
|
||||
@@ -194,7 +194,7 @@ public class StackAnimationController extends
|
||||
return false;
|
||||
}
|
||||
|
||||
float stackCenter = mStackPosition.x + mBubbleIconBitmapSize / 2;
|
||||
float stackCenter = mStackPosition.x + mBubbleBitmapSize / 2;
|
||||
float screenCenter = mLayout.getWidth() / 2;
|
||||
return stackCenter < screenCenter;
|
||||
}
|
||||
@@ -227,7 +227,7 @@ public class StackAnimationController extends
|
||||
* @return The X value that the stack will end up at after the fling/spring.
|
||||
*/
|
||||
public float flingStackThenSpringToEdge(float x, float velX, float velY) {
|
||||
final boolean stackOnLeftSide = x - mBubbleIconBitmapSize / 2 < mLayout.getWidth() / 2;
|
||||
final boolean stackOnLeftSide = x - mBubbleBitmapSize / 2 < mLayout.getWidth() / 2;
|
||||
|
||||
final boolean stackShouldFlingLeft = stackOnLeftSide
|
||||
? velX < ESCAPE_VELOCITY
|
||||
@@ -542,7 +542,7 @@ public class StackAnimationController extends
|
||||
new SpringForce()
|
||||
.setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY)
|
||||
.setStiffness(SpringForce.STIFFNESS_MEDIUM),
|
||||
velX, mLayout.getWidth() / 2f - mBubbleIconBitmapSize / 2f);
|
||||
velX, mLayout.getWidth() / 2f - mBubbleBitmapSize / 2f);
|
||||
|
||||
springFirstBubbleWithStackFollowing(
|
||||
DynamicAnimation.TRANSLATION_Y,
|
||||
@@ -705,7 +705,7 @@ public class StackAnimationController extends
|
||||
Resources res = layout.getResources();
|
||||
mStackOffset = res.getDimensionPixelSize(R.dimen.bubble_stack_offset);
|
||||
mBubbleSize = res.getDimensionPixelSize(R.dimen.individual_bubble_size);
|
||||
mBubbleIconBitmapSize = res.getDimensionPixelSize(R.dimen.bubble_icon_bitmap_size);
|
||||
mBubbleBitmapSize = res.getDimensionPixelSize(R.dimen.bubble_bitmap_size);
|
||||
mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top);
|
||||
mBubbleOffscreen = res.getDimensionPixelSize(R.dimen.bubble_stack_offscreen);
|
||||
mStackStartingVerticalOffset =
|
||||
|
||||
Reference in New Issue
Block a user