1/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.documentsui.dirlist;
18
19import android.animation.Animator;
20import android.animation.ArgbEvaluator;
21import android.animation.ValueAnimator;
22import android.content.Context;
23import android.support.v4.util.ArrayMap;
24import android.support.v7.widget.DefaultItemAnimator;
25import android.support.v7.widget.RecyclerView;
26import android.util.TypedValue;
27
28import com.android.documentsui.R;
29
30import java.util.ArrayList;
31import java.util.List;
32import java.util.Map;
33
34/**
35 * Performs change animations on Items in DirectoryFragment's RecyclerView.  This class overrides
36 * the way selection animations are normally performed - instead of cross fading the old Item with a
37 * new Item, this class manually animates a background color change.  This enables selected Items to
38 * correctly maintain focus.
39 */
40class DirectoryItemAnimator extends DefaultItemAnimator {
41    private final List<ColorAnimation> mPendingAnimations = new ArrayList<>();
42    private final Map<RecyclerView.ViewHolder, ColorAnimation> mRunningAnimations =
43            new ArrayMap<>();
44    private final Integer mDefaultColor;
45    private final Integer mSelectedColor;
46
47    public DirectoryItemAnimator(Context context) {
48        mDefaultColor = context.getResources().getColor(R.color.item_doc_background);
49        mSelectedColor = context.getResources().getColor(R.color.item_doc_background_selected);
50    }
51
52    @Override
53    public void runPendingAnimations() {
54        super.runPendingAnimations();
55        for (ColorAnimation anim: mPendingAnimations) {
56            anim.start();
57            mRunningAnimations.put(anim.viewHolder, anim);
58        }
59        mPendingAnimations.clear();
60    }
61
62    @Override
63    public void endAnimation(RecyclerView.ViewHolder vh) {
64        super.endAnimation(vh);
65
66        for (int i = mPendingAnimations.size() - 1; i >= 0; --i) {
67            ColorAnimation anim = mPendingAnimations.get(i);
68            if (anim.viewHolder == vh) {
69                mPendingAnimations.remove(i);
70                anim.end();
71            }
72        }
73
74        ColorAnimation anim = mRunningAnimations.get(vh);
75        if (anim != null) {
76            anim.cancel();
77        }
78    }
79
80    @Override
81    public ItemHolderInfo recordPreLayoutInformation(
82        RecyclerView.State state,
83        RecyclerView.ViewHolder viewHolder,
84        @AdapterChanges int changeFlags,
85        List<Object> payloads) {
86        ItemInfo info = (ItemInfo) super.recordPreLayoutInformation(state,
87                viewHolder, changeFlags, payloads);
88        info.isActivated = viewHolder.itemView.isActivated();
89        return info;
90    }
91
92
93    @Override
94    public ItemHolderInfo recordPostLayoutInformation(
95        RecyclerView.State state, RecyclerView.ViewHolder viewHolder) {
96        ItemInfo info = (ItemInfo) super.recordPostLayoutInformation(state,
97                viewHolder);
98        info.isActivated = viewHolder.itemView.isActivated();
99        return info;
100    }
101
102    @Override
103    public boolean animateChange(final RecyclerView.ViewHolder oldHolder,
104            RecyclerView.ViewHolder newHolder, ItemHolderInfo preInfo,
105            ItemHolderInfo postInfo) {
106        if (oldHolder != newHolder) {
107            return super.animateChange(oldHolder, newHolder, preInfo, postInfo);
108        }
109
110        ItemInfo pre = (ItemInfo)preInfo;
111        ItemInfo post = (ItemInfo)postInfo;
112
113        if (pre.isActivated == post.isActivated) {
114            dispatchAnimationFinished(oldHolder);
115            return false;
116        } else {
117            Integer startColor = pre.isActivated ? mSelectedColor : mDefaultColor;
118            Integer endColor = post.isActivated ? mSelectedColor : mDefaultColor;
119            oldHolder.itemView.setBackgroundColor(startColor);
120            mPendingAnimations.add(new ColorAnimation(oldHolder, startColor, endColor));
121        }
122        return true;
123    }
124
125    @Override
126    public ItemHolderInfo obtainHolderInfo() {
127        return new ItemInfo();
128    }
129
130    @Override
131    public boolean canReuseUpdatedViewHolder(RecyclerView.ViewHolder vh) {
132        return true;
133    }
134
135    class ItemInfo extends DefaultItemAnimator.ItemHolderInfo {
136        boolean isActivated;
137    };
138
139    /**
140     * Animates changes in background color.
141     */
142    class ColorAnimation
143            implements ValueAnimator.AnimatorUpdateListener, Animator.AnimatorListener {
144        ValueAnimator mValueAnimator;
145        final RecyclerView.ViewHolder viewHolder;
146        int mEndColor;
147
148        public ColorAnimation(RecyclerView.ViewHolder vh, int startColor, int endColor)
149        {
150            viewHolder = vh;
151            mValueAnimator = ValueAnimator.ofObject(new ArgbEvaluator(), startColor, endColor);
152            mValueAnimator.addUpdateListener(this);
153            mValueAnimator.addListener(this);
154
155            mEndColor = endColor;
156        }
157
158        public void start() {
159            mValueAnimator.start();
160        }
161
162        public void cancel() {
163            mValueAnimator.cancel();
164        }
165
166        public void end() {
167            mValueAnimator.end();
168        }
169
170        @Override
171        public void onAnimationUpdate(ValueAnimator animator) {
172            viewHolder.itemView.setBackgroundColor((Integer)animator.getAnimatedValue());
173        }
174
175        @Override
176        public void onAnimationEnd(Animator animator) {
177            viewHolder.itemView.setBackgroundColor(mEndColor);
178            mRunningAnimations.remove(viewHolder);
179            dispatchAnimationFinished(viewHolder);
180        }
181
182        @Override
183        public void onAnimationStart(Animator animation) {
184            dispatchAnimationStarted(viewHolder);
185        }
186
187        @Override
188        public void onAnimationCancel(Animator animation) {}
189
190        @Override
191        public void onAnimationRepeat(Animator animation) {}
192    };
193};
194