Update notification view to match newer specs

- Use smaller radius for notifications round rect background
- Remove "Notifications" header, and clip children to round rect path
- Flip main notification so that icon shows on the right instead of
  left; footer is also flipped so animation makes sense
- Clean up animations to animate view outline instead of height

Bug: 32410600
Change-Id: I6bd1e1f8395b3703f28c3b0056a89e67672368ab
This commit is contained in:
Tony Wickham 2017-02-21 15:16:12 -08:00
parent e05b08f705
commit 7f3526a1a4
14 changed files with 270 additions and 164 deletions

View File

@ -1,22 +0,0 @@
<?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.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#FFFFFF" />
<corners android:bottomLeftRadius="@dimen/bg_pill_radius"
android:bottomRightRadius="@dimen/bg_pill_radius" />
</shape>

View File

@ -17,6 +17,5 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#FFFFFF" />
<corners android:topLeftRadius="@dimen/bg_pill_radius"
android:topRightRadius="@dimen/bg_pill_radius" />
<corners android:radius="@dimen/bg_round_rect_radius" />
</shape>

View File

@ -20,7 +20,7 @@
android:layout_width="@dimen/bg_pill_width"
android:layout_height="wrap_content"
android:elevation="@dimen/deep_shortcuts_elevation"
android:background="@drawable/bg_white_pill">
android:background="@drawable/bg_white_round_rect">
<RelativeLayout
android:layout_width="match_parent"
@ -28,27 +28,10 @@
android:orientation="vertical"
android:clipChildren="false">
<TextView
android:id="@+id/header"
android:layout_width="match_parent"
android:layout_height="@dimen/notification_footer_collapsed_height"
android:gravity="center_vertical"
android:textAlignment="center"
android:text="@string/notifications_header"
android:elevation="@dimen/notification_elevation"
android:background="@drawable/bg_white_pill_top" />
<View
android:id="@+id/divider"
android:layout_width="match_parent"
android:layout_height="@dimen/notification_divider_height"
android:layout_below="@id/header" />
<include layout="@layout/notification_main"
android:id="@+id/main_view"
android:layout_width="match_parent"
android:layout_height="@dimen/bg_pill_height"
android:layout_below="@id/divider" />
android:layout_height="@dimen/notification_main_height" />
<include layout="@layout/notification_footer"
android:id="@+id/footer"

View File

@ -20,7 +20,6 @@
android:layout_height="match_parent"
android:orientation="vertical"
xmlns:android="http://schemas.android.com/apk/res/android"
android:background="@drawable/bg_white_pill_bottom"
android:elevation="@dimen/notification_elevation"
android:clipChildren="false" >
@ -34,6 +33,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:gravity="end"
android:padding="@dimen/notification_footer_icon_row_padding"
android:clipToPadding="false"
android:clipChildren="false"/>

View File

@ -21,20 +21,14 @@
android:layout_height="match_parent"
android:orientation="horizontal"
android:focusable="true"
android:padding="@dimen/notification_padding"
android:elevation="@dimen/notification_elevation" >
<View
android:id="@+id/popup_item_icon"
android:layout_width="@dimen/notification_icon_size"
android:layout_height="@dimen/notification_icon_size"
android:layout_marginStart="@dimen/notification_icon_margin_start"
android:layout_gravity="center_vertical" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:layout_marginStart="@dimen/notification_text_margin_start"
android:layout_weight="1"
android:gravity="center_vertical">
<TextView
android:id="@+id/title"
@ -56,5 +50,12 @@
android:layout_height="wrap_content" />
</LinearLayout>
<View
android:id="@+id/popup_item_icon"
android:layout_width="@dimen/notification_icon_size"
android:layout_height="@dimen/notification_icon_size"
android:layout_weight="0"
android:layout_gravity="center_vertical" />
</com.android.launcher3.notification.NotificationMainView>

View File

