blob: 7642a649476129182a4c5997f072ecf5421fd43f [file] [log] [blame]
Annie Chind0f87d22016-10-24 09:04:12 -07001/*
2 * Copyright (C) 2016 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.calculator2;
18
Annie Chin0e88baa2016-11-28 15:23:07 -080019import android.animation.ArgbEvaluator;
Annie Chind0f87d22016-10-24 09:04:12 -070020import android.support.v7.widget.RecyclerView;
21import android.view.View;
22import android.widget.TextView;
23
24/**
25 * Contains the logic for animating the recyclerview elements on drag.
26 */
27public final class DragController {
28
Annie Chinbc001882016-11-09 19:41:21 -080029 private static final String TAG = "DragController";
30
Annie Chin0e88baa2016-11-28 15:23:07 -080031 private static final ArgbEvaluator mColorEvaluator = new ArgbEvaluator();
32
Annie Chind0f87d22016-10-24 09:04:12 -070033 // References to views from the Calculator Display.
34 private CalculatorFormula mDisplayFormula;
35 private CalculatorResult mDisplayResult;
36 private View mToolbar;
37
38 private int mFormulaTranslationY;
39 private int mFormulaTranslationX;
40 private float mFormulaScale;
Annie Chinbc001882016-11-09 19:41:21 -080041 private float mResultScale;
Annie Chind0f87d22016-10-24 09:04:12 -070042
Annie Chinfe7a4102016-12-02 16:21:04 -080043 private float mResultTranslationY;
Annie Chind0f87d22016-10-24 09:04:12 -070044 private int mResultTranslationX;
45
Annie Chinbc001882016-11-09 19:41:21 -080046 private int mDisplayHeight;
47
Annie Chin0e88baa2016-11-28 15:23:07 -080048 private int mFormulaStartColor;
49 private int mFormulaEndColor;
50
51 private int mResultStartColor;
52 private int mResultEndColor;
53
Annie Chin36147982016-12-01 15:07:34 -080054 // The padding at the bottom of the RecyclerView itself.
55 private int mBottomPaddingHeight;
56
Annie Chind0f87d22016-10-24 09:04:12 -070057 private boolean mAnimationInitialized;
58
Annie Chinbc001882016-11-09 19:41:21 -080059 private AnimationController mAnimationController;
60
61 private Evaluator mEvaluator;
62
63 public void setEvaluator(Evaluator evaluator) {
64 mEvaluator = evaluator;
Annie Chinbc001882016-11-09 19:41:21 -080065 }
66
Annie Chin94c1bd92016-11-23 13:39:56 -080067 public void initializeController(boolean isResult) {
68 if (EvaluatorStateUtils.isDisplayEmpty(mEvaluator)) {
69 // Empty display
70 mAnimationController = new EmptyAnimationController();
71 } else if (isResult) {
72 // Result
73 mAnimationController = new ResultAnimationController();
74 } else {
75 // There is something in the formula field. There may or may not be
76 // a quick result.
77 mAnimationController = new AnimationController();
78 }
Annie Chinbc001882016-11-09 19:41:21 -080079 }
80
Annie Chind0f87d22016-10-24 09:04:12 -070081 public void setDisplayFormula(CalculatorFormula formula) {
82 mDisplayFormula = formula;
83 }
84
85 public void setDisplayResult(CalculatorResult result) {
86 mDisplayResult = result;
87 }
88
89 public void setToolbar(View toolbar) {
90 mToolbar = toolbar;
91 }
92
Annie Chin0e88baa2016-11-28 15:23:07 -080093 public void animateViews(float yFraction, RecyclerView recyclerView) {
Annie Chind0f87d22016-10-24 09:04:12 -070094 final HistoryAdapter.ViewHolder vh = (HistoryAdapter.ViewHolder)
Annie Chinab657d42016-11-03 16:43:59 -070095 recyclerView.findViewHolderForAdapterPosition(0);
Annie Chinefa259a2016-11-23 14:15:15 -080096 if (yFraction > 0) {
97 recyclerView.setVisibility(View.VISIBLE);
98 }
Annie Chin91796232016-11-16 17:27:36 -080099 if (vh != null && !EvaluatorStateUtils.isDisplayEmpty(mEvaluator)) {
Annie Chin8149c8c2016-11-28 13:44:09 -0800100 final AlignedTextView formula = vh.getFormula();
Annie Chind0f87d22016-10-24 09:04:12 -0700101 final CalculatorResult result = vh.getResult();
102 final TextView date = vh.getDate();
Annie Chin36147982016-12-01 15:07:34 -0800103 final View divider = vh.getDivider();
Annie Chind0f87d22016-10-24 09:04:12 -0700104
105 if (!mAnimationInitialized) {
Annie Chin36147982016-12-01 15:07:34 -0800106 mBottomPaddingHeight = recyclerView.getPaddingBottom();
107
Annie Chinbc001882016-11-09 19:41:21 -0800108 mAnimationController.initializeScales(formula, result);
Annie Chind0f87d22016-10-24 09:04:12 -0700109
Annie Chin0e88baa2016-11-28 15:23:07 -0800110 mAnimationController.initializeColorAnimators(formula, result);
111
Annie Chinbc001882016-11-09 19:41:21 -0800112 mAnimationController.initializeFormulaTranslationX(formula);
Annie Chind0f87d22016-10-24 09:04:12 -0700113
Annie Chinbc001882016-11-09 19:41:21 -0800114 mAnimationController.initializeFormulaTranslationY(formula, result);
Annie Chind0f87d22016-10-24 09:04:12 -0700115
Annie Chinbc001882016-11-09 19:41:21 -0800116 mAnimationController.initializeResultTranslationX(result);
Annie Chind0f87d22016-10-24 09:04:12 -0700117
Annie Chinbc001882016-11-09 19:41:21 -0800118 mAnimationController.initializeResultTranslationY(result);
Annie Chind0f87d22016-10-24 09:04:12 -0700119
120 mAnimationInitialized = true;
121 }
122
123 if (mAnimationInitialized) {
Annie Chinbc001882016-11-09 19:41:21 -0800124 result.setScaleX(mAnimationController.getResultScale(yFraction));
125 result.setScaleY(mAnimationController.getResultScale(yFraction));
126
127 formula.setScaleX(mAnimationController.getFormulaScale(yFraction));
128 formula.setScaleY(mAnimationController.getFormulaScale(yFraction));
129
Annie Chind0f87d22016-10-24 09:04:12 -0700130 formula.setPivotX(formula.getWidth() - formula.getPaddingEnd());
131 formula.setPivotY(formula.getHeight() - formula.getPaddingBottom());
132
133 result.setPivotX(result.getWidth() - result.getPaddingEnd());
134 result.setPivotY(result.getHeight() - result.getPaddingBottom());
135
Annie Chinbc001882016-11-09 19:41:21 -0800136 formula.setTranslationX(mAnimationController.getFormulaTranslationX(yFraction));
137 formula.setTranslationY(mAnimationController.getFormulaTranslationY(yFraction));
Annie Chind0f87d22016-10-24 09:04:12 -0700138
Annie Chinbc001882016-11-09 19:41:21 -0800139 result.setTranslationX(mAnimationController.getResultTranslationX(yFraction));
140 result.setTranslationY(mAnimationController.getResultTranslationY(yFraction));
Annie Chind0f87d22016-10-24 09:04:12 -0700141
Annie Chin0e88baa2016-11-28 15:23:07 -0800142 formula.setTextColor((int) mColorEvaluator.evaluate(yFraction, mFormulaStartColor,
143 mFormulaEndColor));
144
145 result.setTextColor((int) mColorEvaluator.evaluate(yFraction, mResultStartColor,
146 mResultEndColor));
147
Annie Chinbc001882016-11-09 19:41:21 -0800148 date.setTranslationY(mAnimationController.getDateTranslationY(yFraction));
Annie Chin36147982016-12-01 15:07:34 -0800149 divider.setTranslationY(mAnimationController.getDateTranslationY(yFraction));
Annie Chinbc001882016-11-09 19:41:21 -0800150 }
Annie Chin91796232016-11-16 17:27:36 -0800151 } else if (EvaluatorStateUtils.isDisplayEmpty(mEvaluator)) {
Annie Chinbc001882016-11-09 19:41:21 -0800152 // There is no current expression but we still need to collect information
153 // to translate the other viewholders.
154 if (!mAnimationInitialized) {
155 mAnimationController.initializeDisplayHeight();
Annie Chind0f87d22016-10-24 09:04:12 -0700156
Annie Chinbc001882016-11-09 19:41:21 -0800157 mAnimationInitialized = true;
158 }
159 }
Annie Chind0f87d22016-10-24 09:04:12 -0700160
Annie Chinbc001882016-11-09 19:41:21 -0800161 // Move up all ViewHolders above the current expression; if there is no current expression,
162 // we're translating all the viewholders.
163 for (int i = recyclerView.getChildCount() - 1;
164 i >= mAnimationController.getFirstTranslatedViewHolderIndex();
165 --i) {
166 final RecyclerView.ViewHolder vh2 =
167 recyclerView.getChildViewHolder(recyclerView.getChildAt(i));
168 if (vh2 != null) {
169 final View view = vh2.itemView;
170 if (view != null) {
171 view.setTranslationY(
172 mAnimationController.getHistoryElementTranslationY(yFraction));
Annie Chind0f87d22016-10-24 09:04:12 -0700173 }
174 }
175 }
176 }
Annie Chinbc001882016-11-09 19:41:21 -0800177
178 /**
Annie Chinb2e96182016-11-28 13:14:54 -0800179 * Reset all initialized values.
180 * If the DragLayout is closed, set recyclerview to INVISIBLE to avoid flickering.
Annie Chinbc001882016-11-09 19:41:21 -0800181 */
Annie Chinb2e96182016-11-28 13:14:54 -0800182 public void initializeAnimation(RecyclerView recyclerView, boolean isResult, boolean isOpen) {
183 recyclerView.setVisibility(isOpen ? View.VISIBLE : View.INVISIBLE);
Annie Chinbc001882016-11-09 19:41:21 -0800184 mAnimationInitialized = false;
Annie Chin94c1bd92016-11-23 13:39:56 -0800185 initializeController(isResult);
Annie Chinbc001882016-11-09 19:41:21 -0800186 }
187
188 public interface AnimateTextInterface {
189
190 void initializeDisplayHeight();
191
Annie Chin0e88baa2016-11-28 15:23:07 -0800192 void initializeColorAnimators(AlignedTextView formula, CalculatorResult result);
193
Annie Chin8149c8c2016-11-28 13:44:09 -0800194 void initializeScales(AlignedTextView formula, CalculatorResult result);
Annie Chinbc001882016-11-09 19:41:21 -0800195
Annie Chin8149c8c2016-11-28 13:44:09 -0800196 void initializeFormulaTranslationX(AlignedTextView formula);
Annie Chinbc001882016-11-09 19:41:21 -0800197
Annie Chin8149c8c2016-11-28 13:44:09 -0800198 void initializeFormulaTranslationY(AlignedTextView formula, CalculatorResult result);
Annie Chinbc001882016-11-09 19:41:21 -0800199
200 void initializeResultTranslationX(CalculatorResult result);
201
202 void initializeResultTranslationY(CalculatorResult result);
203
204 float getResultTranslationX(float yFraction);
205
206 float getResultTranslationY(float yFraction);
207
208 float getResultScale(float yFraction);
209
210 float getFormulaScale(float yFraction);
211
212 float getFormulaTranslationX(float yFraction);
213
214 float getFormulaTranslationY(float yFraction);
215
216 float getDateTranslationY(float yFraction);
217
218 float getHistoryElementTranslationY(float yFraction);
219
220 // Return the lowest index of the first Viewholder to be translated upwards.
221 // If there is no current expression, we translate all the viewholders; otherwise,
222 // we start at index 1.
223 int getFirstTranslatedViewHolderIndex();
224 }
225
226 // The default AnimationController when Display is in INPUT state and DisplayFormula is not
227 // empty. There may or may not be a quick result.
228 public class AnimationController implements DragController.AnimateTextInterface {
229
230 public void initializeDisplayHeight() {
231 // no-op
232 }
233
Annie Chin0e88baa2016-11-28 15:23:07 -0800234 public void initializeColorAnimators(AlignedTextView formula, CalculatorResult result) {
235 mFormulaStartColor = mDisplayFormula.getCurrentTextColor();
236 mFormulaEndColor = formula.getCurrentTextColor();
237
238 mResultStartColor = mDisplayResult.getCurrentTextColor();
239 mResultEndColor = result.getCurrentTextColor();
240 }
241
Annie Chin8149c8c2016-11-28 13:44:09 -0800242 public void initializeScales(AlignedTextView formula, CalculatorResult result) {
Annie Chinbc001882016-11-09 19:41:21 -0800243 // Calculate the scale for the text
244 mFormulaScale = (mDisplayFormula.getTextSize() * 1.0f) / formula.getTextSize();
245 }
246
Annie Chin8149c8c2016-11-28 13:44:09 -0800247 public void initializeFormulaTranslationY(AlignedTextView formula,
Annie Chinbc001882016-11-09 19:41:21 -0800248 CalculatorResult result) {
249 // Baseline of formula moves by the difference in formula bottom padding and the
250 // difference in result height.
251 mFormulaTranslationY =
252 mDisplayFormula.getPaddingBottom() - formula.getPaddingBottom()
Annie Chin36147982016-12-01 15:07:34 -0800253 + mDisplayResult.getHeight() - result.getHeight()
254 - mBottomPaddingHeight;
Annie Chinbc001882016-11-09 19:41:21 -0800255
256 }
257
Annie Chin8149c8c2016-11-28 13:44:09 -0800258 public void initializeFormulaTranslationX(AlignedTextView formula) {
Annie Chinbc001882016-11-09 19:41:21 -0800259 // Right border of formula moves by the difference in formula end padding.
260 mFormulaTranslationX = mDisplayFormula.getPaddingEnd() - formula.getPaddingEnd();
261 }
262
263 public void initializeResultTranslationY(CalculatorResult result) {
264 // Baseline of result moves by the difference in result bottom padding.
Annie Chin36147982016-12-01 15:07:34 -0800265 mResultTranslationY = mDisplayResult.getPaddingBottom() - result.getPaddingBottom()
266 - mBottomPaddingHeight;
Annie Chinbc001882016-11-09 19:41:21 -0800267 }
268
269 public void initializeResultTranslationX(CalculatorResult result) {
270 mResultTranslationX = mDisplayResult.getPaddingEnd() - result.getPaddingEnd();
271 }
272
273 public float getResultTranslationX(float yFraction) {
274 return (mResultTranslationX * yFraction) - mResultTranslationX;
275 }
276
277 public float getResultTranslationY(float yFraction) {
278 return (mResultTranslationY * yFraction) - mResultTranslationY;
279 }
280
281 public float getResultScale(float yFraction) {
282 return 1;
283 }
284
285 public float getFormulaScale(float yFraction) {
286 return mFormulaScale - (mFormulaScale * yFraction) + yFraction;
287 }
288
289 public float getFormulaTranslationX(float yFraction) {
290 return (mFormulaTranslationX * yFraction) -
291 mFormulaTranslationX;
292 }
293
294 public float getFormulaTranslationY(float yFraction) {
295 // Scale linearly between -FormulaTranslationY and 0.
296 return (mFormulaTranslationY * yFraction) - mFormulaTranslationY;
297 }
298
299 public float getDateTranslationY(float yFraction) {
300 // We also want the date to start out above the visible screen with
301 // this distance decreasing as it's pulled down.
Annie Chin5a2bebb2016-12-06 14:38:49 -0800302 // Account for the scaled formula height.
Annie Chinbc001882016-11-09 19:41:21 -0800303 return -mToolbar.getHeight() * (1 - yFraction)
Annie Chin5a2bebb2016-12-06 14:38:49 -0800304 + getFormulaTranslationY(yFraction)
305 - mDisplayFormula.getHeight() /getFormulaScale(yFraction) * (1 - yFraction);
Annie Chinbc001882016-11-09 19:41:21 -0800306 }
307
308 public float getHistoryElementTranslationY(float yFraction) {
309 return getDateTranslationY(yFraction);
310 }
311
312 public int getFirstTranslatedViewHolderIndex() {
313 return 1;
314 }
315 }
316
317 // The default AnimationController when Display is in RESULT state.
318 public class ResultAnimationController extends AnimationController
319 implements DragController.AnimateTextInterface {
320 @Override
Annie Chin8149c8c2016-11-28 13:44:09 -0800321 public void initializeScales(AlignedTextView formula, CalculatorResult result) {
Annie Chinbc001882016-11-09 19:41:21 -0800322 final float textSize = mDisplayResult.getTextSize() * mDisplayResult.getScaleX();
323 mResultScale = textSize / result.getTextSize();
324
325 mFormulaScale = 1;
326 }
327
328 @Override
Annie Chin8149c8c2016-11-28 13:44:09 -0800329 public void initializeFormulaTranslationY(AlignedTextView formula,
Annie Chinbc001882016-11-09 19:41:21 -0800330 CalculatorResult result) {
331 // Baseline of formula moves by the difference in formula bottom padding and the
332 // difference in the result height.
333 mFormulaTranslationY = mDisplayFormula.getPaddingBottom() - formula.getPaddingBottom()
Annie Chin36147982016-12-01 15:07:34 -0800334 + mDisplayResult.getHeight() - result.getHeight()
335 - mBottomPaddingHeight;
Annie Chinbc001882016-11-09 19:41:21 -0800336 }
337
338 @Override
Annie Chin8149c8c2016-11-28 13:44:09 -0800339 public void initializeFormulaTranslationX(AlignedTextView formula) {
Annie Chinbc001882016-11-09 19:41:21 -0800340 // Right border of formula moves by the difference in formula end padding.
341 mFormulaTranslationX = mDisplayFormula.getPaddingEnd() - formula.getPaddingEnd();
342 }
343
344 @Override
345 public void initializeResultTranslationY(CalculatorResult result) {
346 // Baseline of result moves by the difference in result bottom padding.
Annie Chinfe7a4102016-12-02 16:21:04 -0800347 mResultTranslationY = mDisplayResult.getPaddingBottom() - result.getPaddingBottom()
348 - mDisplayResult.getTranslationY()
349 - mBottomPaddingHeight;
Annie Chinbc001882016-11-09 19:41:21 -0800350 }
351
352 @Override
353 public void initializeResultTranslationX(CalculatorResult result) {
354 mResultTranslationX = mDisplayResult.getPaddingEnd() - result.getPaddingEnd();
355 }
356
357 @Override
358 public float getResultTranslationX(float yFraction) {
359 return (mResultTranslationX * yFraction) - mResultTranslationX;
360 }
361
362 @Override
363 public float getResultTranslationY(float yFraction) {
364 return (mResultTranslationY * yFraction) - mResultTranslationY;
365 }
366
367 @Override
368 public float getFormulaTranslationX(float yFraction) {
369 return (mFormulaTranslationX * yFraction) -
370 mFormulaTranslationX;
371 }
372
373 @Override
374 public float getFormulaTranslationY(float yFraction) {
375 return getDateTranslationY(yFraction);
376 }
377
378 @Override
379 public float getResultScale(float yFraction) {
380 return mResultScale - (mResultScale * yFraction) + yFraction;
381 }
382
383 @Override
384 public float getFormulaScale(float yFraction) {
385 return 1;
386 }
387
388 @Override
389 public float getDateTranslationY(float yFraction) {
390 // We also want the date to start out above the visible screen with
391 // this distance decreasing as it's pulled down.
392 return -mToolbar.getHeight() * (1 - yFraction)
393 + (mResultTranslationY * yFraction) - mResultTranslationY
394 - mDisplayFormula.getPaddingTop() +
395 (mDisplayFormula.getPaddingTop() * yFraction);
396 }
397
398 @Override
399 public int getFirstTranslatedViewHolderIndex() {
400 return 1;
401 }
402 }
403
404 // The default AnimationController when Display is completely empty.
405 public class EmptyAnimationController extends AnimationController
406 implements DragController.AnimateTextInterface {
407 @Override
408 public void initializeDisplayHeight() {
409 mDisplayHeight = mToolbar.getHeight() + mDisplayResult.getHeight()
410 + mDisplayFormula.getHeight();
411 }
412
413 @Override
Annie Chin8149c8c2016-11-28 13:44:09 -0800414 public void initializeScales(AlignedTextView formula, CalculatorResult result) {
Annie Chinbc001882016-11-09 19:41:21 -0800415 // no-op
416 }
417
418 @Override
Annie Chin8149c8c2016-11-28 13:44:09 -0800419 public void initializeFormulaTranslationY(AlignedTextView formula,
Annie Chinbc001882016-11-09 19:41:21 -0800420 CalculatorResult result) {
421 // no-op
422 }
423
424 @Override
Annie Chin8149c8c2016-11-28 13:44:09 -0800425 public void initializeFormulaTranslationX(AlignedTextView formula) {
Annie Chinbc001882016-11-09 19:41:21 -0800426 // no-op
427 }
428
429 @Override
430 public void initializeResultTranslationY(CalculatorResult result) {
431 // no-op
432 }
433
434 @Override
435 public void initializeResultTranslationX(CalculatorResult result) {
436 // no-op
437 }
438
439 @Override
440 public float getResultTranslationX(float yFraction) {
441 return 0;
442 }
443
444 @Override
445 public float getResultTranslationY(float yFraction) {
446 return 0;
447 }
448
449 @Override
450 public float getFormulaScale(float yFraction) {
451 return 1;
452 }
453
454 @Override
455 public float getDateTranslationY(float yFraction) {
456 return 0;
457 }
458
459 @Override
460 public float getHistoryElementTranslationY(float yFraction) {
Annie Chin36147982016-12-01 15:07:34 -0800461 return -mDisplayHeight * (1 - yFraction) - mBottomPaddingHeight;
Annie Chinbc001882016-11-09 19:41:21 -0800462 }
463
464 @Override
465 public int getFirstTranslatedViewHolderIndex() {
466 return 0;
467 }
468 }
Annie Chind0f87d22016-10-24 09:04:12 -0700469}