1/*
2 * Copyright (C) 2014 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#include "AnimatorManager.h"
17
18#include <algorithm>
19
20#include "AnimationContext.h"
21#include "RenderNode.h"
22
23namespace android {
24namespace uirenderer {
25
26using namespace std;
27
28static void unref(BaseRenderNodeAnimator* animator) {
29    animator->detach();
30    animator->decStrong(0);
31}
32
33AnimatorManager::AnimatorManager(RenderNode& parent)
34        : mParent(parent)
35        , mAnimationHandle(NULL) {
36}
37
38AnimatorManager::~AnimatorManager() {
39    for_each(mNewAnimators.begin(), mNewAnimators.end(), unref);
40    for_each(mAnimators.begin(), mAnimators.end(), unref);
41}
42
43void AnimatorManager::addAnimator(const sp<BaseRenderNodeAnimator>& animator) {
44    animator->incStrong(0);
45    animator->attach(&mParent);
46    mNewAnimators.push_back(animator.get());
47}
48
49void AnimatorManager::setAnimationHandle(AnimationHandle* handle) {
50    LOG_ALWAYS_FATAL_IF(mAnimationHandle && handle, "Already have an AnimationHandle!");
51    mAnimationHandle = handle;
52    LOG_ALWAYS_FATAL_IF(!mAnimationHandle && mAnimators.size(),
53            "Lost animation handle on %p (%s) with outstanding animators!",
54            &mParent, mParent.getName());
55}
56
57template<typename T>
58static void move_all(T& source, T& dest) {
59    dest.reserve(source.size() + dest.size());
60    for (typename T::iterator it = source.begin(); it != source.end(); it++) {
61        dest.push_back(*it);
62    }
63    source.clear();
64}
65
66void AnimatorManager::pushStaging() {
67    if (mNewAnimators.size()) {
68        LOG_ALWAYS_FATAL_IF(!mAnimationHandle,
69                "Trying to start new animators on %p (%s) without an animation handle!",
70                &mParent, mParent.getName());
71        // Since this is a straight move, we don't need to inc/dec the ref count
72        move_all(mNewAnimators, mAnimators);
73    }
74    for (vector<BaseRenderNodeAnimator*>::iterator it = mAnimators.begin(); it != mAnimators.end(); it++) {
75        (*it)->pushStaging(mAnimationHandle->context());
76    }
77}
78
79class AnimateFunctor {
80public:
81    AnimateFunctor(TreeInfo& info, AnimationContext& context)
82            : dirtyMask(0), mInfo(info), mContext(context) {}
83
84    bool operator() (BaseRenderNodeAnimator* animator) {
85        dirtyMask |= animator->dirtyMask();
86        bool remove = animator->animate(mContext);
87        if (remove) {
88            animator->decStrong(0);
89        } else {
90            if (animator->isRunning()) {
91                mInfo.out.hasAnimations = true;
92            }
93            if (CC_UNLIKELY(!animator->mayRunAsync())) {
94                mInfo.out.requiresUiRedraw = true;
95            }
96        }
97        return remove;
98    }
99
100    uint32_t dirtyMask;
101
102private:
103    TreeInfo& mInfo;
104    AnimationContext& mContext;
105};
106
107uint32_t AnimatorManager::animate(TreeInfo& info) {
108    if (!mAnimators.size()) return 0;
109
110    // TODO: Can we target this better? For now treat it like any other staging
111    // property push and just damage self before and after animators are run
112
113    mParent.damageSelf(info);
114    info.damageAccumulator->popTransform();
115
116    uint32_t dirty = animateCommon(info);
117
118    mParent.mProperties.updateMatrix();
119    info.damageAccumulator->pushTransform(&mParent);
120    mParent.damageSelf(info);
121
122    return dirty;
123}
124
125void AnimatorManager::animateNoDamage(TreeInfo& info) {
126    if (!mAnimators.size()) return;
127
128    animateCommon(info);
129}
130
131uint32_t AnimatorManager::animateCommon(TreeInfo& info) {
132    AnimateFunctor functor(info, mAnimationHandle->context());
133    std::vector< BaseRenderNodeAnimator* >::iterator newEnd;
134    newEnd = std::remove_if(mAnimators.begin(), mAnimators.end(), functor);
135    mAnimators.erase(newEnd, mAnimators.end());
136    mAnimationHandle->notifyAnimationsRan();
137    return functor.dirtyMask;
138}
139
140static void endStagingAnimator(BaseRenderNodeAnimator* animator) {
141    animator->end();
142    if (animator->listener()) {
143        animator->listener()->onAnimationFinished(animator);
144    }
145    animator->decStrong(0);
146}
147
148void AnimatorManager::endAllStagingAnimators() {
149    ALOGD("endAllStagingAnimators on %p (%s)", &mParent, mParent.getName());
150    // This works because this state can only happen on the UI thread,
151    // which means we're already on the right thread to invoke listeners
152    for_each(mNewAnimators.begin(), mNewAnimators.end(), endStagingAnimator);
153    mNewAnimators.clear();
154}
155
156class EndActiveAnimatorsFunctor {
157public:
158    EndActiveAnimatorsFunctor(AnimationContext& context) : mContext(context) {}
159
160    void operator() (BaseRenderNodeAnimator* animator) {
161        animator->forceEndNow(mContext);
162        animator->decStrong(0);
163    }
164
165private:
166    AnimationContext& mContext;
167};
168
169void AnimatorManager::endAllActiveAnimators() {
170    ALOGD("endAllStagingAnimators on %p (%s) with handle %p",
171            &mParent, mParent.getName(), mAnimationHandle);
172    EndActiveAnimatorsFunctor functor(mAnimationHandle->context());
173    for_each(mAnimators.begin(), mAnimators.end(), functor);
174    mAnimators.clear();
175    mAnimationHandle->release();
176}
177
178} /* namespace uirenderer */
179} /* namespace android */
180