@ -175,19 +175,18 @@
<!-- Icon badges (with notification counts) -->
<dimen name="badge_size">24dp</dimen>
<dimen name="badge_text_size">12dp</dimen>
<dimen name="badge_small_padding">1dp</dimen>
<dimen name="badge_small_padding">0dp</dimen>
<dimen name="badge_large_padding">3dp</dimen>
<dimen name="notification_icon_size">28dp</dimen>
<dimen name="notification_footer_icon_size">24dp</dimen>
<!-- (icon_size - secondary_icon_size) / 2 -->
<!-- Notifications -->
<dimen name="bg_round_rect_radius">12dp</dimen>
<dimen name="notification_padding">12dp</dimen>
<!-- (icon_size - footer_icon_size) / 2 -->
<dimen name="notification_footer_icon_row_padding">2dp</dimen>
<dimen name="notification_icon_margin_start">8dp</dimen>
<dimen name="notification_text_margin_start">8dp</dimen>
<dimen name="notification_footer_height">36dp</dimen>
<!-- The height to use when there are no icons in the footer -->
<dimen name="notification_footer_collapsed_height">@dimen/bg_pill_radius</dimen>
<dimen name="notification_main_height">60dp</dimen>
<dimen name="notification_footer_height">@dimen/bg_pill_height</dimen>
<dimen name="notification_elevation">2dp</dimen>
<dimen name="notification_divider_height">0.5dp</dimen>
<dimen name="swipe_helper_falsing_threshold">70dp</dimen>

View File

@ -130,17 +130,4 @@ public class LauncherAnimUtils {
return anim;
}
public static ValueAnimator animateViewHeight(final View v, int fromHeight, int toHeight) {
ValueAnimator anim = ValueAnimator.ofInt(fromHeight, toHeight);
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
int val = (Integer) valueAnimator.getAnimatedValue();
ViewGroup.LayoutParams layoutParams = v.getLayoutParams();
layoutParams.height = val;
v.setLayoutParams(layoutParams);
}
});
return anim;
}
}

View File

@ -0,0 +1,43 @@
/*
* 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.anim;
import android.graphics.Rect;
import com.android.launcher3.util.PillRevealOutlineProvider;
/**
* Extension of {@link PillRevealOutlineProvider} which only changes the height of the pill.
* For now, we assume the height is added/removed from the bottom.
*/
public class PillHeightRevealOutlineProvider extends PillRevealOutlineProvider {
private final int mNewHeight;
public PillHeightRevealOutlineProvider(Rect pillRect, float radius, int newHeight) {
super(0, 0, pillRect, radius);
mOutline.set(pillRect);
mNewHeight = newHeight;
}
@Override
public void setProgress(float progress) {
mOutline.top = 0;
int heightDifference = mPillRect.height() - mNewHeight;
mOutline.bottom = (int) (mPillRect.bottom - heightDifference * (1 - progress));
}
}

View File

@ -0,0 +1,41 @@
/*
* 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.anim;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.util.Property;
/**
* An AnimatorListener that sets the given property to the given value at the end of the animation.
*/
public class PropertyResetListener<T, V> extends AnimatorListenerAdapter {
private Property<T, V> mPropertyToReset;
private V mResetToValue;
public PropertyResetListener(Property<T, V> propertyToReset, V resetToValue) {
mPropertyToReset = propertyToReset;
mResetToValue = resetToValue;
}
@Override
public void onAnimationEnd(Animator animation) {
mPropertyToReset.set((T) ((ObjectAnimator) animation).getTarget(), mResetToValue);
}
}

View File

