diff options
Diffstat (limited to 'java')
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 { |