Merge "Removing custom state definition from FastBitmapDrawable" into ub-launcher3-dorval

This commit is contained in:
TreeHugger Robot
2017-02-24 00:37:30 +00:00
committed by Android (Google) Code Review
7 changed files with 135 additions and 235 deletions
@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright 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.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_activated="true">
<set>
<objectAnimator
android:duration="225"
android:propertyName="scaleX"
android:valueTo="1.15"
android:valueType="floatType" />
<objectAnimator
android:duration="225"
android:propertyName="scaleY"
android:valueTo="1.15"
android:valueType="floatType" />
</set>
</item>
<item>
<set>
<objectAnimator
android:duration="275"
android:propertyName="scaleX"
android:valueTo="1"
android:valueType="floatType" />
<objectAnimator
android:duration="275"
android:propertyName="scaleY"
android:valueTo="1"
android:valueType="floatType" />
</set>
</item>
</selector>
+1
View File
@@ -20,6 +20,7 @@
android:id="@+id/icon" android:id="@+id/icon"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:stateListAnimator="@animator/all_apps_fastscroll_icon_anim"
launcher:iconDisplay="all_apps" launcher:iconDisplay="all_apps"
launcher:centerVertically="true" launcher:centerVertically="true"
android:paddingLeft="4dp" android:paddingLeft="4dp"
@@ -36,10 +36,6 @@ import com.android.launcher3.util.Themes;
*/ */
public class BaseRecyclerViewFastScrollBar { public class BaseRecyclerViewFastScrollBar {
public interface FastScrollFocusableView {
void setFastScrollFocusState(final FastBitmapDrawable.State focusState, boolean animated);
}
private static final Property<BaseRecyclerViewFastScrollBar, Integer> TRACK_WIDTH = private static final Property<BaseRecyclerViewFastScrollBar, Integer> TRACK_WIDTH =
new Property<BaseRecyclerViewFastScrollBar, Integer>(Integer.class, "width") { new Property<BaseRecyclerViewFastScrollBar, Integer>(Integer.class, "width") {
+16 -72
View File
@@ -53,8 +53,7 @@ import java.text.NumberFormat;
* because we want to make the bubble taller than the text and TextView's clip is * because we want to make the bubble taller than the text and TextView's clip is
* too aggressive. * too aggressive.
*/ */
public class BubbleTextView extends TextView public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver {
implements BaseRecyclerViewFastScrollBar.FastScrollFocusableView, ItemInfoUpdateReceiver {
// Dimensions in DP // Dimensions in DP
private static final float AMBIENT_SHADOW_RADIUS = 2.5f; private static final float AMBIENT_SHADOW_RADIUS = 2.5f;
@@ -67,6 +66,8 @@ public class BubbleTextView extends TextView
private static final int DISPLAY_ALL_APPS = 1; private static final int DISPLAY_ALL_APPS = 1;
private static final int DISPLAY_FOLDER = 2; private static final int DISPLAY_FOLDER = 2;
private static final int[] STATE_PRESSED = new int[] {android.R.attr.state_pressed};
private final Launcher mLauncher; private final Launcher mLauncher;
private Drawable mIcon; private Drawable mIcon;
private final boolean mCenterVertically; private final boolean mCenterVertically;
@@ -232,14 +233,21 @@ public class BubbleTextView extends TextView
} }
@Override @Override
public void setPressed(boolean pressed) { public void refreshDrawableState() {
super.setPressed(pressed);
if (!mIgnorePressedStateChange) { if (!mIgnorePressedStateChange) {
updateIconState(); super.refreshDrawableState();
} }
} }
@Override
protected int[] onCreateDrawableState(int extraSpace) {
final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
if (mStayPressed) {
mergeDrawableStates(drawableState, STATE_PRESSED);
}
return drawableState;
}
/** Returns the icon for this view. */ /** Returns the icon for this view. */
public Drawable getIcon() { public Drawable getIcon() {
return mIcon; return mIcon;
@@ -250,17 +258,6 @@ public class BubbleTextView extends TextView
return mLayoutHorizontal; return mLayoutHorizontal;
} }
private void updateIconState() {
if (mIcon instanceof FastBitmapDrawable) {
FastBitmapDrawable d = (FastBitmapDrawable) mIcon;
if (isPressed() || mStayPressed) {
d.animateState(FastBitmapDrawable.State.PRESSED);
} else {
d.animateState(FastBitmapDrawable.State.NORMAL);
}
}
}
@Override @Override
public void setOnLongClickListener(OnLongClickListener l) { public void setOnLongClickListener(OnLongClickListener l) {
super.setOnLongClickListener(l); super.setOnLongClickListener(l);
@@ -334,7 +331,7 @@ public class BubbleTextView extends TextView
this, mPressedBackground); this, mPressedBackground);
} }
updateIconState(); refreshDrawableState();
} }
void clearPressedBackground() { void clearPressedBackground() {
@@ -364,7 +361,7 @@ public class BubbleTextView extends TextView
mPressedBackground = null; mPressedBackground = null;
mIgnorePressedStateChange = false; mIgnorePressedStateChange = false;
updateIconState(); refreshDrawableState();
return result; return result;
} }
@@ -544,10 +541,6 @@ public class BubbleTextView extends TextView
@Override @Override
public void reapplyItemInfo(ItemInfoWithIcon info) { public void reapplyItemInfo(ItemInfoWithIcon info) {
if (getTag() == info) { if (getTag() == info) {
FastBitmapDrawable.State prevState = FastBitmapDrawable.State.NORMAL;
if (mIcon instanceof FastBitmapDrawable) {
prevState = ((FastBitmapDrawable) mIcon).getCurrentState();
}
mIconLoadRequest = null; mIconLoadRequest = null;
mDisableRelayout = true; mDisableRelayout = true;
@@ -566,12 +559,6 @@ public class BubbleTextView extends TextView
applyFromPackageItemInfo((PackageItemInfo) info); applyFromPackageItemInfo((PackageItemInfo) info);
} }
// If we are reapplying over an old icon, then we should update the new icon to the same
// state as the old icon
if (mIcon instanceof FastBitmapDrawable) {
((FastBitmapDrawable) mIcon).setState(prevState);
}
mDisableRelayout = false; mDisableRelayout = false;
} }
} }
@@ -593,34 +580,6 @@ public class BubbleTextView extends TextView
} }
} }
@Override
public void setFastScrollFocusState(final FastBitmapDrawable.State focusState, boolean animated) {
// We can only set the fast scroll focus state on a FastBitmapDrawable
if (!(mIcon instanceof FastBitmapDrawable)) {
return;
}
FastBitmapDrawable d = (FastBitmapDrawable) mIcon;
if (animated) {
FastBitmapDrawable.State prevState = d.getCurrentState();
if (d.animateState(focusState)) {
// If the state was updated, then update the view accordingly
animate().scaleX(focusState.viewScale)
.scaleY(focusState.viewScale)
.setStartDelay(getStartDelayForStateChange(prevState, focusState))
.setDuration(d.getDurationForStateChange(prevState, focusState))
.start();
}
} else {
if (d.setState(focusState)) {
// If the state was updated, then update the view accordingly
animate().cancel();
setScaleX(focusState.viewScale);
setScaleY(focusState.viewScale);
}
}
}
/** /**
* Returns true if the view can show custom shortcuts. * Returns true if the view can show custom shortcuts.
*/ */
@@ -629,21 +588,6 @@ public class BubbleTextView extends TextView
.isEmpty(); .isEmpty();
} }
/**
* Returns the start delay when animating between certain {@link FastBitmapDrawable} states.
*/
private static int getStartDelayForStateChange(final FastBitmapDrawable.State fromState,
final FastBitmapDrawable.State toState) {
switch (toState) {
case NORMAL:
switch (fromState) {
case FAST_SCROLL_HIGHLIGHTED:
return FastBitmapDrawable.FAST_SCROLL_INACTIVE_DURATION / 4;
}
}
return 0;
}
/** /**
* Interface to be implemented by the grand parent to allow click shadow effect. * Interface to be implemented by the grand parent to allow click shadow effect.
*/ */
+53 -127
View File
@@ -16,7 +16,6 @@
package com.android.launcher3; package com.android.launcher3;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator; import android.animation.ObjectAnimator;
import android.animation.TimeInterpolator; import android.animation.TimeInterpolator;
import android.graphics.Bitmap; import android.graphics.Bitmap;
@@ -32,42 +31,19 @@ import android.graphics.PorterDuffColorFilter;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.util.Property; import android.util.Property;
import android.util.SparseArray; import android.util.SparseArray;
import android.view.animation.DecelerateInterpolator;
import com.android.launcher3.badge.BadgeInfo; import com.android.launcher3.badge.BadgeInfo;
import com.android.launcher3.badge.BadgeRenderer; import com.android.launcher3.badge.BadgeRenderer;
import com.android.launcher3.graphics.IconPalette; import com.android.launcher3.graphics.IconPalette;
public class FastBitmapDrawable extends Drawable { public class FastBitmapDrawable extends Drawable {
private static final int[] STATE_PRESSED = new int[] {android.R.attr.state_pressed};
private static final float PRESSED_BRIGHTNESS = 100f / 255f;
private static final float DISABLED_DESATURATION = 1f; private static final float DISABLED_DESATURATION = 1f;
private static final float DISABLED_BRIGHTNESS = 0.5f; private static final float DISABLED_BRIGHTNESS = 0.5f;
/**
* The possible states that a FastBitmapDrawable can be in.
*/
public enum State {
NORMAL (0f, 0f, 1f, new DecelerateInterpolator()),
PRESSED (0f, 100f / 255f, 1f, CLICK_FEEDBACK_INTERPOLATOR),
FAST_SCROLL_HIGHLIGHTED (0f, 0f, 1.15f, new DecelerateInterpolator()),
FAST_SCROLL_UNHIGHLIGHTED (0f, 0f, 1f, new DecelerateInterpolator());
public final float desaturation;
public final float brightness;
/**
* Used specifically by the view drawing this FastBitmapDrawable.
*/
public final float viewScale;
public final TimeInterpolator interpolator;
State(float desaturation, float brightness, float viewScale, TimeInterpolator interpolator) {
this.desaturation = desaturation;
this.brightness = brightness;
this.viewScale = viewScale;
this.interpolator = interpolator;
}
}
public static final TimeInterpolator CLICK_FEEDBACK_INTERPOLATOR = new TimeInterpolator() { public static final TimeInterpolator CLICK_FEEDBACK_INTERPOLATOR = new TimeInterpolator() {
@Override @Override
@@ -82,10 +58,6 @@ public class FastBitmapDrawable extends Drawable {
} }
}; };
public static final int CLICK_FEEDBACK_DURATION = 2000; public static final int CLICK_FEEDBACK_DURATION = 2000;
public static final int FAST_SCROLL_HIGHLIGHT_DURATION = 225;
public static final int FAST_SCROLL_UNHIGHLIGHT_DURATION = 150;
public static final int FAST_SCROLL_UNHIGHLIGHT_FROM_NORMAL_DURATION = 225;
public static final int FAST_SCROLL_INACTIVE_DURATION = 275;
// Since we don't need 256^2 values for combinations of both the brightness and saturation, we // Since we don't need 256^2 values for combinations of both the brightness and saturation, we
// reduce the value space to a smaller value V, which reduces the number of cached // reduce the value space to a smaller value V, which reduces the number of cached
@@ -101,7 +73,8 @@ public class FastBitmapDrawable extends Drawable {
protected final Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.ANTI_ALIAS_FLAG); protected final Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.ANTI_ALIAS_FLAG);
private final Bitmap mBitmap; private final Bitmap mBitmap;
private State mState = State.NORMAL;
private boolean mIsPressed;
private boolean mIsDisabled; private boolean mIsDisabled;
private BadgeInfo mBadgeInfo; private BadgeInfo mBadgeInfo;
@@ -123,6 +96,19 @@ public class FastBitmapDrawable extends Drawable {
} }
}; };
private static final Property<FastBitmapDrawable, Float> BRIGHTNESS
= new Property<FastBitmapDrawable, Float>(Float.TYPE, "brightness") {
@Override
public Float get(FastBitmapDrawable fastBitmapDrawable) {
return fastBitmapDrawable.getBrightness();
}
@Override
public void set(FastBitmapDrawable fastBitmapDrawable, Float value) {
fastBitmapDrawable.setBrightness(value);
}
};
// The saturation and brightness are values that are mapped to REDUCED_FILTER_VALUE_SPACE and // The saturation and brightness are values that are mapped to REDUCED_FILTER_VALUE_SPACE and
// as a result, can be used to compose the key for the cached ColorMatrixColorFilters // as a result, can be used to compose the key for the cached ColorMatrixColorFilters
private int mDesaturation = 0; private int mDesaturation = 0;
@@ -130,8 +116,8 @@ public class FastBitmapDrawable extends Drawable {
private int mAlpha = 255; private int mAlpha = 255;
private int mPrevUpdateKey = Integer.MAX_VALUE; private int mPrevUpdateKey = Integer.MAX_VALUE;
// Animators for the fast bitmap drawable's properties // Animators for the fast bitmap drawable's brightness
private AnimatorSet mPropertyAnimator; private ObjectAnimator mBrightnessAnimator;
public FastBitmapDrawable(Bitmap b) { public FastBitmapDrawable(Bitmap b) {
mBitmap = b; mBitmap = b;
@@ -243,60 +229,50 @@ public class FastBitmapDrawable extends Drawable {
return mBitmap; return mBitmap;
} }
/** @Override
* Animates this drawable to a new state. public boolean isStateful() {
* return true;
* @return whether the state has changed.
*/
public boolean animateState(State newState) {
State prevState = mState;
if (mState != newState) {
mState = newState;
float desaturation = mIsDisabled ? DISABLED_DESATURATION : newState.desaturation;
float brightness = mIsDisabled ? DISABLED_BRIGHTNESS: newState.brightness;
mPropertyAnimator = cancelAnimator(mPropertyAnimator);
mPropertyAnimator = new AnimatorSet();
mPropertyAnimator.playTogether(
ObjectAnimator.ofFloat(this, "desaturation", desaturation),
ObjectAnimator.ofFloat(this, "brightness", brightness));
mPropertyAnimator.setInterpolator(newState.interpolator);
mPropertyAnimator.setDuration(getDurationForStateChange(prevState, newState));
mPropertyAnimator.setStartDelay(getStartDelayForStateChange(prevState, newState));
mPropertyAnimator.start();
return true;
}
return false;
} }
/** @Override
* Immediately sets this drawable to a new state. protected boolean onStateChange(int[] state) {
* boolean isPressed = false;
* @return whether the state has changed. for (int s : state) {
*/ if (s == android.R.attr.state_pressed) {
public boolean setState(State newState) { isPressed = true;
if (mState != newState) { break;
mState = newState; }
}
if (mIsPressed != isPressed) {
mIsPressed = isPressed;
mPropertyAnimator = cancelAnimator(mPropertyAnimator); if (mBrightnessAnimator != null) {
mBrightnessAnimator.cancel();
}
invalidateDesaturationAndBrightness(); if (mIsPressed) {
// Animate when going to pressed state
mBrightnessAnimator = ObjectAnimator.ofFloat(
this, BRIGHTNESS, getExpectedBrightness());
mBrightnessAnimator.setDuration(CLICK_FEEDBACK_DURATION);
mBrightnessAnimator.setInterpolator(CLICK_FEEDBACK_INTERPOLATOR);
mBrightnessAnimator.start();
} else {
setBrightness(getExpectedBrightness());
}
return true; return true;
} }
return false; return false;
} }
private void invalidateDesaturationAndBrightness() { private void invalidateDesaturationAndBrightness() {
setDesaturation(mIsDisabled ? DISABLED_DESATURATION : mState.desaturation); setDesaturation(mIsDisabled ? DISABLED_DESATURATION : 0);
setBrightness(mIsDisabled ? DISABLED_BRIGHTNESS: mState.brightness); setBrightness(getExpectedBrightness());
} }
/** private float getExpectedBrightness() {
* Returns the current state. return mIsDisabled ? DISABLED_BRIGHTNESS :
*/ (mIsPressed ? PRESSED_BRIGHTNESS : 0);
public State getCurrentState() {
return mState;
} }
public void setIsDisabled(boolean isDisabled) { public void setIsDisabled(boolean isDisabled) {
@@ -306,49 +282,6 @@ public class FastBitmapDrawable extends Drawable {
} }
} }
/**
* Returns the duration for the state change animation.
*/
public static int getDurationForStateChange(State fromState, State toState) {
switch (toState) {
case NORMAL:
switch (fromState) {
case PRESSED:
return 0;
case FAST_SCROLL_HIGHLIGHTED:
case FAST_SCROLL_UNHIGHLIGHTED:
return FAST_SCROLL_INACTIVE_DURATION;
}
case PRESSED:
return CLICK_FEEDBACK_DURATION;
case FAST_SCROLL_HIGHLIGHTED:
return FAST_SCROLL_HIGHLIGHT_DURATION;
case FAST_SCROLL_UNHIGHLIGHTED:
switch (fromState) {
case NORMAL:
// When animating from normal state, take a little longer
return FAST_SCROLL_UNHIGHLIGHT_FROM_NORMAL_DURATION;
default:
return FAST_SCROLL_UNHIGHLIGHT_DURATION;
}
}
return 0;
}
/**
* Returns the start delay when animating between certain fast scroll states.
*/
public static int getStartDelayForStateChange(State fromState, State toState) {
switch (toState) {
case FAST_SCROLL_UNHIGHLIGHTED:
switch (fromState) {
case NORMAL:
return FAST_SCROLL_UNHIGHLIGHT_DURATION / 4;
}
}
return 0;
}
/** /**
* Sets the saturation of this icon, 0 [full color] -> 1 [desaturated] * Sets the saturation of this icon, 0 [full color] -> 1 [desaturated]
*/ */
@@ -435,11 +368,4 @@ public class FastBitmapDrawable extends Drawable {
} }
invalidateSelf(); invalidateSelf();
} }
private AnimatorSet cancelAnimator(AnimatorSet animator) {
if (animator != null) {
animator.cancel();
}
return null;
}
} }
@@ -19,6 +19,7 @@ import android.support.v7.widget.RecyclerView;
import android.view.View; import android.view.View;
import com.android.launcher3.BaseRecyclerViewFastScrollBar; import com.android.launcher3.BaseRecyclerViewFastScrollBar;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.FastBitmapDrawable; import com.android.launcher3.FastBitmapDrawable;
import com.android.launcher3.util.Thunk; import com.android.launcher3.util.Thunk;
@@ -45,8 +46,7 @@ public class AllAppsFastScrollHelper implements AllAppsGridAdapter.BindViewCallb
// Set of all views animated during fast scroll. We keep track of these ourselves since there // Set of all views animated during fast scroll. We keep track of these ourselves since there
// is no way to reset a view once it gets scrapped or recycled without other hacks // is no way to reset a view once it gets scrapped or recycled without other hacks
private HashSet<BaseRecyclerViewFastScrollBar.FastScrollFocusableView> mTrackedFastScrollViews = private HashSet<RecyclerView.ViewHolder> mTrackedFastScrollViews = new HashSet<>();
new HashSet<>();
// Smooth fast-scroll animation frames // Smooth fast-scroll animation frames
@Thunk int mFastScrollFrameIndex; @Thunk int mFastScrollFrameIndex;
@@ -186,12 +186,7 @@ public class AllAppsFastScrollHelper implements AllAppsGridAdapter.BindViewCallb
public void onBindView(AllAppsGridAdapter.ViewHolder holder) { public void onBindView(AllAppsGridAdapter.ViewHolder holder) {
// Update newly bound views to the current fast scroll state if we are fast scrolling // Update newly bound views to the current fast scroll state if we are fast scrolling
if (mCurrentFastScrollSection != null || mTargetFastScrollSection != null) { if (mCurrentFastScrollSection != null || mTargetFastScrollSection != null) {
if (holder.itemView instanceof BaseRecyclerViewFastScrollBar.FastScrollFocusableView) { mTrackedFastScrollViews.add(holder);
BaseRecyclerViewFastScrollBar.FastScrollFocusableView v =
(BaseRecyclerViewFastScrollBar.FastScrollFocusableView) holder.itemView;
updateViewFastScrollFocusState(v, holder.getPosition(), false /* animated */);
mTrackedFastScrollViews.add(v);
}
} }
} }
@@ -201,9 +196,9 @@ public class AllAppsFastScrollHelper implements AllAppsGridAdapter.BindViewCallb
private void trackAllChildViews() { private void trackAllChildViews() {
int childCount = mRv.getChildCount(); int childCount = mRv.getChildCount();
for (int i = 0; i < childCount; i++) { for (int i = 0; i < childCount; i++) {
View v = mRv.getChildAt(i); RecyclerView.ViewHolder viewHolder = mRv.getChildViewHolder(mRv.getChildAt(i));
if (v instanceof BaseRecyclerViewFastScrollBar.FastScrollFocusableView) { if (viewHolder != null) {
mTrackedFastScrollViews.add((BaseRecyclerViewFastScrollBar.FastScrollFocusableView) v); mTrackedFastScrollViews.add(viewHolder);
} }
} }
} }
@@ -212,27 +207,16 @@ public class AllAppsFastScrollHelper implements AllAppsGridAdapter.BindViewCallb
* Updates the fast scroll focus on all the children. * Updates the fast scroll focus on all the children.
*/ */
private void updateTrackedViewsFastScrollFocusState() { private void updateTrackedViewsFastScrollFocusState() {
for (BaseRecyclerViewFastScrollBar.FastScrollFocusableView v : mTrackedFastScrollViews) { for (RecyclerView.ViewHolder viewHolder : mTrackedFastScrollViews) {
RecyclerView.ViewHolder viewHolder = mRv.getChildViewHolder((View) v); int pos = viewHolder.getAdapterPosition();
int pos = (viewHolder != null) ? viewHolder.getPosition() : -1; boolean isActive = false;
updateViewFastScrollFocusState(v, pos, true); if (mCurrentFastScrollSection != null && pos > -1) {
AlphabeticalAppsList.AdapterItem item = mApps.getAdapterItems().get(pos);
isActive = item != null &&
mCurrentFastScrollSection.equals(item.sectionName) &&
item.position == mTargetFastScrollPosition;
}
viewHolder.itemView.setActivated(isActive);
} }
} }
/**
* Updates the fast scroll focus on all a given view.
*/
private void updateViewFastScrollFocusState(BaseRecyclerViewFastScrollBar.FastScrollFocusableView v,
int pos, boolean animated) {
FastBitmapDrawable.State newState = FastBitmapDrawable.State.NORMAL;
if (mCurrentFastScrollSection != null && pos > -1) {
AlphabeticalAppsList.AdapterItem item = mApps.getAdapterItems().get(pos);
boolean highlight = item.sectionName.equals(mCurrentFastScrollSection) &&
item.position == mTargetFastScrollPosition;
newState = highlight ?
FastBitmapDrawable.State.FAST_SCROLL_HIGHLIGHTED :
FastBitmapDrawable.State.FAST_SCROLL_UNHIGHLIGHTED;
}
v.setFastScrollFocusState(newState, animated);
}
} }