@ -51,6 +51,7 @@ public class BadgeRenderer {
mSmallIconDrawer = new IconDrawer(res.getDimensionPixelSize(R.dimen.badge_large_padding));
mTextPaint.setTextAlign(Paint.Align.CENTER);
mTextPaint.setTextSize(res.getDimensionPixelSize(R.dimen.badge_text_size));
mTextPaint.setFakeBoldText(true);
// Measure the text height.
Rect tempTextHeight = new Rect();
mTextPaint.getTextBounds("0", 0, 1, tempTextHeight);

View File

@ -21,18 +21,21 @@ import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAnimUtils;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.PropertyListBuilder;
import com.android.launcher3.anim.PropertyResetListener;
import com.android.launcher3.graphics.IconPalette;
import com.android.launcher3.popup.PopupContainerWithArrow;
@ -50,16 +53,17 @@ public class NotificationFooterLayout extends LinearLayout {
void onIconAnimationEnd(NotificationInfo animatedNotification);
}
private static final int MAX_FOOTER_NOTIFICATIONS = 5;
private static final int MAX_FOOTER_NOTIFICATIONS = 4;
private static final Rect sTempRect = new Rect();
private final List<NotificationInfo> mNotifications = new ArrayList<>();
private final List<NotificationInfo> mOverflowNotifications = new ArrayList<>();
private final boolean mRtl;
LinearLayout.LayoutParams mIconLayoutParams;
private LinearLayout mIconRow;
private int mBackgroundColor;
private final ColorDrawable mBackgroundColor;
private int mTextColor;
private TextView mOverflowView;
@ -74,13 +78,17 @@ public class NotificationFooterLayout extends LinearLayout {
public NotificationFooterLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mRtl = Utilities.isRtl(getResources());
int size = getResources().getDimensionPixelSize(
R.dimen.notification_footer_icon_size);
int padding = getResources().getDimensionPixelSize(
R.dimen.deep_shortcut_drawable_padding);
int padding = getResources().getDimensionPixelSize(R.dimen.notification_padding);
mIconLayoutParams = new LayoutParams(size, size);
mIconLayoutParams.setMarginStart(padding);
mIconLayoutParams.setMarginEnd(padding);
mIconLayoutParams.gravity = Gravity.CENTER_VERTICAL;
mBackgroundColor = new ColorDrawable();
setBackground(mBackgroundColor);
}
@Override
@ -90,12 +98,15 @@ public class NotificationFooterLayout extends LinearLayout {
}
public void applyColors(IconPalette iconPalette) {
mBackgroundColor = iconPalette.backgroundColor;
setBackgroundTintList(ColorStateList.valueOf(mBackgroundColor));
mBackgroundColor.setColor(iconPalette.backgroundColor);
findViewById(R.id.divider).setBackgroundColor(iconPalette.secondaryColor);
mTextColor = iconPalette.textColor;
}
public int getBackgroundColor() {
return mBackgroundColor.getColor();
}
/**
* Keep track of the NotificationInfo, and then update the UI when
* {@link #commitNotificationInfos()} is called.
@ -124,18 +135,18 @@ public class NotificationFooterLayout extends LinearLayout {
mOverflowView = new TextView(getContext());
mOverflowView.setTextColor(mTextColor);
updateOverflowText();
mIconRow.addView(mOverflowView, mIconLayoutParams);
mIconRow.addView(mOverflowView, 0, mIconLayoutParams);
}
}
private void addNotificationIconForInfo(NotificationInfo info, boolean fromOverflow) {
View icon = new View(getContext());
icon.setBackground(info.getIconForBackground(getContext(), mBackgroundColor));
icon.setBackground(info.getIconForBackground(getContext(), getBackgroundColor()));
icon.setOnClickListener(info);
int addIndex = mIconRow.getChildCount();
int addIndex = 0;
if (fromOverflow) {
// Add the notification before the overflow view.
addIndex--;
addIndex = 1;
icon.setAlpha(0);
icon.animate().alpha(1);
}
@ -151,7 +162,7 @@ public class NotificationFooterLayout extends LinearLayout {
public void animateFirstNotificationTo(Rect toBounds,
final IconAnimationEndListener callback) {
AnimatorSet animation = LauncherAnimUtils.createAnimatorSet();
final View firstNotification = mIconRow.getChildAt(0);
final View firstNotification = mIconRow.getChildAt(mIconRow.getChildCount() - 1);
Rect fromBounds = sTempRect;
firstNotification.getGlobalVisibleRect(fromBounds);
@ -169,20 +180,19 @@ public class NotificationFooterLayout extends LinearLayout {
animation.play(moveAndScaleIcon);
// Shift all notifications (not the overflow) over to fill the gap.
int gapWidth = mIconLayoutParams.width + mIconLayoutParams.getMarginStart();
int numIcons = mIconRow.getChildCount()
- (mOverflowNotifications.isEmpty() ? 0 : 1);
for (int i = 1; i < numIcons; i++) {
final View child = mIconRow.getChildAt(i);
Animator shiftChild = ObjectAnimator.ofFloat(child, TRANSLATION_X, -gapWidth);
shiftChild.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
int gapWidth = mIconLayoutParams.width + mIconLayoutParams.getMarginEnd();
if (mRtl) {
gapWidth = -gapWidth;
}
int numIcons = mIconRow.getChildCount() - 1;
// We have to set the translation X to 0 when the new main notification
// is removed from the footer.
child.setTranslationX(0);
}
});
PropertyResetListener<View, Float> propertyResetListener
= new PropertyResetListener<>(TRANSLATION_X, 0f);
for (int i = mOverflowNotifications.isEmpty() ? 0 : 1; i < numIcons; i++) {
final View child = mIconRow.getChildAt(i);
Animator shiftChild = ObjectAnimator.ofFloat(child, TRANSLATION_X, gapWidth);
shiftChild.addListener(propertyResetListener);
animation.play(shiftChild);
}
animation.start();
@ -205,18 +215,18 @@ public class NotificationFooterLayout extends LinearLayout {
}
}
if (mIconRow.getChildCount() == 0) {
// There are no more icons in the secondary view, so hide it.
// There are no more icons in the footer, so hide it.
PopupContainerWithArrow popup = PopupContainerWithArrow.getOpen(
Launcher.getLauncher(getContext()));
int newHeight = getResources().getDimensionPixelSize(
R.dimen.notification_footer_collapsed_height);
AnimatorSet collapseSecondary = LauncherAnimUtils.createAnimatorSet();
collapseSecondary.play(popup.animateTranslationYBy(getHeight() - newHeight, 0));
collapseSecondary.play(LauncherAnimUtils.animateViewHeight(
this, getHeight(), newHeight));
collapseSecondary.setDuration(getResources().getInteger(
R.integer.config_removeNotificationViewDuration));
collapseSecondary.start();
Animator collapseFooter = popup.reduceNotificationViewHeight(getHeight(),
getResources().getInteger(R.integer.config_removeNotificationViewDuration));
collapseFooter.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
((ViewGroup) getParent()).removeView(NotificationFooterLayout.this);
}
});
collapseFooter.start();
}
}

View File

@ -18,29 +18,32 @@ package com.android.launcher3.notification;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
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;
import android.view.animation.LinearInterpolator;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.TextView;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherAnimUtils;
import com.android.launcher3.R;
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;
import static com.android.launcher3.LauncherAnimUtils.animateViewHeight;
/**
* A {@link FrameLayout} that contains a header, main view and a footer.
* The main view contains the icon and text (title + subtext) of the first notification.
@ -51,7 +54,9 @@ public class NotificationItemView extends PopupItemView implements LogContainerP
private static final Rect sTempRect = new Rect();
private TextView mHeader;
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;
@ -73,11 +78,65 @@ public class NotificationItemView extends PopupItemView implements LogContainerP
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mHeader = (TextView) findViewById(R.id.header);
mDivider = findViewById(R.id.divider);
mMainView = (NotificationMainView) findViewById(R.id.main_view);
mFooter = (NotificationFooterLayout) findViewById(R.id.footer);
mSwipeHelper = new SwipeHelper(SwipeHelper.X, mMainView, getContext());
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,
getBackgroundRadius(), newHeight).createRevealAnimator(this, true /* isReversed */);
heightAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
if (newHeight > 0) {
measure(MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(newHeight, MeasureSpec.EXACTLY));
initializeBackgroundClipping(true /* force */);
invalidate();
} else {
((ViewGroup) getParent()).removeView(NotificationItemView.this);
}
}
});
return heightAnimator;
}
@Override
protected float getBackgroundRadius() {
return getResources().getDimensionPixelSize(R.dimen.bg_round_rect_radius);
}
@Override
@ -103,7 +162,7 @@ public class NotificationItemView extends PopupItemView implements LogContainerP
protected ColorStateList getAttachedArrowColor() {
// This NotificationView itself has a different color that is only
// revealed when dismissing notifications.
return mFooter.getBackgroundTintList();
return ColorStateList.valueOf(mFooter.getBackgroundColor());
}
public void applyNotificationInfos(final List<NotificationInfo> notificationInfos) {
@ -122,8 +181,6 @@ public class NotificationItemView extends PopupItemView implements LogContainerP
public void applyColors(IconPalette iconPalette) {
setBackgroundTintList(ColorStateList.valueOf(iconPalette.secondaryColor));
mHeader.setBackgroundTintList(ColorStateList.valueOf(iconPalette.backgroundColor));
mHeader.setTextColor(ColorStateList.valueOf(iconPalette.textColor));
mDivider.setBackgroundColor(iconPalette.secondaryColor);
mMainView.applyColors(iconPalette);
mFooter.applyColors(iconPalette);
@ -154,27 +211,6 @@ public class NotificationItemView extends PopupItemView implements LogContainerP
}
}
public Animator createRemovalAnimation(int fullDuration) {
AnimatorSet animation = new AnimatorSet();
int mainHeight = mMainView.getMeasuredHeight();
Animator removeMainView = animateViewHeight(mMainView, mainHeight, 0);
removeMainView.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
// Remove the remaining views but take on their color instead of the darker one.
setBackgroundTintList(mHeader.getBackgroundTintList());
removeAllViews();
}
});
Animator removeRest = LauncherAnimUtils.animateViewHeight(this, getHeight() - mainHeight, 0);
removeMainView.setDuration(fullDuration / 2);
removeRest.setDuration(fullDuration / 2);
removeMainView.setInterpolator(new LinearInterpolator());
removeRest.setInterpolator(new LinearInterpolator());
animation.playSequentially(removeMainView, removeRest);
return animation;
}
@Override
public void fillInLogContainerData(View v, ItemInfo info, LauncherLogProto.Target target,
LauncherLogProto.Target targetParent) {

View File

@ -34,7 +34,6 @@ import android.graphics.drawable.ShapeDrawable;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.LayoutInflater;
@ -60,6 +59,7 @@ import com.android.launcher3.Utilities;
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
import com.android.launcher3.accessibility.ShortcutMenuAccessibilityDelegate;
import com.android.launcher3.anim.PropertyListBuilder;
import com.android.launcher3.anim.PropertyResetListener;
import com.android.launcher3.badge.BadgeInfo;
import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragLayer;
@ -225,7 +225,7 @@ public class PopupContainerWithArrow extends AbstractFloatingView
}
private void addDummyViews(BubbleTextView originalIcon,
PopupPopulator.Item[] itemsToPopulate, boolean secondaryNotificationViewHasIcons) {
PopupPopulator.Item[] itemsToPopulate, boolean notificationFooterHasIcons) {
final Resources res = getResources();
final int spacing = res.getDimensionPixelSize(R.dimen.deep_shortcuts_spacing);
final LayoutInflater inflater = mLauncher.getLayoutInflater();
@ -234,10 +234,9 @@ public class PopupContainerWithArrow extends AbstractFloatingView
final PopupItemView item = (PopupItemView) inflater.inflate(
itemsToPopulate[i].layoutId, this, false);
if (itemsToPopulate[i] == PopupPopulator.Item.NOTIFICATION) {
int secondaryHeight = secondaryNotificationViewHasIcons ?
res.getDimensionPixelSize(R.dimen.notification_footer_height) :
res.getDimensionPixelSize(R.dimen.notification_footer_collapsed_height);
item.findViewById(R.id.footer).getLayoutParams().height = secondaryHeight;
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;
@ -575,7 +574,8 @@ public class PopupContainerWithArrow extends AbstractFloatingView
}
public void trimNotifications(Map<PackageUserKey, BadgeInfo> updatedBadges) {
final NotificationItemView notificationView = (NotificationItemView) findViewById(R.id.notification_view);
final NotificationItemView notificationView =
(NotificationItemView) findViewById(R.id.notification_view);
if (notificationView == null) {
return;
}
@ -586,9 +586,8 @@ public class PopupContainerWithArrow extends AbstractFloatingView
final int duration = getResources().getInteger(
R.integer.config_removeNotificationViewDuration);
final int spacing = getResources().getDimensionPixelSize(R.dimen.deep_shortcuts_spacing);
removeNotification.play(animateTranslationYBy(notificationView.getHeight() + spacing,
duration));
Animator reduceHeight = notificationView.createRemovalAnimation(duration);
removeNotification.play(reduceNotificationViewHeight(
notificationView.getHeight() + spacing, duration, notificationView));
final View removeMarginView = mIsAboveIcon ? getItemViewAt(getItemCount() - 2)
: notificationView;
if (removeMarginView != null) {
@ -602,7 +601,6 @@ public class PopupContainerWithArrow extends AbstractFloatingView
});
removeNotification.play(removeMargin);
}
removeNotification.play(reduceHeight);
Animator fade = ObjectAnimator.ofFloat(notificationView, ALPHA, 0)
.setDuration(duration);
fade.addListener(new AnimatorListenerAdapter() {
@ -636,16 +634,43 @@ public class PopupContainerWithArrow extends AbstractFloatingView
mArrow, new PropertyListBuilder().scale(scale).build());
}
/**
* Animates the translationY of this container if it is open above the icon.
* If it is below the icon, the container already shifts up when the height
* of a child (e.g. NotificationView) changes, so the translation isn't necessary.
* Animates the height of the notification item and the translationY of other items accordingly.
*/
public @Nullable Animator animateTranslationYBy(int translationY, int duration) {
if (mIsAboveIcon) {
return ObjectAnimator.ofFloat(this, TRANSLATION_Y, getTranslationY() + translationY)
.setDuration(duration);
public Animator reduceNotificationViewHeight(int heightToRemove, int duration,
NotificationItemView notificationItem) {
final int translateYBy = mIsAboveIcon ? heightToRemove : -heightToRemove;
AnimatorSet animatorSet = LauncherAnimUtils.createAnimatorSet();
animatorSet.play(notificationItem.animateHeightRemoval(heightToRemove));
PropertyResetListener<View, Float> resetTranslationYListener
= new PropertyResetListener<>(TRANSLATION_Y, 0f);
for (int i = 0; i < getItemCount(); i++) {
final PopupItemView itemView = getItemViewAt(i);
if (!mIsAboveIcon && itemView == notificationItem) {
// The notification view is already in the right place when container is below icon.
continue;
}
return null;
ValueAnimator translateItem = ObjectAnimator.ofFloat(itemView, TRANSLATION_Y,
itemView.getTranslationY() + translateYBy).setDuration(duration);
translateItem.addListener(resetTranslationYListener);
animatorSet.play(translateItem);
}
if (mIsAboveIcon) {
// All the items, including the notification item, translated down, but the
// container itself did not. This means the items would jump back to their
// original translation unless we update the container's translationY here.
animatorSet.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
setTranslationY(getTranslationY() + translateYBy);
}
});
}
return animatorSet;
}
public Animator reduceNotificationViewHeight(int heightToRemove, int duration) {
return reduceNotificationViewHeight(heightToRemove, duration,
(NotificationItemView) findViewById(R.id.notification_view));
}
@Override

