Merge "Merge deep shortcuts in rounded rect" into ub-launcher3-dorval

This commit is contained in:
Tony Wickham
2017-03-01 18:57:22 +00:00
committed by Android (Google) Code Review
17 changed files with 363 additions and 288 deletions
-24
View File
@@ -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>
-21
View File
@@ -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>
+13 -7
View File
@@ -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>
+1 -1
View File
@@ -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">
+1 -1
View File
@@ -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"
+32
View File
@@ -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>
+7 -6
View File
@@ -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 -->
@@ -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);
@@ -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;
@@ -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,
@@ -134,11 +95,6 @@ public class NotificationItemView extends PopupItemView implements LogContainerP
return heightAnimator;
}
@Override
protected float getBackgroundRadius() {
return getResources().getDimensionPixelSize(R.dimen.bg_round_rect_radius);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (mMainView.getNotificationInfo() == null) {
@@ -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;
}
@@ -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);
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);
}
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) {
@@ -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();
}
/**
@@ -125,17 +157,6 @@ public abstract class PopupItemView extends FrameLayout
return closeAnimator;
}
/**
* 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.
*/
@@ -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));
@@ -196,7 +196,8 @@ public class PopupPopulator {
@Override
public void run() {
mShortcutChild.applyShortcutInfo(mShortcutChildInfo, mDetail, mContainer);
mShortcutChild.applyShortcutInfo(mShortcutChildInfo, mDetail,
mContainer.mShortcutsItemView);
}
}
@@ -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);
@@ -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;
}
}
@@ -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);
}
}