PageRenderTime 46ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/core/java/android/animation/AnimationHandler.java

https://gitlab.com/amardeep434/nitro_base
Java | 316 lines | 187 code | 32 blank | 97 comment | 36 complexity | 35a52c3bd5872669f9a81d2381fc0b63 MD5 | raw file
  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. package android.animation;
  17. import android.os.SystemClock;
  18. import android.util.ArrayMap;
  19. import android.view.Choreographer;
  20. import java.util.ArrayList;
  21. /**
  22. * This custom, static handler handles the timing pulse that is shared by all active
  23. * ValueAnimators. This approach ensures that the setting of animation values will happen on the
  24. * same thread that animations start on, and that all animations will share the same times for
  25. * calculating their values, which makes synchronizing animations possible.
  26. *
  27. * The handler uses the Choreographer by default for doing periodic callbacks. A custom
  28. * AnimationFrameCallbackProvider can be set on the handler to provide timing pulse that
  29. * may be independent of UI frame update. This could be useful in testing.
  30. *
  31. * @hide
  32. */
  33. public class AnimationHandler {
  34. /**
  35. * Internal per-thread collections used to avoid set collisions as animations start and end
  36. * while being processed.
  37. * @hide
  38. */
  39. private final ArrayMap<AnimationFrameCallback, Long> mDelayedCallbackStartTime =
  40. new ArrayMap<>();
  41. private final ArrayList<AnimationFrameCallback> mAnimationCallbacks =
  42. new ArrayList<>();
  43. private final ArrayList<AnimationFrameCallback> mCommitCallbacks =
  44. new ArrayList<>();
  45. private AnimationFrameCallbackProvider mProvider;
  46. private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() {
  47. @Override
  48. public void doFrame(long frameTimeNanos) {
  49. doAnimationFrame(getProvider().getFrameTime());
  50. if (mAnimationCallbacks.size() > 0) {
  51. getProvider().postFrameCallback(this);
  52. }
  53. }
  54. };
  55. public final static ThreadLocal<AnimationHandler> sAnimatorHandler = new ThreadLocal<>();
  56. private boolean mListDirty = false;
  57. public static AnimationHandler getInstance() {
  58. if (sAnimatorHandler.get() == null) {
  59. sAnimatorHandler.set(new AnimationHandler());
  60. }
  61. return sAnimatorHandler.get();
  62. }
  63. /**
  64. * By default, the Choreographer is used to provide timing for frame callbacks. A custom
  65. * provider can be used here to provide different timing pulse.
  66. */
  67. public void setProvider(AnimationFrameCallbackProvider provider) {
  68. if (provider == null) {
  69. mProvider = new MyFrameCallbackProvider();
  70. } else {
  71. mProvider = provider;
  72. }
  73. }
  74. private AnimationFrameCallbackProvider getProvider() {
  75. if (mProvider == null) {
  76. mProvider = new MyFrameCallbackProvider();
  77. }
  78. return mProvider;
  79. }
  80. /**
  81. * Register to get a callback on the next frame after the delay.
  82. */
  83. public void addAnimationFrameCallback(final AnimationFrameCallback callback, long delay) {
  84. if (mAnimationCallbacks.size() == 0) {
  85. getProvider().postFrameCallback(mFrameCallback);
  86. }
  87. if (!mAnimationCallbacks.contains(callback)) {
  88. mAnimationCallbacks.add(callback);
  89. }
  90. if (delay > 0) {
  91. mDelayedCallbackStartTime.put(callback, (SystemClock.uptimeMillis() + delay));
  92. }
  93. }
  94. /**
  95. * Register to get a one shot callback for frame commit timing. Frame commit timing is the
  96. * time *after* traversals are done, as opposed to the animation frame timing, which is
  97. * before any traversals. This timing can be used to adjust the start time of an animation
  98. * when expensive traversals create big delta between the animation frame timing and the time
  99. * that animation is first shown on screen.
  100. *
  101. * Note this should only be called when the animation has already registered to receive
  102. * animation frame callbacks. This callback will be guaranteed to happen *after* the next
  103. * animation frame callback.
  104. */
  105. public void addOneShotCommitCallback(final AnimationFrameCallback callback) {
  106. if (!mCommitCallbacks.contains(callback)) {
  107. mCommitCallbacks.add(callback);
  108. }
  109. }
  110. /**
  111. * Removes the given callback from the list, so it will no longer be called for frame related
  112. * timing.
  113. */
  114. public void removeCallback(AnimationFrameCallback callback) {
  115. mCommitCallbacks.remove(callback);
  116. mDelayedCallbackStartTime.remove(callback);
  117. int id = mAnimationCallbacks.indexOf(callback);
  118. if (id >= 0) {
  119. mAnimationCallbacks.set(id, null);
  120. mListDirty = true;
  121. }
  122. }
  123. private void doAnimationFrame(long frameTime) {
  124. int size = mAnimationCallbacks.size();
  125. long currentTime = SystemClock.uptimeMillis();
  126. for (int i = 0; i < size; i++) {
  127. final AnimationFrameCallback callback = mAnimationCallbacks.get(i);
  128. if (callback == null) {
  129. continue;
  130. }
  131. if (isCallbackDue(callback, currentTime)) {
  132. callback.doAnimationFrame(frameTime);
  133. if (mCommitCallbacks.contains(callback)) {
  134. getProvider().postCommitCallback(new Runnable() {
  135. @Override
  136. public void run() {
  137. commitAnimationFrame(callback, getProvider().getFrameTime());
  138. }
  139. });
  140. }
  141. }
  142. }
  143. cleanUpList();
  144. }
  145. private void commitAnimationFrame(AnimationFrameCallback callback, long frameTime) {
  146. if (!mDelayedCallbackStartTime.containsKey(callback) &&
  147. mCommitCallbacks.contains(callback)) {
  148. callback.commitAnimationFrame(frameTime);
  149. mCommitCallbacks.remove(callback);
  150. }
  151. }
  152. /**
  153. * Remove the callbacks from mDelayedCallbackStartTime once they have passed the initial delay
  154. * so that they can start getting frame callbacks.
  155. *
  156. * @return true if they have passed the initial delay or have no delay, false otherwise.
  157. */
  158. private boolean isCallbackDue(AnimationFrameCallback callback, long currentTime) {
  159. Long startTime = mDelayedCallbackStartTime.get(callback);
  160. if (startTime == null) {
  161. return true;
  162. }
  163. if (startTime < currentTime) {
  164. mDelayedCallbackStartTime.remove(callback);
  165. return true;
  166. }
  167. return false;
  168. }
  169. /**
  170. * Return the number of callbacks that have registered for frame callbacks.
  171. */
  172. public static int getAnimationCount() {
  173. AnimationHandler handler = sAnimatorHandler.get();
  174. if (handler == null) {
  175. return 0;
  176. }
  177. return handler.getCallbackSize();
  178. }
  179. public static void setFrameDelay(long delay) {
  180. getInstance().getProvider().setFrameDelay(delay);
  181. }
  182. public static long getFrameDelay() {
  183. return getInstance().getProvider().getFrameDelay();
  184. }
  185. void autoCancelBasedOn(ObjectAnimator objectAnimator) {
  186. for (int i = mAnimationCallbacks.size() - 1; i >= 0; i--) {
  187. AnimationFrameCallback cb = mAnimationCallbacks.get(i);
  188. if (cb == null) {
  189. continue;
  190. }
  191. if (objectAnimator.shouldAutoCancel(cb)) {
  192. ((Animator) mAnimationCallbacks.get(i)).cancel();
  193. }
  194. }
  195. }
  196. private void cleanUpList() {
  197. if (mListDirty) {
  198. for (int i = mAnimationCallbacks.size() - 1; i >= 0; i--) {
  199. if (mAnimationCallbacks.get(i) == null) {
  200. mAnimationCallbacks.remove(i);
  201. }
  202. }
  203. mListDirty = false;
  204. }
  205. }
  206. private int getCallbackSize() {
  207. int count = 0;
  208. int size = mAnimationCallbacks.size();
  209. for (int i = size - 1; i >= 0; i--) {
  210. if (mAnimationCallbacks.get(i) != null) {
  211. count++;
  212. }
  213. }
  214. return count;
  215. }
  216. /**
  217. * Default provider of timing pulse that uses Choreographer for frame callbacks.
  218. */
  219. private class MyFrameCallbackProvider implements AnimationFrameCallbackProvider {
  220. final Choreographer mChoreographer = Choreographer.getInstance();
  221. @Override
  222. public void postFrameCallback(Choreographer.FrameCallback callback) {
  223. mChoreographer.postFrameCallback(callback);
  224. }
  225. @Override
  226. public void postCommitCallback(Runnable runnable) {
  227. mChoreographer.postCallback(Choreographer.CALLBACK_COMMIT, runnable, null);
  228. }
  229. @Override
  230. public long getFrameTime() {
  231. return mChoreographer.getFrameTime();
  232. }
  233. @Override
  234. public long getFrameDelay() {
  235. return Choreographer.getFrameDelay();
  236. }
  237. @Override
  238. public void setFrameDelay(long delay) {
  239. Choreographer.setFrameDelay(delay);
  240. }
  241. }
  242. /**
  243. * Callbacks that receives notifications for animation timing and frame commit timing.
  244. */
  245. interface AnimationFrameCallback {
  246. /**
  247. * Run animation based on the frame time.
  248. * @param frameTime The frame start time, in the {@link SystemClock#uptimeMillis()} time
  249. * base.
  250. */
  251. void doAnimationFrame(long frameTime);
  252. /**
  253. * This notifies the callback of frame commit time. Frame commit time is the time after
  254. * traversals happen, as opposed to the normal animation frame time that is before
  255. * traversals. This is used to compensate expensive traversals that happen as the
  256. * animation starts. When traversals take a long time to complete, the rendering of the
  257. * initial frame will be delayed (by a long time). But since the startTime of the
  258. * animation is set before the traversal, by the time of next frame, a lot of time would
  259. * have passed since startTime was set, the animation will consequently skip a few frames
  260. * to respect the new frameTime. By having the commit time, we can adjust the start time to
  261. * when the first frame was drawn (after any expensive traversals) so that no frames
  262. * will be skipped.
  263. *
  264. * @param frameTime The frame time after traversals happen, if any, in the
  265. * {@link SystemClock#uptimeMillis()} time base.
  266. */
  267. void commitAnimationFrame(long frameTime);
  268. }
  269. /**
  270. * The intention for having this interface is to increase the testability of ValueAnimator.
  271. * Specifically, we can have a custom implementation of the interface below and provide
  272. * timing pulse without using Choreographer. That way we could use any arbitrary interval for
  273. * our timing pulse in the tests.
  274. *
  275. * @hide
  276. */
  277. public interface AnimationFrameCallbackProvider {
  278. void postFrameCallback(Choreographer.FrameCallback callback);
  279. void postCommitCallback(Runnable runnable);
  280. long getFrameTime();
  281. long getFrameDelay();
  282. void setFrameDelay(long delay);
  283. }
  284. }