diff options
author | Tony Wickham <twickham@google.com> | 2017-02-27 16:30:47 -0800 |
---|---|---|
committer | Tony Wickham <twickham@google.com> | 2017-03-01 10:16:16 -0800 |
commit | 51889b0be83dc34c9752fc066d0d6e75ab4f5e26 (patch) | |
tree | b3c466ce4742e561fc19dce1a1a028a65f28801b | |
parent | 7f3526a1a4d5d3578d4648abb1422646d23c6080 (diff) | |
download | android_packages_apps_Trebuchet-51889b0be83dc34c9752fc066d0d6e75ab4f5e26.tar.gz android_packages_apps_Trebuchet-51889b0be83dc34c9752fc066d0d6e75ab4f5e26.tar.bz2 android_packages_apps_Trebuchet-51889b0be83dc34c9752fc066d0d6e75ab4f5e26.zip |
Merge deep shortcuts in rounded rect
- DeepShortcutViews are added to ShortcutsItemView, which
is in PopupContainerWithArrow
- Moved some shortcut-specific logic to ShortcutsItemView
(namely, touch/long-click handling for draggin shortcuts)
- Moved round-rect clipping to PopupItemView
- Removed collapseToIcon() logic, including
PillWidthRevealOutlineProvider, which was only used for
that purpose. It isn't necessary now that the deep
shortcuts have no background themselves.
- Replaced focus pill drawable with ripple effect on
shortcuts and notification view.
Bug: 35766387
Change-Id: I6bc09f1851cfbb806df4bf75a6e435b0f1900c9c
-rw-r--r-- | res/drawable/bg_pill_focused.xml | 24 | ||||
-rw-r--r-- | res/drawable/bg_white_pill.xml | 21 | ||||
-rw-r--r-- | res/layout/deep_shortcut.xml | 20 | ||||
-rw-r--r-- | res/layout/notification.xml | 2 | ||||
-rw-r--r-- | res/layout/notification_footer.xml | 2 | ||||
-rw-r--r-- | res/layout/shortcuts_item.xml | 32 | ||||
-rw-r--r-- | res/values/dimens.xml | 13 | ||||
-rw-r--r-- | src/com/android/launcher3/AbstractFloatingView.java | 2 | ||||
-rw-r--r-- | src/com/android/launcher3/badge/BadgeRenderer.java | 1 | ||||
-rw-r--r-- | src/com/android/launcher3/notification/NotificationItemView.java | 46 | ||||
-rw-r--r-- | src/com/android/launcher3/notification/NotificationMainView.java | 6 | ||||
-rw-r--r-- | src/com/android/launcher3/popup/PopupContainerWithArrow.java | 175 | ||||
-rw-r--r-- | src/com/android/launcher3/popup/PopupItemView.java | 61 | ||||
-rw-r--r-- | src/com/android/launcher3/popup/PopupPopulator.java | 3 | ||||
-rw-r--r-- | src/com/android/launcher3/shortcuts/DeepShortcutView.java | 24 | ||||
-rw-r--r-- | src/com/android/launcher3/shortcuts/ShortcutsItemView.java | 180 | ||||
-rw-r--r-- | src/com/android/launcher3/util/PillWidthRevealOutlineProvider.java | 41 |
17 files changed, 364 insertions, 289 deletions
diff --git a/res/drawable/bg_pill_focused.xml b/res/drawable/bg_pill_focused.xml deleted file mode 100644 index 54075d92c..000000000 --- a/res/drawable/bg_pill_focused.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2016 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:state_focused="true"> - <shape android:shape="rectangle"> - <stroke android:color="?android:attr/colorControlActivated" android:width="2dp" /> - <corners android:radius="@dimen/bg_pill_radius" /> - </shape> - </item> -</selector>
\ No newline at end of file diff --git a/res/drawable/bg_white_pill.xml b/res/drawable/bg_white_pill.xml deleted file mode 100644 index f92f7394e..000000000 --- a/res/drawable/bg_white_pill.xml +++ /dev/null @@ -1,21 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2016 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<shape xmlns:android="http://schemas.android.com/apk/res/android" - android:shape="rectangle"> - <solid android:color="#FFFFFF" /> - <corners android:radius="@dimen/bg_pill_radius" /> -</shape>
\ No newline at end of file diff --git a/res/layout/deep_shortcut.xml b/res/layout/deep_shortcut.xml index 6c1d4da91..b2ed709e2 100644 --- a/res/layout/deep_shortcut.xml +++ b/res/layout/deep_shortcut.xml @@ -17,18 +17,16 @@ <com.android.launcher3.shortcuts.DeepShortcutView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:launcher="http://schemas.android.com/apk/res-auto" - android:layout_width="@dimen/bg_pill_width" - android:layout_height="@dimen/bg_pill_height" - android:elevation="@dimen/deep_shortcuts_elevation" - android:background="@drawable/bg_white_pill" > + android:layout_width="@dimen/bg_popup_item_width" + android:layout_height="@dimen/bg_popup_item_height" > <com.android.launcher3.shortcuts.DeepShortcutTextView style="@style/BaseIcon" android:id="@+id/deep_shortcut" - android:background="@drawable/bg_pill_focused" + android:background="?android:attr/selectableItemBackground" android:gravity="start|center_vertical" android:textAlignment="viewStart" - android:paddingStart="@dimen/bg_pill_height" + android:paddingStart="@dimen/bg_popup_item_height" android:paddingEnd="@dimen/deep_shortcut_padding_end" android:drawableEnd="@drawable/deep_shortcuts_drag_handle" android:drawablePadding="@dimen/deep_shortcut_drawable_padding" @@ -40,10 +38,18 @@ android:elevation="@dimen/deep_shortcuts_elevation" /> <View - android:id="@+id/popup_item_icon" + android:id="@+id/icon" android:layout_width="@dimen/deep_shortcut_icon_size" android:layout_height="@dimen/deep_shortcut_icon_size" android:layout_margin="@dimen/deep_shortcut_padding_start" android:layout_gravity="start" /> + <View + android:id="@+id/divider" + android:layout_width="@dimen/deep_shortcuts_divider_width" + android:layout_height="@dimen/popup_item_divider_height" + android:layout_gravity="end|bottom" + android:visibility="gone" + android:background="?android:attr/listDivider" /> + </com.android.launcher3.shortcuts.DeepShortcutView> diff --git a/res/layout/notification.xml b/res/layout/notification.xml index 48c7b48b9..e148cbb44 100644 --- a/res/layout/notification.xml +++ b/res/layout/notification.xml @@ -17,7 +17,7 @@ <com.android.launcher3.notification.NotificationItemView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/notification_view" - android:layout_width="@dimen/bg_pill_width" + android:layout_width="@dimen/bg_popup_item_width" android:layout_height="wrap_content" android:elevation="@dimen/deep_shortcuts_elevation" android:background="@drawable/bg_white_round_rect"> diff --git a/res/layout/notification_footer.xml b/res/layout/notification_footer.xml index c025819a2..54282086c 100644 --- a/res/layout/notification_footer.xml +++ b/res/layout/notification_footer.xml @@ -26,7 +26,7 @@ <View android:id="@+id/divider" android:layout_width="match_parent" - android:layout_height="@dimen/notification_divider_height"/> + android:layout_height="@dimen/popup_item_divider_height"/> <LinearLayout android:id="@+id/icon_row" diff --git a/res/layout/shortcuts_item.xml b/res/layout/shortcuts_item.xml new file mode 100644 index 000000000..8b20bcbdc --- /dev/null +++ b/res/layout/shortcuts_item.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<com.android.launcher3.shortcuts.ShortcutsItemView + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/shortcuts_view" + android:layout_width="@dimen/bg_popup_item_width" + android:layout_height="wrap_content" + android:elevation="@dimen/deep_shortcuts_elevation" + android:background="@drawable/bg_white_round_rect"> + + <LinearLayout + android:id="@+id/deep_shortcuts" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + </LinearLayout> + +</com.android.launcher3.shortcuts.ShortcutsItemView> diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 177e08e2f..8fee26b0b 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -150,10 +150,9 @@ <!-- Deep shortcuts --> <dimen name="deep_shortcuts_elevation">9dp</dimen> - <dimen name="bg_pill_width">208dp</dimen> - <dimen name="bg_pill_height">48dp</dimen> - <dimen name="bg_pill_radius">24dp</dimen> - <dimen name="deep_shortcuts_spacing">4dp</dimen> + <dimen name="bg_popup_item_width">208dp</dimen> + <dimen name="bg_popup_item_height">48dp</dimen> + <dimen name="popup_items_spacing">4dp</dimen> <dimen name="pre_drag_view_scale">6dp</dimen> <!-- an icon with shortcuts must be dragged this far before the container is removed. --> <dimen name="deep_shortcuts_start_drag_threshold">16dp</dimen> @@ -171,6 +170,8 @@ deep_shortcut_padding_end + deep_shortcut_drag_handle_size / 2 - deep_shortcuts_arrow_width / 2 also happens to equal 19dp--> <dimen name="deep_shortcuts_arrow_horizontal_offset">19dp</dimen> + <!-- popup_item_width - icon_size - padding_start - drawable_padding --> + <dimen name="deep_shortcuts_divider_width">158dp</dimen> <!-- Icon badges (with notification counts) --> <dimen name="badge_size">24dp</dimen> @@ -186,9 +187,9 @@ <!-- (icon_size - footer_icon_size) / 2 --> <dimen name="notification_footer_icon_row_padding">2dp</dimen> <dimen name="notification_main_height">60dp</dimen> - <dimen name="notification_footer_height">@dimen/bg_pill_height</dimen> + <dimen name="notification_footer_height">@dimen/bg_popup_item_height</dimen> <dimen name="notification_elevation">2dp</dimen> - <dimen name="notification_divider_height">0.5dp</dimen> + <dimen name="popup_item_divider_height">0.5dp</dimen> <dimen name="swipe_helper_falsing_threshold">70dp</dimen> <!-- Other --> diff --git a/src/com/android/launcher3/AbstractFloatingView.java b/src/com/android/launcher3/AbstractFloatingView.java index 52a83dcdf..bd1268651 100644 --- a/src/com/android/launcher3/AbstractFloatingView.java +++ b/src/com/android/launcher3/AbstractFloatingView.java @@ -95,7 +95,7 @@ public abstract class AbstractFloatingView extends LinearLayout { return null; } - protected static void closeOpenContainer(Launcher launcher, @FloatingViewType int type) { + public static void closeOpenContainer(Launcher launcher, @FloatingViewType int type) { AbstractFloatingView view = getOpenView(launcher, type); if (view != null) { view.close(true); diff --git a/src/com/android/launcher3/badge/BadgeRenderer.java b/src/com/android/launcher3/badge/BadgeRenderer.java index 864a65d8a..1f3e22de2 100644 --- a/src/com/android/launcher3/badge/BadgeRenderer.java +++ b/src/com/android/launcher3/badge/BadgeRenderer.java @@ -22,7 +22,6 @@ import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Rect; -import android.graphics.RectF; import android.graphics.Shader; import android.support.annotation.Nullable; diff --git a/src/com/android/launcher3/notification/NotificationItemView.java b/src/com/android/launcher3/notification/NotificationItemView.java index 742c90a5a..efd9a3b06 100644 --- a/src/com/android/launcher3/notification/NotificationItemView.java +++ b/src/com/android/launcher3/notification/NotificationItemView.java @@ -20,14 +20,7 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.content.Context; import android.content.res.ColorStateList; -import android.graphics.Bitmap; -import android.graphics.BitmapShader; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.PorterDuff; -import android.graphics.PorterDuffXfermode; import android.graphics.Rect; -import android.graphics.Shader; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; @@ -36,11 +29,11 @@ import android.widget.FrameLayout; import com.android.launcher3.ItemInfo; import com.android.launcher3.R; +import com.android.launcher3.anim.PillHeightRevealOutlineProvider; import com.android.launcher3.graphics.IconPalette; import com.android.launcher3.logging.UserEventDispatcher.LogContainerProvider; import com.android.launcher3.popup.PopupItemView; import com.android.launcher3.userevent.nano.LauncherLogProto; -import com.android.launcher3.anim.PillHeightRevealOutlineProvider; import java.util.List; @@ -54,9 +47,6 @@ public class NotificationItemView extends PopupItemView implements LogContainerP private static final Rect sTempRect = new Rect(); - private final Paint mBackgroundClipPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG | - Paint.FILTER_BITMAP_FLAG); - private View mDivider; private NotificationMainView mMainView; private NotificationFooterLayout mFooter; @@ -85,35 +75,6 @@ public class NotificationItemView extends PopupItemView implements LogContainerP mSwipeHelper.setDisableHardwareLayers(true); } - private void initializeBackgroundClipping(boolean force) { - if (force || mBackgroundClipPaint.getShader() == null) { - mBackgroundClipPaint.setXfermode(null); - mBackgroundClipPaint.setShader(null); - Bitmap backgroundBitmap = Bitmap.createBitmap(getMeasuredWidth(), getMeasuredHeight(), - Bitmap.Config.ALPHA_8); - Canvas canvas = new Canvas(); - canvas.setBitmap(backgroundBitmap); - float roundRectRadius = getResources().getDimensionPixelSize( - R.dimen.bg_round_rect_radius); - canvas.drawRoundRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), - roundRectRadius, roundRectRadius, mBackgroundClipPaint); - Shader backgroundClipShader = new BitmapShader(backgroundBitmap, - Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); - mBackgroundClipPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN)); - mBackgroundClipPaint.setShader(backgroundClipShader); - } - } - - @Override - protected void dispatchDraw(Canvas canvas) { - initializeBackgroundClipping(false /* force */); - int saveCount = canvas.saveLayer(0, 0, getWidth(), getHeight(), null, - Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG); - super.dispatchDraw(canvas); - canvas.drawPaint(mBackgroundClipPaint); - canvas.restoreToCount(saveCount); - } - public Animator animateHeightRemoval(int heightToRemove) { final int newHeight = getHeight() - heightToRemove; Animator heightAnimator = new PillHeightRevealOutlineProvider(mPillRect, @@ -135,11 +96,6 @@ public class NotificationItemView extends PopupItemView implements LogContainerP } @Override - protected float getBackgroundRadius() { - return getResources().getDimensionPixelSize(R.dimen.bg_round_rect_radius); - } - - @Override public boolean onInterceptTouchEvent(MotionEvent ev) { if (mMainView.getNotificationInfo() == null) { // The notification hasn't been populated yet. diff --git a/src/com/android/launcher3/notification/NotificationMainView.java b/src/com/android/launcher3/notification/NotificationMainView.java index b3425259b..824dbf279 100644 --- a/src/com/android/launcher3/notification/NotificationMainView.java +++ b/src/com/android/launcher3/notification/NotificationMainView.java @@ -22,7 +22,9 @@ import android.animation.ArgbEvaluator; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.content.Context; +import android.content.res.ColorStateList; import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.RippleDrawable; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; @@ -71,7 +73,9 @@ public class NotificationMainView extends LinearLayout implements SwipeHelper.Ca public void applyColors(IconPalette iconPalette) { mColorBackground = new ColorDrawable(iconPalette.backgroundColor); - setBackground(mColorBackground); + RippleDrawable rippleDrawable = new RippleDrawable(ColorStateList.valueOf( + iconPalette.secondaryColor), mColorBackground, null); + setBackground(rippleDrawable); mIconPalette = iconPalette; } diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java index 7811b960e..15dde4330 100644 --- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java +++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java @@ -27,7 +27,6 @@ import android.annotation.TargetApi; import android.content.Context; import android.content.res.Resources; import android.graphics.Color; -import android.graphics.Point; import android.graphics.PointF; import android.graphics.Rect; import android.graphics.drawable.ShapeDrawable; @@ -64,15 +63,13 @@ import com.android.launcher3.badge.BadgeInfo; import com.android.launcher3.dragndrop.DragController; import com.android.launcher3.dragndrop.DragLayer; import com.android.launcher3.dragndrop.DragOptions; -import com.android.launcher3.dragndrop.DragView; import com.android.launcher3.graphics.IconPalette; import com.android.launcher3.graphics.TriangleShape; import com.android.launcher3.notification.NotificationItemView; import com.android.launcher3.shortcuts.DeepShortcutView; -import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider; +import com.android.launcher3.shortcuts.ShortcutsItemView; import com.android.launcher3.util.PackageUserKey; -import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -84,18 +81,17 @@ import static com.android.launcher3.userevent.nano.LauncherLogProto.Target; * A container for shortcuts to deep links within apps. */ @TargetApi(Build.VERSION_CODES.N) -public class PopupContainerWithArrow extends AbstractFloatingView - implements View.OnLongClickListener, View.OnTouchListener, DragSource, +public class PopupContainerWithArrow extends AbstractFloatingView implements DragSource, DragController.DragListener { - private final Point mIconShift = new Point(); - private final Point mIconLastTouchPos = new Point(); - protected final Launcher mLauncher; private final int mStartDragThreshold; private LauncherAccessibilityDelegate mAccessibilityDelegate; private final boolean mIsRtl; + public ShortcutsItemView mShortcutsItemView; + private NotificationItemView mNotificationItemView; + protected BubbleTextView mOriginalIcon; private final Rect mTempRect = new Rect(); private PointF mInterceptTouchDown = new PointF(); @@ -177,6 +173,8 @@ public class PopupContainerWithArrow extends AbstractFloatingView boolean reverseOrder = mIsAboveIcon; if (reverseOrder) { removeAllViews(); + mNotificationItemView = null; + mShortcutsItemView = null; itemsToPopulate = PopupPopulator.reverseItems(itemsToPopulate); addDummyViews(originalIcon, itemsToPopulate, notificationKeys.length > 1); @@ -184,22 +182,12 @@ public class PopupContainerWithArrow extends AbstractFloatingView orientAboutIcon(originalIcon, arrowHeight + arrowVerticalOffset); } - List<DeepShortcutView> shortcutViews = new ArrayList<>(); - NotificationItemView notificationView = null; - for (int i = 0; i < getChildCount(); i++) { - View item = getChildAt(i); + List<DeepShortcutView> shortcutViews = mShortcutsItemView.getDeepShortcutViews(reverseOrder); + for (int i = 0; i < itemsToPopulate.length; i++) { switch (itemsToPopulate[i]) { - case SHORTCUT: - if (reverseOrder) { - shortcutViews.add(0, (DeepShortcutView) item); - } else { - shortcutViews.add((DeepShortcutView) item); - } - break; case NOTIFICATION: - notificationView = (NotificationItemView) item; IconPalette iconPalette = originalIcon.getIconPalette(); - notificationView.applyColors(iconPalette); + mNotificationItemView.applyColors(iconPalette); break; } } @@ -221,28 +209,46 @@ public class PopupContainerWithArrow extends AbstractFloatingView final Looper workerLooper = LauncherModel.getWorkerLooper(); new Handler(workerLooper).postAtFrontOfQueue(PopupPopulator.createUpdateRunnable( mLauncher, (ItemInfo) originalIcon.getTag(), new Handler(Looper.getMainLooper()), - this, shortcutIds, shortcutViews, notificationKeys, notificationView)); + this, shortcutIds, shortcutViews, notificationKeys, mNotificationItemView)); } private void addDummyViews(BubbleTextView originalIcon, - PopupPopulator.Item[] itemsToPopulate, boolean notificationFooterHasIcons) { + PopupPopulator.Item[] itemTypesToPopulate, boolean notificationFooterHasIcons) { final Resources res = getResources(); - final int spacing = res.getDimensionPixelSize(R.dimen.deep_shortcuts_spacing); + final int spacing = res.getDimensionPixelSize(R.dimen.popup_items_spacing); final LayoutInflater inflater = mLauncher.getLayoutInflater(); - int numItems = itemsToPopulate.length; + int numItems = itemTypesToPopulate.length; for (int i = 0; i < numItems; i++) { - final PopupItemView item = (PopupItemView) inflater.inflate( - itemsToPopulate[i].layoutId, this, false); - if (itemsToPopulate[i] == PopupPopulator.Item.NOTIFICATION) { + PopupPopulator.Item itemTypeToPopulate = itemTypesToPopulate[i]; + final View item = inflater.inflate(itemTypeToPopulate.layoutId, this, false); + + if (itemTypeToPopulate == PopupPopulator.Item.NOTIFICATION) { + mNotificationItemView = (NotificationItemView) item; int footerHeight = notificationFooterHasIcons ? res.getDimensionPixelSize(R.dimen.notification_footer_height) : 0; item.findViewById(R.id.footer).getLayoutParams().height = footerHeight; } - if (i < numItems - 1) { - ((LayoutParams) item.getLayoutParams()).bottomMargin = spacing; - } + + boolean itemIsFollowedByDifferentType = i < numItems - 1 + && itemTypesToPopulate[i + 1] != itemTypeToPopulate; + item.setAccessibilityDelegate(mAccessibilityDelegate); - addView(item); + if (itemTypeToPopulate == PopupPopulator.Item.SHORTCUT) { + if (mShortcutsItemView == null) { + mShortcutsItemView = (ShortcutsItemView) inflater.inflate( + R.layout.shortcuts_item, this, false); + addView(mShortcutsItemView); + } + mShortcutsItemView.addDeepShortcutView((DeepShortcutView) item); + if (itemIsFollowedByDifferentType) { + ((LayoutParams) mShortcutsItemView.getLayoutParams()).bottomMargin = spacing; + } + } else { + addView(item); + if (itemIsFollowedByDifferentType) { + ((LayoutParams) item.getLayoutParams()).bottomMargin = spacing; + } + } } // TODO: update this, since not all items are shortcuts setContentDescription(getContext().getString(R.string.shortcuts_menu_description, @@ -534,49 +540,8 @@ public class PopupContainerWithArrow extends AbstractFloatingView return true; } - @Override - public boolean onTouch(View v, MotionEvent ev) { - // Touched a shortcut, update where it was touched so we can drag from there on long click. - switch (ev.getAction()) { - case MotionEvent.ACTION_DOWN: - case MotionEvent.ACTION_MOVE: - mIconLastTouchPos.set((int) ev.getX(), (int) ev.getY()); - break; - } - return false; - } - - public boolean onLongClick(View v) { - // Return early if this is not initiated from a touch or not the correct view - if (!v.isInTouchMode() || !(v.getParent() instanceof DeepShortcutView)) return false; - // Return early if global dragging is not enabled - if (!mLauncher.isDraggingEnabled()) return false; - // Return early if an item is already being dragged (e.g. when long-pressing two shortcuts) - if (mLauncher.getDragController().isDragging()) return false; - - // Long clicked on a shortcut. - mDeferContainerRemoval = true; - DeepShortcutView sv = (DeepShortcutView) v.getParent(); - sv.setWillDrawIcon(false); - - // Move the icon to align with the center-top of the touch point - mIconShift.x = mIconLastTouchPos.x - sv.getIconCenter().x; - mIconShift.y = mIconLastTouchPos.y - mLauncher.getDeviceProfile().iconSizePx; - - DragView dv = mLauncher.getWorkspace().beginDragShared( - sv.getBubbleText(), this, sv.getFinalInfo(), - new ShortcutDragPreviewProvider(sv.getIconView(), mIconShift), new DragOptions()); - dv.animateShift(-mIconShift.x, -mIconShift.y); - - // TODO: support dragging from within folder without having to close it - AbstractFloatingView.closeOpenContainer(mLauncher, AbstractFloatingView.TYPE_FOLDER); - return false; - } - public void trimNotifications(Map<PackageUserKey, BadgeInfo> updatedBadges) { - final NotificationItemView notificationView = - (NotificationItemView) findViewById(R.id.notification_view); - if (notificationView == null) { + if (mNotificationItemView == null) { return; } ItemInfo originalInfo = (ItemInfo) mOriginalIcon.getTag(); @@ -585,11 +550,11 @@ public class PopupContainerWithArrow extends AbstractFloatingView AnimatorSet removeNotification = LauncherAnimUtils.createAnimatorSet(); final int duration = getResources().getInteger( R.integer.config_removeNotificationViewDuration); - final int spacing = getResources().getDimensionPixelSize(R.dimen.deep_shortcuts_spacing); + final int spacing = getResources().getDimensionPixelSize(R.dimen.popup_items_spacing); removeNotification.play(reduceNotificationViewHeight( - notificationView.getHeight() + spacing, duration, notificationView)); + mNotificationItemView.getHeight() + spacing, duration, mNotificationItemView)); final View removeMarginView = mIsAboveIcon ? getItemViewAt(getItemCount() - 2) - : notificationView; + : mNotificationItemView; if (removeMarginView != null) { ValueAnimator removeMargin = ValueAnimator.ofFloat(1, 0).setDuration(duration); removeMargin.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @@ -601,12 +566,12 @@ public class PopupContainerWithArrow extends AbstractFloatingView }); removeNotification.play(removeMargin); } - Animator fade = ObjectAnimator.ofFloat(notificationView, ALPHA, 0) + Animator fade = ObjectAnimator.ofFloat(mNotificationItemView, ALPHA, 0) .setDuration(duration); fade.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - removeView(notificationView); + removeView(mNotificationItemView); if (getItemCount() == 0) { close(false); return; @@ -626,7 +591,7 @@ public class PopupContainerWithArrow extends AbstractFloatingView removeNotification.start(); return; } - notificationView.trimNotifications(badgeInfo.getNotificationKeys()); + mNotificationItemView.trimNotifications(badgeInfo.getNotificationKeys()); } private ObjectAnimator createArrowScaleAnim(float scale) { @@ -669,8 +634,7 @@ public class PopupContainerWithArrow extends AbstractFloatingView } public Animator reduceNotificationViewHeight(int heightToRemove, int duration) { - return reduceNotificationViewHeight(heightToRemove, duration, - (NotificationItemView) findViewById(R.id.notification_view)); + return reduceNotificationViewHeight(heightToRemove, duration, mNotificationItemView); } @Override @@ -702,6 +666,7 @@ public class PopupContainerWithArrow extends AbstractFloatingView public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) { // Either the original icon or one of the shortcuts was dragged. // Hide the container, but don't remove it yet because that interferes with touch events. + mDeferContainerRemoval = true; animateClose(); } @@ -723,7 +688,6 @@ public class PopupContainerWithArrow extends AbstractFloatingView @Override public void fillInLogContainerData(View v, ItemInfo info, Target target, Target targetParent) { target.itemType = ItemType.DEEPSHORTCUT; - target.rank = info.rank; targetParent.containerType = ContainerType.DEEPSHORTCUTS; } @@ -765,38 +729,17 @@ public class PopupContainerWithArrow extends AbstractFloatingView for (int i = firstOpenItemIndex; i < firstOpenItemIndex + numOpenShortcuts; i++) { final PopupItemView view = getItemViewAt(i); Animator anim; - if (view.willDrawIcon()) { - anim = view.createCloseAnimation(mIsAboveIcon, mIsLeftAligned, duration); - int animationIndex = mIsAboveIcon ? i - firstOpenItemIndex - : numOpenShortcuts - i - 1; - anim.setStartDelay(stagger * animationIndex); - - Animator fadeAnim = ObjectAnimator.ofFloat(view, View.ALPHA, 0); - // Don't start fading until the arrow is gone. - fadeAnim.setStartDelay(stagger * animationIndex + arrowScaleDuration); - fadeAnim.setDuration(duration - arrowScaleDuration); - fadeAnim.setInterpolator(fadeInterpolator); - shortcutAnims.play(fadeAnim); - } else { - // The view is being dragged. Animate it such that it collapses with the drag view - anim = view.collapseToIcon(); - anim.setDuration(DragView.VIEW_ZOOM_DURATION); - - // Scale and translate the view to follow the drag view. - Point iconCenter = view.getIconCenter(); - view.setPivotX(iconCenter.x); - view.setPivotY(iconCenter.y); - - float scale = ((float) mLauncher.getDeviceProfile().iconSizePx) / view.getHeight(); - Animator anim2 = LauncherAnimUtils.ofPropertyValuesHolder(view, - new PropertyListBuilder() - .scale(scale) - .translationX(mIconShift.x) - .translationY(mIconShift.y) - .build()) - .setDuration(DragView.VIEW_ZOOM_DURATION); - shortcutAnims.play(anim2); - } + anim = view.createCloseAnimation(mIsAboveIcon, mIsLeftAligned, duration); + int animationIndex = mIsAboveIcon ? i - firstOpenItemIndex + : numOpenShortcuts - i - 1; + anim.setStartDelay(stagger * animationIndex); + + Animator fadeAnim = ObjectAnimator.ofFloat(view, View.ALPHA, 0); + // Don't start fading until the arrow is gone. + fadeAnim.setStartDelay(stagger * animationIndex + arrowScaleDuration); + fadeAnim.setDuration(duration - arrowScaleDuration); + fadeAnim.setInterpolator(fadeInterpolator); + shortcutAnims.play(fadeAnim); anim.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { diff --git a/src/com/android/launcher3/popup/PopupItemView.java b/src/com/android/launcher3/popup/PopupItemView.java index b3d7155aa..dc4f415d5 100644 --- a/src/com/android/launcher3/popup/PopupItemView.java +++ b/src/com/android/launcher3/popup/PopupItemView.java @@ -21,8 +21,15 @@ import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.content.Context; import android.content.res.ColorStateList; +import android.graphics.Bitmap; +import android.graphics.BitmapShader; +import android.graphics.Canvas; +import android.graphics.Paint; import android.graphics.Point; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffXfermode; import android.graphics.Rect; +import android.graphics.Shader; import android.util.AttributeSet; import android.view.View; import android.widget.FrameLayout; @@ -31,7 +38,6 @@ import com.android.launcher3.LogAccelerateInterpolator; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.util.PillRevealOutlineProvider; -import com.android.launcher3.util.PillWidthRevealOutlineProvider; /** * An abstract {@link FrameLayout} that supports animating an item's content @@ -47,6 +53,9 @@ public abstract class PopupItemView extends FrameLayout protected View mIconView; + private final Paint mBackgroundClipPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG | + Paint.FILTER_BITMAP_FLAG); + public PopupItemView(Context context) { this(context, null, 0); } @@ -73,12 +82,35 @@ public abstract class PopupItemView extends FrameLayout mPillRect.set(0, 0, getMeasuredWidth(), getMeasuredHeight()); } - protected ColorStateList getAttachedArrowColor() { - return getBackgroundTintList(); + protected void initializeBackgroundClipping(boolean force) { + if (force || mBackgroundClipPaint.getShader() == null) { + mBackgroundClipPaint.setXfermode(null); + mBackgroundClipPaint.setShader(null); + Bitmap backgroundBitmap = Bitmap.createBitmap(getMeasuredWidth(), getMeasuredHeight(), + Bitmap.Config.ALPHA_8); + Canvas canvas = new Canvas(); + canvas.setBitmap(backgroundBitmap); + canvas.drawRoundRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), + getBackgroundRadius(), getBackgroundRadius(), mBackgroundClipPaint); + Shader backgroundClipShader = new BitmapShader(backgroundBitmap, + Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); + mBackgroundClipPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN)); + mBackgroundClipPaint.setShader(backgroundClipShader); + } } - public boolean willDrawIcon() { - return true; + @Override + protected void dispatchDraw(Canvas canvas) { + initializeBackgroundClipping(false /* force */); + int saveCount = canvas.saveLayer(0, 0, getWidth(), getHeight(), null, + Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG); + super.dispatchDraw(canvas); + canvas.drawPaint(mBackgroundClipPaint); + canvas.restoreToCount(saveCount); + } + + protected ColorStateList getAttachedArrowColor() { + return getBackgroundTintList(); } /** @@ -126,17 +158,6 @@ public abstract class PopupItemView extends FrameLayout } /** - * Creates an animator which clips the container to form a circle around the icon. - */ - public Animator collapseToIcon() { - int halfHeight = getMeasuredHeight() / 2; - int iconCenterX = getIconCenter().x; - return new PillWidthRevealOutlineProvider(mPillRect, - iconCenterX - halfHeight, iconCenterX + halfHeight) - .createRevealAnimator(this, true); - } - - /** * Returns the position of the center of the icon relative to the container. */ public Point getIconCenter() { @@ -148,7 +169,7 @@ public abstract class PopupItemView extends FrameLayout } protected float getBackgroundRadius() { - return getResources().getDimensionPixelSize(R.dimen.bg_pill_radius); + return getResources().getDimensionPixelSize(R.dimen.bg_round_rect_radius); } /** @@ -182,8 +203,10 @@ public abstract class PopupItemView extends FrameLayout public void setProgress(float progress) { super.setProgress(progress); - mZoomView.setScaleX(progress); - mZoomView.setScaleY(progress); + if (mZoomView != null) { + mZoomView.setScaleX(progress); + mZoomView.setScaleY(progress); + } float height = mOutline.height(); mTranslateView.setTranslationY(mTranslateYMultiplier * (mFullHeight - height)); diff --git a/src/com/android/launcher3/popup/PopupPopulator.java b/src/com/android/launcher3/popup/PopupPopulator.java index d2814ee7b..39c2db2d5 100644 --- a/src/com/android/launcher3/popup/PopupPopulator.java +++ b/src/com/android/launcher3/popup/PopupPopulator.java @@ -196,7 +196,8 @@ public class PopupPopulator { @Override public void run() { - mShortcutChild.applyShortcutInfo(mShortcutChildInfo, mDetail, mContainer); + mShortcutChild.applyShortcutInfo(mShortcutChildInfo, mDetail, + mContainer.mShortcutsItemView); } } diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutView.java b/src/com/android/launcher3/shortcuts/DeepShortcutView.java index 2f07c9ac9..47a023e25 100644 --- a/src/com/android/launcher3/shortcuts/DeepShortcutView.java +++ b/src/com/android/launcher3/shortcuts/DeepShortcutView.java @@ -17,26 +17,30 @@ package com.android.launcher3.shortcuts; import android.content.Context; +import android.graphics.Point; import android.graphics.Rect; import android.text.TextUtils; import android.util.AttributeSet; import android.view.View; +import android.widget.FrameLayout; import com.android.launcher3.Launcher; import com.android.launcher3.R; import com.android.launcher3.ShortcutInfo; -import com.android.launcher3.popup.PopupContainerWithArrow; -import com.android.launcher3.popup.PopupItemView; +import com.android.launcher3.Utilities; /** * A {@link android.widget.FrameLayout} that contains a {@link DeepShortcutView}. * This lets us animate the DeepShortcutView (icon and text) separately from the background. */ -public class DeepShortcutView extends PopupItemView { +public class DeepShortcutView extends FrameLayout { + + private static final Point sTempPoint = new Point(); private final Rect mPillRect; private DeepShortcutTextView mBubbleText; + private View mIconView; private ShortcutInfo mInfo; private ShortcutInfoCompat mDetail; @@ -59,6 +63,7 @@ public class DeepShortcutView extends PopupItemView { protected void onFinishInflate() { super.onFinishInflate(); mBubbleText = (DeepShortcutTextView) findViewById(R.id.deep_shortcut); + mIconView = findViewById(R.id.icon); } public DeepShortcutTextView getBubbleText() { @@ -73,6 +78,17 @@ public class DeepShortcutView extends PopupItemView { return mIconView.getVisibility() == View.VISIBLE; } + /** + * Returns the position of the center of the icon relative to the container. + */ + public Point getIconCenter() { + sTempPoint.y = sTempPoint.x = getMeasuredHeight() / 2; + if (Utilities.isRtl(getResources())) { + sTempPoint.x = getMeasuredWidth() - sTempPoint.x; + } + return sTempPoint; + } + @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); @@ -81,7 +97,7 @@ public class DeepShortcutView extends PopupItemView { /** package private **/ public void applyShortcutInfo(ShortcutInfo info, ShortcutInfoCompat detail, - PopupContainerWithArrow container) { + ShortcutsItemView container) { mInfo = info; mDetail = detail; mBubbleText.applyFromShortcutInfo(info); diff --git a/src/com/android/launcher3/shortcuts/ShortcutsItemView.java b/src/com/android/launcher3/shortcuts/ShortcutsItemView.java new file mode 100644 index 000000000..349c4c946 --- /dev/null +++ b/src/com/android/launcher3/shortcuts/ShortcutsItemView.java @@ -0,0 +1,180 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher3.shortcuts; + +import android.animation.Animator; +import android.animation.AnimatorSet; +import android.content.Context; +import android.graphics.Point; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; +import android.widget.LinearLayout; + +import com.android.launcher3.AbstractFloatingView; +import com.android.launcher3.ItemInfo; +import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherAnimUtils; +import com.android.launcher3.R; +import com.android.launcher3.anim.PropertyListBuilder; +import com.android.launcher3.dragndrop.DragOptions; +import com.android.launcher3.dragndrop.DragView; +import com.android.launcher3.logging.UserEventDispatcher.LogContainerProvider; +import com.android.launcher3.popup.PopupContainerWithArrow; +import com.android.launcher3.popup.PopupItemView; +import com.android.launcher3.userevent.nano.LauncherLogProto; + +import java.util.ArrayList; +import java.util.List; + +/** + * A {@link PopupItemView} that contains all of the {@link DeepShortcutView}s for an app. + */ +public class ShortcutsItemView extends PopupItemView implements View.OnLongClickListener, + View.OnTouchListener, LogContainerProvider { + + private Launcher mLauncher; + private LinearLayout mDeepShortcutsLayout; + private final Point mIconShift = new Point(); + private final Point mIconLastTouchPos = new Point(); + + public ShortcutsItemView(Context context) { + this(context, null, 0); + } + + public ShortcutsItemView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public ShortcutsItemView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + + mLauncher = Launcher.getLauncher(context); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + mDeepShortcutsLayout = (LinearLayout) findViewById(R.id.deep_shortcuts); + } + + @Override + public boolean onTouch(View v, MotionEvent ev) { + // Touched a shortcut, update where it was touched so we can drag from there on long click. + switch (ev.getAction()) { + case MotionEvent.ACTION_DOWN: + case MotionEvent.ACTION_MOVE: + mIconLastTouchPos.set((int) ev.getX(), (int) ev.getY()); + break; + } + return false; + } + + @Override + public boolean onLongClick(View v) { + // Return early if this is not initiated from a touch or not the correct view + if (!v.isInTouchMode() || !(v.getParent() instanceof DeepShortcutView)) return false; + // Return early if global dragging is not enabled + if (!mLauncher.isDraggingEnabled()) return false; + // Return early if an item is already being dragged (e.g. when long-pressing two shortcuts) + if (mLauncher.getDragController().isDragging()) return false; + + // Long clicked on a shortcut. + DeepShortcutView sv = (DeepShortcutView) v.getParent(); + sv.setWillDrawIcon(false); + + // Move the icon to align with the center-top of the touch point + mIconShift.x = mIconLastTouchPos.x - sv.getIconCenter().x; + mIconShift.y = mIconLastTouchPos.y - mLauncher.getDeviceProfile().iconSizePx; + + DragView dv = mLauncher.getWorkspace().beginDragShared(sv.getBubbleText(), + (PopupContainerWithArrow) getParent(), sv.getFinalInfo(), + new ShortcutDragPreviewProvider(sv.getIconView(), mIconShift), new DragOptions()); + dv.animateShift(-mIconShift.x, -mIconShift.y); + + // TODO: support dragging from within folder without having to close it + AbstractFloatingView.closeOpenContainer(mLauncher, AbstractFloatingView.TYPE_FOLDER); + return false; + } + + public void addDeepShortcutView(DeepShortcutView deepShortcutView) { + if (getNumDeepShortcuts() > 0) { + getDeepShortcutAt(getNumDeepShortcuts() - 1).findViewById(R.id.divider) + .setVisibility(VISIBLE); + } + mDeepShortcutsLayout.addView(deepShortcutView); + } + + private DeepShortcutView getDeepShortcutAt(int index) { + return (DeepShortcutView) mDeepShortcutsLayout.getChildAt(index); + } + + private int getNumDeepShortcuts() { + return mDeepShortcutsLayout.getChildCount(); + } + + public List<DeepShortcutView> getDeepShortcutViews(boolean reverseOrder) { + int numDeepShortcuts = getNumDeepShortcuts(); + List<DeepShortcutView> deepShortcutViews = new ArrayList<>(numDeepShortcuts); + for (int i = 0; i < numDeepShortcuts; i++) { + DeepShortcutView deepShortcut = getDeepShortcutAt(i); + if (reverseOrder) { + deepShortcutViews.add(0, deepShortcut); + } else { + deepShortcutViews.add(deepShortcut); + } + } + return deepShortcutViews; + } + + @Override + public Animator createOpenAnimation(boolean isContainerAboveIcon, boolean pivotLeft) { + AnimatorSet openAnimation = LauncherAnimUtils.createAnimatorSet(); + openAnimation.play(super.createOpenAnimation(isContainerAboveIcon, pivotLeft)); + for (int i = 0; i < getNumDeepShortcuts(); i++) { + View deepShortcutIcon = getDeepShortcutAt(i).getIconView(); + deepShortcutIcon.setScaleX(0); + deepShortcutIcon.setScaleY(0); + openAnimation.play(LauncherAnimUtils.ofPropertyValuesHolder( + deepShortcutIcon, new PropertyListBuilder().scale(1).build())); + } + return openAnimation; + } + + @Override + public Animator createCloseAnimation(boolean isContainerAboveIcon, boolean pivotLeft, + long duration) { + AnimatorSet closeAnimation = LauncherAnimUtils.createAnimatorSet(); + closeAnimation.play(super.createCloseAnimation(isContainerAboveIcon, pivotLeft, duration)); + for (int i = 0; i < getNumDeepShortcuts(); i++) { + View deepShortcutIcon = getDeepShortcutAt(i).getIconView(); + deepShortcutIcon.setScaleX(1); + deepShortcutIcon.setScaleY(1); + closeAnimation.play(LauncherAnimUtils.ofPropertyValuesHolder( + deepShortcutIcon, new PropertyListBuilder().scale(0).build())); + } + return closeAnimation; + } + + @Override + public void fillInLogContainerData(View v, ItemInfo info, LauncherLogProto.Target target, + LauncherLogProto.Target targetParent) { + target.itemType = LauncherLogProto.ItemType.DEEPSHORTCUT; + target.rank = info.rank; + targetParent.containerType = LauncherLogProto.ContainerType.DEEPSHORTCUTS; + } +} diff --git a/src/com/android/launcher3/util/PillWidthRevealOutlineProvider.java b/src/com/android/launcher3/util/PillWidthRevealOutlineProvider.java deleted file mode 100644 index 89dda3b26..000000000 --- a/src/com/android/launcher3/util/PillWidthRevealOutlineProvider.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.launcher3.util; - -import android.graphics.Rect; - -/** - * Extension of {@link PillRevealOutlineProvider} which only changes the width of the pill. - */ -public class PillWidthRevealOutlineProvider extends PillRevealOutlineProvider { - - private final int mStartLeft; - private final int mStartRight; - - public PillWidthRevealOutlineProvider(Rect pillRect, int left, int right) { - super(0, 0, pillRect); - mOutline.set(pillRect); - mStartLeft = left; - mStartRight = right; - } - - @Override - public void setProgress(float progress) { - mOutline.left = (int) (progress * mPillRect.left + (1 - progress) * mStartLeft); - mOutline.right = (int) (progress * mPillRect.right + (1 - progress) * mStartRight); - } -} |