diff options
author | 2023-03-23 18:57:24 +0000 | |
---|---|---|
committer | 2023-03-23 18:57:24 +0000 | |
commit | 2ca1722eed8806dafd814e61bbdd0578fdda90cc (patch) | |
tree | 17d803ce60c108f7e83d753bf2d06c05d32219e0 | |
parent | bd116374228ddfc1b2a4e40d70484636837d007e (diff) | |
parent | 85ff8d76831e01c3a9b3a04db0ec480094505b7e (diff) |
Merge "Update Chooser icon animation; animate labels" into udc-dev am: 85ff8d7683
Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/modules/IntentResolver/+/22214716
Change-Id: I4ca94e88814c4b2816e64ea32ce791f4161e1b83
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
3 files changed, 94 insertions, 17 deletions
diff --git a/java/src/com/android/intentresolver/ChooserListAdapter.java b/java/src/com/android/intentresolver/ChooserListAdapter.java index dab44577..ceeed6f0 100644 --- a/java/src/com/android/intentresolver/ChooserListAdapter.java +++ b/java/src/com/android/intentresolver/ChooserListAdapter.java @@ -98,6 +98,8 @@ public class ChooserListAdapter extends ResolverListAdapter { // Sorted list of DisplayResolveInfos for the alphabetical app section. private List<DisplayResolveInfo> mSortedList = new ArrayList<>(); + private final ItemRevealAnimationTracker mAnimationTracker = new ItemRevealAnimationTracker(); + // For pinned direct share labels, if the text spans multiple lines, the TextView will consume // the full width, even if the characters actually take up less than that. Measure the actual // line widths and constrain the View's width based upon that so that the pin doesn't end up @@ -243,6 +245,15 @@ public class ChooserListAdapter extends ResolverListAdapter { } + @Override + protected boolean rebuildList(boolean doPostProcessing) { + mAnimationTracker.reset(); + mSortedList.clear(); + boolean result = super.rebuildList(doPostProcessing); + notifyDataSetChanged(); + return result; + } + private void createPlaceHolders() { mServiceTargets.clear(); for (int i = 0; i < mMaxRankedTargets; ++i) { @@ -266,7 +277,17 @@ public class ChooserListAdapter extends ResolverListAdapter { } holder.bindLabel(info.getDisplayLabel(), info.getExtendedInfo(), alwaysShowSubLabel()); - holder.bindIcon(info, /*animate =*/ true); + mAnimationTracker.animateLabel(holder.text, info); + if (holder.text2.getVisibility() == View.VISIBLE) { + mAnimationTracker.animateLabel(holder.text2, info); + } + holder.bindIcon(info); + if (info.getDisplayIconHolder().getDisplayIcon() != null) { + mAnimationTracker.animateIcon(holder.icon, info); + } else { + holder.icon.clearAnimation(); + } + if (info.isSelectableTargetInfo()) { // direct share targets should append the application name for a better readout DisplayResolveInfo rInfo = info.getDisplayResolveInfo(); diff --git a/java/src/com/android/intentresolver/ItemRevealAnimationTracker.kt b/java/src/com/android/intentresolver/ItemRevealAnimationTracker.kt new file mode 100644 index 00000000..d3e07c6b --- /dev/null +++ b/java/src/com/android/intentresolver/ItemRevealAnimationTracker.kt @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2023 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.intentresolver + +import android.view.View +import android.view.animation.AlphaAnimation +import android.view.animation.LinearInterpolator +import android.view.animation.Transformation +import com.android.intentresolver.chooser.TargetInfo + +private const val IMAGE_FADE_IN_MILLIS = 150L + +internal class ItemRevealAnimationTracker { + private val iconProgress = HashMap<TargetInfo, Record>() + private val labelProgress = HashMap<TargetInfo, Record>() + + fun reset() { + iconProgress.clear() + labelProgress.clear() + } + + fun animateIcon(view: View, info: TargetInfo) = animateView(view, info, iconProgress) + fun animateLabel(view: View, info: TargetInfo) = animateView(view, info, labelProgress) + + private fun animateView(view: View, info: TargetInfo, map: MutableMap<TargetInfo, Record>) { + val record = map.getOrPut(info) { + Record() + } + if ((view.animation as? RevealAnimation)?.record === record) return + + view.clearAnimation() + if (record.alpha >= 1f) { + view.alpha = 1f + return + } + + view.startAnimation(RevealAnimation(record)) + } + + private class Record(var alpha: Float = 0f) + + private class RevealAnimation(val record: Record) : AlphaAnimation(record.alpha, 1f) { + init { + duration = (IMAGE_FADE_IN_MILLIS * (1f - record.alpha)).toLong() + interpolator = LinearInterpolator() + } + + override fun applyTransformation(interpolatedTime: Float, t: Transformation) { + super.applyTransformation(interpolatedTime, t) + // One TargetInfo can be simultaneously bou into multiple UI grid items; make sure + // that the alpha value only increases. This should not affect running animations, only + // a starting point for a new animation when a different view is bound to this target. + record.alpha = minOf(1f, maxOf(record.alpha, t.alpha)) + } + } +} diff --git a/java/src/com/android/intentresolver/ResolverListAdapter.java b/java/src/com/android/intentresolver/ResolverListAdapter.java index b0586f2d..f090f3a2 100644 --- a/java/src/com/android/intentresolver/ResolverListAdapter.java +++ b/java/src/com/android/intentresolver/ResolverListAdapter.java @@ -18,7 +18,6 @@ package com.android.intentresolver; import static android.content.Context.ACTIVITY_SERVICE; -import android.animation.ObjectAnimator; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; @@ -43,7 +42,6 @@ import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.view.animation.DecelerateInterpolator; import android.widget.AbsListView; import android.widget.BaseAdapter; import android.widget.ImageView; @@ -926,7 +924,6 @@ public class ResolverListAdapter extends BaseAdapter { */ @VisibleForTesting public static class ViewHolder { - private static final long IMAGE_FADE_IN_MILLIS = 150; public View itemView; public Drawable defaultItemViewBackground; @@ -964,23 +961,12 @@ public class ResolverListAdapter extends BaseAdapter { itemView.setContentDescription(description); } - public void bindIcon(TargetInfo info) { - bindIcon(info, false); - } - /** - * Bind view holder to a TargetInfo, run icon reveal animation, if required. + * Bind view holder to a TargetInfo. */ - public void bindIcon(TargetInfo info, boolean animate) { + public void bindIcon(TargetInfo info) { Drawable displayIcon = info.getDisplayIconHolder().getDisplayIcon(); - boolean runAnimation = animate && (icon.getDrawable() == null) && (displayIcon != null); icon.setImageDrawable(displayIcon); - if (runAnimation) { - ObjectAnimator animator = ObjectAnimator.ofFloat(icon, "alpha", 0.0f, 1.0f); - animator.setInterpolator(new DecelerateInterpolator(1.0f)); - animator.setDuration(IMAGE_FADE_IN_MILLIS); - animator.start(); - } if (info.isSuspended()) { icon.setColorFilter(getSuspendedColorMatrix()); } else { |