diff options
Diffstat (limited to 'java/com/android/dialershared/bubble')
-rw-r--r-- | java/com/android/dialershared/bubble/Bubble.java | 182 | ||||
-rw-r--r-- | java/com/android/dialershared/bubble/BubbleInfo.java | 19 | ||||
-rw-r--r-- | java/com/android/dialershared/bubble/MoveHandler.java | 22 | ||||
-rw-r--r-- | java/com/android/dialershared/bubble/g3doc/INTEGRATION.md | 69 | ||||
-rw-r--r-- | java/com/android/dialershared/bubble/g3doc/images/bubble_collapsed.png | bin | 0 -> 60187 bytes | |||
-rw-r--r-- | java/com/android/dialershared/bubble/g3doc/images/bubble_expanded.png | bin | 0 -> 79674 bytes | |||
-rw-r--r-- | java/com/android/dialershared/bubble/g3doc/images/bubble_state.png | bin | 0 -> 83470 bytes | |||
-rw-r--r-- | java/com/android/dialershared/bubble/g3doc/images/bubble_text.png | bin | 0 -> 65641 bytes | |||
-rw-r--r-- | java/com/android/dialershared/bubble/res/layout/bubble_base.xml | 25 | ||||
-rw-r--r-- | java/com/android/dialershared/bubble/res/values/values.xml | 10 |
10 files changed, 263 insertions, 64 deletions
diff --git a/java/com/android/dialershared/bubble/Bubble.java b/java/com/android/dialershared/bubble/Bubble.java index 3eb88aa22..dbb5ea759 100644 --- a/java/com/android/dialershared/bubble/Bubble.java +++ b/java/com/android/dialershared/bubble/Bubble.java @@ -26,8 +26,12 @@ import android.content.Context; import android.content.Intent; import android.content.res.ColorStateList; import android.graphics.PixelFormat; +import android.graphics.drawable.Animatable; +import android.graphics.drawable.Drawable; import android.graphics.drawable.RippleDrawable; import android.net.Uri; +import android.os.Build.VERSION; +import android.os.Build.VERSION_CODES; import android.os.Handler; import android.provider.Settings; import android.support.annotation.ColorInt; @@ -47,6 +51,7 @@ import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; +import android.view.ViewGroup.MarginLayoutParams; import android.view.ViewPropertyAnimator; import android.view.ViewTreeObserver.OnPreDrawListener; import android.view.WindowManager; @@ -69,6 +74,8 @@ import java.util.List; * convenience) */ public class Bubble { + // This class has some odd behavior that is not immediately obvious in order to avoid jank when + // resizing. See http://go/bubble-resize for details. // How long text should show after showText(CharSequence) is called private static final int SHOW_TEXT_DURATION_MILLIS = 3000; @@ -96,6 +103,8 @@ public class Bubble { private final Handler handler = new Handler(); private ViewHolder viewHolder; + private ViewPropertyAnimator collapseAnimation; + private Integer overrideGravity; @Retention(RetentionPolicy.SOURCE) @IntDef({CollapseEnd.NOTHING, CollapseEnd.HIDE}) @@ -114,7 +123,7 @@ public class Bubble { public static boolean canShowBubbles(@NonNull Context context) { return canShowBubblesForTesting != null ? canShowBubblesForTesting - : Settings.canDrawOverlays(context); + : VERSION.SDK_INT < VERSION_CODES.M || Settings.canDrawOverlays(context); } @VisibleForTesting(otherwise = VisibleForTesting.NONE) @@ -127,7 +136,7 @@ public class Bubble { public static Intent getRequestPermissionIntent(@NonNull Context context) { return new Intent( Settings.ACTION_MANAGE_OVERLAY_PERMISSION, - new Uri.Builder().scheme("package").fragment(context.getPackageName()).build()); + Uri.fromParts("package", context.getPackageName(), null)); } /** Creates instances of Bubble. The default implementation just calls the constructor. */ @@ -183,13 +192,12 @@ public class Bubble { type, LayoutParams.FLAG_NOT_TOUCH_MODAL | LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH - | LayoutParams.FLAG_NOT_FOCUSABLE, + | LayoutParams.FLAG_NOT_FOCUSABLE + | LayoutParams.FLAG_LAYOUT_NO_LIMITS, PixelFormat.TRANSLUCENT); windowParams.gravity = Gravity.TOP | Gravity.LEFT; - windowParams.x = - context.getResources().getDimensionPixelOffset(R.dimen.bubble_initial_offset_x); - windowParams.y = - context.getResources().getDimensionPixelOffset(R.dimen.bubble_initial_offset_y); + windowParams.x = context.getResources().getDimensionPixelOffset(R.dimen.bubble_safe_margin_x); + windowParams.y = currentInfo.getStartingYPosition(); windowParams.height = LayoutParams.WRAP_CONTENT; windowParams.width = LayoutParams.WRAP_CONTENT; } @@ -203,6 +211,7 @@ public class Bubble { showAnimator.setInterpolator(new OvershootInterpolator()); showAnimator.start(); isShowing = true; + updatePrimaryIconAnimation(); } /** @@ -235,6 +244,7 @@ public class Bubble { () -> { windowManager.removeView(viewHolder.getRoot()); isShowing = false; + updatePrimaryIconAnimation(); }) .start(); } @@ -342,6 +352,11 @@ public class Bubble { SHOW_TEXT_DURATION_MILLIS); } + @Nullable + Integer getGravityOverride() { + return overrideGravity; + } + void onMoveStart() { startCollapse(CollapseEnd.NOTHING); viewHolder @@ -353,23 +368,27 @@ public class Bubble { void onMoveFinish() { viewHolder.getPrimaryButton().animate().translationZ(0); + // If it's GONE, no resize is necessary. If it's VISIBLE, it will get cleaned up when the + // collapse animation finishes + if (viewHolder.getExpandedView().getVisibility() == View.INVISIBLE) { + doResize(null); + } } void primaryButtonClick() { if (expanded || textShowing || currentInfo.getActions().isEmpty()) { try { - currentInfo.getPrimaryAction().send(); + currentInfo.getPrimaryIntent().send(); } catch (CanceledException e) { throw new RuntimeException(e); } return; } - boolean onRight = (windowParams.gravity & Gravity.RIGHT) == Gravity.RIGHT; doResize( () -> { - onLeftRightSwitch(onRight); - viewHolder.getExpandedView().setVisibility(View.VISIBLE); + onLeftRightSwitch(isDrawingFromRight()); + viewHolder.setDrawerVisibility(View.VISIBLE); }); View expandedView = viewHolder.getExpandedView(); expandedView @@ -380,7 +399,7 @@ public class Bubble { public boolean onPreDraw() { expandedView.getViewTreeObserver().removeOnPreDrawListener(this); expandedView.setTranslationX( - onRight ? expandedView.getWidth() : -expandedView.getWidth()); + isDrawingFromRight() ? expandedView.getWidth() : -expandedView.getWidth()); expandedView .animate() .setInterpolator(new LinearOutSlowInInterpolator()) @@ -393,6 +412,14 @@ public class Bubble { } void onLeftRightSwitch(boolean onRight) { + if (viewHolder.isMoving()) { + if (viewHolder.getExpandedView().getVisibility() == View.GONE) { + // If the drawer is not part of the layout we don't need to do anything. Layout flips will + // happen if necessary when opening the drawer. + return; + } + } + viewHolder .getRoot() .setLayoutDirection(onRight ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR); @@ -437,6 +464,7 @@ public class Bubble { viewHolder.getSecondButton().setVisibility(numButtons < 2 ? View.GONE : View.VISIBLE); viewHolder.getPrimaryIcon().setImageIcon(currentInfo.getPrimaryIcon()); + updatePrimaryIconAnimation(); viewHolder .getExpandedView() @@ -445,6 +473,17 @@ public class Bubble { updateButtonStates(); } + private void updatePrimaryIconAnimation() { + Drawable drawable = viewHolder.getPrimaryIcon().getDrawable(); + if (drawable instanceof Animatable) { + if (isShowing) { + ((Animatable) drawable).start(); + } else { + ((Animatable) drawable).stop(); + } + } + } + private void setBackgroundDrawable(CheckableImageButton view, @ColorInt int color) { RippleDrawable itemRipple = (RippleDrawable) @@ -492,7 +531,7 @@ public class Bubble { private void doAction(Action action) { try { - action.getAction().send(); + action.getIntent().send(); } catch (CanceledException e) { throw new RuntimeException(e); } @@ -504,9 +543,8 @@ public class Bubble { // would occur. To fix this, instead of resizing the window, we create a new one and destroy // the old one. There is a short delay before destroying the old view to ensure the new one has // had time to draw. - boolean onRight = (windowParams.gravity & Gravity.RIGHT) == Gravity.RIGHT; ViewHolder oldViewHolder = viewHolder; - if (onRight) { + if (isDrawingFromRight()) { viewHolder = new ViewHolder(oldViewHolder.getRoot().getContext()); update(); viewHolder @@ -519,12 +557,13 @@ public class Bubble { operation.run(); } - if (onRight) { + if (isDrawingFromRight()) { swapViewHolders(oldViewHolder); } } private void swapViewHolders(ViewHolder oldViewHolder) { + oldViewHolder.getShadowProvider().setVisibility(View.GONE); ViewGroup root = viewHolder.getRoot(); windowManager.addView(root, windowParams); root.getViewTreeObserver() @@ -542,32 +581,56 @@ public class Bubble { }); } - private ViewPropertyAnimator startCollapse(@CollapseEnd int collapseEndAction) { - setFocused(false); - boolean onRight = (windowParams.gravity & Gravity.RIGHT) == Gravity.RIGHT; + private void startCollapse(@CollapseEnd int collapseEndAction) { View expandedView = viewHolder.getExpandedView(); - return expandedView - .animate() - .translationX(onRight ? expandedView.getWidth() : -expandedView.getWidth()) - .setInterpolator(new FastOutLinearInInterpolator()) - .withEndAction( - () -> { - expanded = false; - if (collapseEndAction == CollapseEnd.HIDE) { - hide(); - } else if (!textShowing) { - // Don't swap the window while the user is moving it, even if we're on the right. - // The movement will help hide the jank of the resize. - boolean swapWindow = onRight && !viewHolder.isMoving(); - if (swapWindow) { - // We don't actually need to set the drawer to GONE since in the new window it - // will already be GONE. Just do the resize operation. - doResize(null); - } else { - expandedView.setVisibility(View.GONE); - } - } - }); + if (expandedView.getVisibility() != View.VISIBLE || collapseAnimation != null) { + // Drawer is already collapsed or animation is running. + return; + } + + overrideGravity = isDrawingFromRight() ? Gravity.RIGHT : Gravity.LEFT; + setFocused(false); + collapseAnimation = + expandedView + .animate() + .translationX(isDrawingFromRight() ? expandedView.getWidth() : -expandedView.getWidth()) + .setInterpolator(new FastOutLinearInInterpolator()) + .withEndAction( + () -> { + collapseAnimation = null; + expanded = false; + + if (textShowing) { + // Will do resize once the text is done. + return; + } + + // Hide the drawer and resize if possible. + viewHolder.setDrawerVisibility(View.INVISIBLE); + if (!viewHolder.isMoving() || !isDrawingFromRight()) { + doResize(() -> viewHolder.setDrawerVisibility(View.GONE)); + } + + // If this collapse was to come before a hide, do it now. + if (collapseEndAction == CollapseEnd.HIDE) { + hide(); + } + + // Resume normal gravity after any resizing is done. + handler.postDelayed( + () -> { + overrideGravity = null; + if (!viewHolder.isMoving()) { + viewHolder.undoGravityOverride(); + } + }, + // Need to wait twice as long for resize and layout + WINDOW_REDRAW_DELAY_MILLIS * 2); + }); + } + + private boolean isDrawingFromRight() { + return (windowParams.gravity & Gravity.RIGHT) == Gravity.RIGHT; } private void setFocused(boolean focused) { @@ -594,6 +657,7 @@ public class Bubble { private final CheckableImageButton secondButton; private final CheckableImageButton thirdButton; private final View expandedView; + private final View shadowProvider; public ViewHolder(Context context) { // Window root is not in the layout file so that the inflater has a view to inflate into @@ -604,6 +668,7 @@ public class Bubble { primaryButton = contentView.findViewById(R.id.bubble_button_primary); primaryIcon = contentView.findViewById(R.id.bubble_icon_primary); primaryText = contentView.findViewById(R.id.bubble_text); + shadowProvider = contentView.findViewById(R.id.bubble_drawer_shadow_provider); firstButton = contentView.findViewById(R.id.bubble_icon_first); secondButton = contentView.findViewById(R.id.bubble_icon_second); @@ -625,6 +690,28 @@ public class Bubble { } return false; }); + expandedView + .getViewTreeObserver() + .addOnDrawListener( + () -> { + int translationX = (int) expandedView.getTranslationX(); + int parentOffset = + ((MarginLayoutParams) ((ViewGroup) expandedView.getParent()).getLayoutParams()) + .leftMargin; + if (isDrawingFromRight()) { + int maxLeft = + shadowProvider.getRight() + - context.getResources().getDimensionPixelSize(R.dimen.bubble_size); + shadowProvider.setLeft( + Math.min(maxLeft, expandedView.getLeft() + translationX + parentOffset)); + } else { + int minRight = + shadowProvider.getLeft() + + context.getResources().getDimensionPixelSize(R.dimen.bubble_size); + shadowProvider.setRight( + Math.max(minRight, expandedView.getRight() + translationX + parentOffset)); + } + }); moveHandler = new MoveHandler(primaryButton, Bubble.this); } @@ -660,8 +747,21 @@ public class Bubble { return expandedView; } + public View getShadowProvider() { + return shadowProvider; + } + + public void setDrawerVisibility(int visibility) { + expandedView.setVisibility(visibility); + shadowProvider.setVisibility(visibility); + } + public boolean isMoving() { return moveHandler.isMoving(); } + + public void undoGravityOverride() { + moveHandler.undoGravityOverride(); + } } } diff --git a/java/com/android/dialershared/bubble/BubbleInfo.java b/java/com/android/dialershared/bubble/BubbleInfo.java index 52417ae7b..eb9abd059 100644 --- a/java/com/android/dialershared/bubble/BubbleInfo.java +++ b/java/com/android/dialershared/bubble/BubbleInfo.java @@ -20,6 +20,7 @@ import android.app.PendingIntent; import android.graphics.drawable.Icon; import android.support.annotation.ColorInt; import android.support.annotation.NonNull; +import android.support.annotation.Px; import com.google.auto.value.AutoValue; import java.util.Collections; import java.util.List; @@ -34,7 +35,10 @@ public abstract class BubbleInfo { public abstract Icon getPrimaryIcon(); @NonNull - public abstract PendingIntent getPrimaryAction(); + public abstract PendingIntent getPrimaryIntent(); + + @Px + public abstract int getStartingYPosition(); @NonNull public abstract List<Action> getActions(); @@ -45,9 +49,10 @@ public abstract class BubbleInfo { public static Builder from(@NonNull BubbleInfo bubbleInfo) { return builder() - .setPrimaryAction(bubbleInfo.getPrimaryAction()) + .setPrimaryIntent(bubbleInfo.getPrimaryIntent()) .setPrimaryColor(bubbleInfo.getPrimaryColor()) .setPrimaryIcon(bubbleInfo.getPrimaryIcon()) + .setStartingYPosition(bubbleInfo.getStartingYPosition()) .setActions(bubbleInfo.getActions()); } @@ -59,7 +64,9 @@ public abstract class BubbleInfo { public abstract Builder setPrimaryIcon(@NonNull Icon primaryIcon); - public abstract Builder setPrimaryAction(@NonNull PendingIntent primaryAction); + public abstract Builder setPrimaryIntent(@NonNull PendingIntent primaryIntent); + + public abstract Builder setStartingYPosition(@Px int startingYPosition); public abstract Builder setActions(List<Action> actions); @@ -77,7 +84,7 @@ public abstract class BubbleInfo { public abstract CharSequence getName(); @NonNull - public abstract PendingIntent getAction(); + public abstract PendingIntent getIntent(); public abstract boolean isEnabled(); @@ -89,7 +96,7 @@ public abstract class BubbleInfo { public static Builder from(@NonNull Action action) { return builder() - .setAction(action.getAction()) + .setIntent(action.getIntent()) .setChecked(action.isChecked()) .setEnabled(action.isEnabled()) .setName(action.getName()) @@ -104,7 +111,7 @@ public abstract class BubbleInfo { public abstract Builder setName(@NonNull CharSequence name); - public abstract Builder setAction(@NonNull PendingIntent action); + public abstract Builder setIntent(@NonNull PendingIntent intent); public abstract Builder setEnabled(boolean enabled); diff --git a/java/com/android/dialershared/bubble/MoveHandler.java b/java/com/android/dialershared/bubble/MoveHandler.java index 8a21cd7e1..bc6db64bc 100644 --- a/java/com/android/dialershared/bubble/MoveHandler.java +++ b/java/com/android/dialershared/bubble/MoveHandler.java @@ -39,7 +39,7 @@ class MoveHandler implements OnTouchListener { // Amount the ViewConfiguration's minFlingVelocity will be scaled by for our own minVelocity private static final int MIN_FLING_VELOCITY_FACTOR = 8; // The friction multiplier to control how slippery the bubble is when flung - private static final float SCROLL_FRICTION_MULTIPLIER = 8f; + private static final float SCROLL_FRICTION_MULTIPLIER = 4f; private final Context context; private final WindowManager windowManager; @@ -78,12 +78,22 @@ class MoveHandler implements OnTouchListener { @Override public void setValue(LayoutParams windowParams, float value) { + boolean wasOnRight = (windowParams.gravity & Gravity.RIGHT) == Gravity.RIGHT; int displayWidth = context.getResources().getDisplayMetrics().widthPixels; - boolean onRight = value > displayWidth / 2; + boolean onRight; + Integer gravityOverride = bubble.getGravityOverride(); + if (gravityOverride == null) { + onRight = value > displayWidth / 2; + } else { + onRight = (gravityOverride & Gravity.RIGHT) == Gravity.RIGHT; + } int centeringOffset = bubbleSize / 2 + shadowPaddingSize; windowParams.x = (int) (onRight ? (displayWidth - value - centeringOffset) : value - centeringOffset); windowParams.gravity = Gravity.TOP | (onRight ? Gravity.RIGHT : Gravity.LEFT); + if (wasOnRight != onRight) { + bubble.onLeftRightSwitch(onRight); + } if (bubble.isShowing()) { windowManager.updateViewLayout(bubble.getRootView(), windowParams); } @@ -134,6 +144,11 @@ class MoveHandler implements OnTouchListener { return isMoving; } + public void undoGravityOverride() { + LayoutParams windowParams = bubble.getWindowParams(); + xProperty.setValue(windowParams, xProperty.getValue(windowParams)); + } + @Override public boolean onTouch(View v, MotionEvent event) { float eventX = event.getRawX(); @@ -190,13 +205,12 @@ class MoveHandler implements OnTouchListener { } else { snapX(); } - + isMoving = false; bubble.onMoveFinish(); } else { v.performClick(); bubble.primaryButtonClick(); } - isMoving = false; break; } return true; diff --git a/java/com/android/dialershared/bubble/g3doc/INTEGRATION.md b/java/com/android/dialershared/bubble/g3doc/INTEGRATION.md new file mode 100644 index 000000000..a13a6053b --- /dev/null +++ b/java/com/android/dialershared/bubble/g3doc/INTEGRATION.md @@ -0,0 +1,69 @@ +# Floating Bubble Integration + +go/bubble-integration + +Author: keyboardr@ + +Last Updated: 2017-06-06 + +Floating bubbles provide a lightweight means of providing interactive UI while +the user is away from the app. This document details the steps necessary to +integrate these bubbles into your app. + +[TOC] + +![Floating bubble](images/bubble_collapsed.png){height=400} + +## Ensure Bubbles can be shown + +Add the `android.permission.SYSTEM_ALERT_WINDOW` permission to your manifest. +Before you show the bubble, call `Bubble.canShowBubbles(Context)` to see if the +user has granted you permission. If not, you can start an Activity from +`Bubble.getRequestPermissionIntent(Context)` to navigate the user to the system +settings to enable drawing over other apps. This is more than just a simple +runtime permission; the user must explicitly allow you to draw over other apps +via this system setting. System apps may have this allowed by default, but be +sure to test. + +## Create your initial `BubbleInfo` + +Use `BubbleInfo.builder()` to populate a `BubbleInfo` with your color, main +icon, main Intent (which should navigate back to your app), starting Y position, +and a list of `Actions` to put in the drawer. Each `Action` will define its +icon, user-displayable name (used for content description), Intent to perform +when clicked, whether it is enabled (optional, default true), and whether it is +checked (optional, default false). + +![Floating bubble expanded](images/bubble_expanded.png){height=400} + +## Create, show, and hide the Bubble + +Create the bubble using `Bubble.createBubble(Context, BubbleInfo)`. The `show()` +method is safe to call at any time. If the Bubble is already showing, it is a +no-op. `hide()` may also be called at any time and will collapse the drawer +before hiding if already open. While `show()` will show immediately, `hide()` +may need to wait for other operations or animations before the bubble is hidden. +It is unlikely you will need to keep track of this, however. The bubble will be +hidden at its next opportunity, and `hide()` will not block. + +![Floating bubble with state](images/bubble_state.png){height=400} + +## Update the Bubble's state + +Call `Bubble.setBubbleInfo(BubbleInfo)` to update all displayed state. +`BubbleInfo`s are immutable, so to make a new one using an existing +`BubbleInfo`, use `BubbleInfo.from(BubbleInfo)` to get a `Builder` with +prepopulated info. If only the `Action` state has changed, it is more efficient +to just call `Bubble.updateActions(List<Action>)` + +![Floating bubble with text](images/bubble_text.png){height=400} + +## Show text + +To temporarily replace the icon with a textual message, call +`Bubble.showText(CharSequence)`. The text will be displayed for several seconds +before transitioning back to the primary icon. The drawer will be closed if open +and cannot be reopened while the text is displayed. Any calls to `hide()` will +be deferred until after the text is done being displayed, so if you wish to show +an ending message of some sort you may call `hide()` immediately after +`showText(CharSequence)`. diff --git a/java/com/android/dialershared/bubble/g3doc/images/bubble_collapsed.png b/java/com/android/dialershared/bubble/g3doc/images/bubble_collapsed.png Binary files differnew file mode 100644 index 000000000..7ecc0675b --- /dev/null +++ b/java/com/android/dialershared/bubble/g3doc/images/bubble_collapsed.png diff --git a/java/com/android/dialershared/bubble/g3doc/images/bubble_expanded.png b/java/com/android/dialershared/bubble/g3doc/images/bubble_expanded.png Binary files differnew file mode 100644 index 000000000..cd477f334 --- /dev/null +++ b/java/com/android/dialershared/bubble/g3doc/images/bubble_expanded.png diff --git a/java/com/android/dialershared/bubble/g3doc/images/bubble_state.png b/java/com/android/dialershared/bubble/g3doc/images/bubble_state.png Binary files differnew file mode 100644 index 000000000..21ca8a8b5 --- /dev/null +++ b/java/com/android/dialershared/bubble/g3doc/images/bubble_state.png diff --git a/java/com/android/dialershared/bubble/g3doc/images/bubble_text.png b/java/com/android/dialershared/bubble/g3doc/images/bubble_text.png Binary files differnew file mode 100644 index 000000000..9c476dca6 --- /dev/null +++ b/java/com/android/dialershared/bubble/g3doc/images/bubble_text.png diff --git a/java/com/android/dialershared/bubble/res/layout/bubble_base.xml b/java/com/android/dialershared/bubble/res/layout/bubble_base.xml index 3acd2af2e..76970f020 100644 --- a/java/com/android/dialershared/bubble/res/layout/bubble_base.xml +++ b/java/com/android/dialershared/bubble/res/layout/bubble_base.xml @@ -19,24 +19,36 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:clipToPadding="false" tools:theme="@style/Theme.AppCompat"> + <View + android:id="@+id/bubble_drawer_shadow_provider" + android:layout_width="@dimen/bubble_size" + android:layout_height="@dimen/bubble_size" + android:layout_marginTop="@dimen/bubble_shadow_padding_size" + android:layout_marginBottom="@dimen/bubble_shadow_padding_size" + android:layout_marginStart="@dimen/bubble_shadow_padding_size" + android:background="@drawable/bubble_ripple_circle" + android:backgroundTint="@android:color/transparent" + android:elevation="10dp" + android:visibility="invisible" + /> <FrameLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="48dp" + android:elevation="10dp" android:paddingTop="@dimen/bubble_shadow_padding_size" android:paddingBottom="@dimen/bubble_shadow_padding_size" - android:paddingEnd="@dimen/bubble_shadow_padding_size" - android:background="@android:color/transparent"> + android:paddingEnd="@dimen/bubble_shadow_padding_size"> <LinearLayout android:id="@+id/bubble_expanded_layout" android:layout_width="wrap_content" android:layout_height="wrap_content" android:paddingStart="32dp" - android:paddingEnd="12dp" + android:paddingEnd="8dp" android:background="@drawable/bubble_background_pill_ltr" - android:elevation="2dp" android:layoutDirection="inherit" android:orientation="horizontal" android:visibility="gone" @@ -80,16 +92,15 @@ android:layout_height="wrap_content" android:layout_gravity="start" android:animateLayoutChanges="true" - android:background="@android:color/transparent" android:clipChildren="false" - android:clipToPadding="false"> + android:clipToPadding="false" + android:elevation="12dp"> <ViewAnimator android:id="@+id/bubble_button_primary" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="@dimen/bubble_shadow_padding_size" android:background="@drawable/bubble_ripple_circle" - android:elevation="6dp" android:measureAllChildren="false" tools:backgroundTint="#FF0000AA"> <ImageView diff --git a/java/com/android/dialershared/bubble/res/values/values.xml b/java/com/android/dialershared/bubble/res/values/values.xml index 5b85e0d23..f5816172d 100644 --- a/java/com/android/dialershared/bubble/res/values/values.xml +++ b/java/com/android/dialershared/bubble/res/values/values.xml @@ -16,12 +16,10 @@ --> <resources> - <dimen name="bubble_safe_margin_x">0dp</dimen> - <dimen name="bubble_safe_margin_y">0dp</dimen> - <dimen name="bubble_initial_offset_x">0dp</dimen> - <dimen name="bubble_initial_offset_y">120dp</dimen> - <dimen name="bubble_size">64dp</dimen> - <dimen name="bubble_icon_padding">20dp</dimen> + <dimen name="bubble_safe_margin_x">16dp</dimen> + <dimen name="bubble_safe_margin_y">64dp</dimen> + <dimen name="bubble_size">56dp</dimen> + <dimen name="bubble_icon_padding">16dp</dimen> <dimen name="bubble_move_elevation_change">4dp</dimen> <dimen name="bubble_shadow_padding_size">16dp</dimen> </resources> |