View File

@ -42,7 +42,7 @@ public abstract class PopupItemView extends FrameLayout
protected static final Point sTempPoint = new Point();
private final Rect mPillRect;
protected final Rect mPillRect;
private float mOpenAnimationProgress;
protected View mIconView;
@ -147,6 +147,10 @@ public abstract class PopupItemView extends FrameLayout
return sTempPoint;
}
protected float getBackgroundRadius() {
return getResources().getDimensionPixelSize(R.dimen.bg_pill_radius);
}
/**
* Extension of {@link PillRevealOutlineProvider} which scales the icon based on the height.
*/
@ -161,10 +165,9 @@ public abstract class PopupItemView extends FrameLayout
private final boolean mPivotLeft;
private final float mTranslateX;
public ZoomRevealOutlineProvider(int x, int y, Rect pillRect,
View translateView, View zoomView, boolean isContainerAboveIcon, boolean pivotLeft) {
super(x, y, pillRect, zoomView.getResources().getDimensionPixelSize(
R.dimen.bg_pill_radius));
public ZoomRevealOutlineProvider(int x, int y, Rect pillRect, PopupItemView translateView,
View zoomView, boolean isContainerAboveIcon, boolean pivotLeft) {
super(x, y, pillRect, translateView.getBackgroundRadius());
mTranslateView = translateView;
mZoomView = zoomView;
mFullHeight = pillRect.height();