blob: e1954638ca06a57316532a54fc1b1f13ad29e7f3 [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
19import android.support.v7.widget.RecyclerView;
20import android.view.View;
21import android.widget.TextView;
22
23/**
24 * Contains the logic for animating the recyclerview elements on drag.
25 */
26public final class DragController {
27
Annie Chinbc001882016-11-09 19:41:21 -080028 private static final String TAG = "DragController";
29
Annie Chind0f87d22016-10-24 09:04:12 -070030 // References to views from the Calculator Display.
31 private CalculatorFormula mDisplayFormula;
32 private CalculatorResult mDisplayResult;
33 private View mToolbar;
34
35 private int mFormulaTranslationY;
36 private int mFormulaTranslationX;
37 private float mFormulaScale;
Annie Chinbc001882016-11-09 19:41:21 -080038 private float mResultScale;
Annie Chind0f87d22016-10-24 09:04:12 -070039
40 private int mResultTranslationY;
41 private int mResultTranslationX;
42
Annie Chinbc001882016-11-09 19:41:21 -080043 private int mDisplayHeight;
44
Annie Chind0f87d22016-10-24 09:04:12 -070045 private boolean mAnimationInitialized;
46
Annie Chinbc001882016-11-09 19:41:21 -080047 private AnimationController mAnimationController;
48
49 private Evaluator mEvaluator;
50
51 public void setEvaluator(Evaluator evaluator) {
52 mEvaluator = evaluator;
Annie Chinbc001882016-11-09 19:41:21 -080053 }
54
Annie Chin94c1bd92016-11-23 13:39:56 -080055 public void initializeController(boolean isResult) {
56 if (EvaluatorStateUtils.isDisplayEmpty(mEvaluator)) {
57 // Empty display
58 mAnimationController = new EmptyAnimationController();
59 } else if (isResult) {
60 // Result
61 mAnimationController = new ResultAnimationController();
62 } else {
63 // There is something in the formula field. There may or may not be
64 // a quick result.
65 mAnimationController = new AnimationController();
66 }
Annie Chinbc001882016-11-09 19:41:21 -080067 }
68
Annie Chind0f87d22016-10-24 09:04:12 -070069 public void setDisplayFormula(CalculatorFormula formula) {
70 mDisplayFormula = formula;
71 }
72
73 public void setDisplayResult(CalculatorResult result) {
74 mDisplayResult = result;
75 }
76
77 public void setToolbar(View toolbar) {
78 mToolbar = toolbar;
79 }
80
81 public void animateViews(float yFraction, RecyclerView recyclerView, int itemCount) {
82 final HistoryAdapter.ViewHolder vh = (HistoryAdapter.ViewHolder)
Annie Chinab657d42016-11-03 16:43:59 -070083 recyclerView.findViewHolderForAdapterPosition(0);
Annie Chinefa259a2016-11-23 14:15:15 -080084 if (yFraction > 0) {
85 recyclerView.setVisibility(View.VISIBLE);
86 }
Annie Chin91796232016-11-16 17:27:36 -080087 if (vh != null && !EvaluatorStateUtils.isDisplayEmpty(mEvaluator)) {
Annie Chin8149c8c2016-11-28 13:44:09 -080088 final AlignedTextView formula = vh.getFormula();
Annie Chind0f87d22016-10-24 09:04:12 -070089 final CalculatorResult result = vh.getResult();
90 final TextView date = vh.getDate();
91
92 if (!mAnimationInitialized) {
Annie Chinbc001882016-11-09 19:41:21 -080093 mAnimationController.initializeScales(formula, result);
Annie Chind0f87d22016-10-24 09:04:12 -070094
Annie Chinbc001882016-11-09 19:41:21 -080095 mAnimationController.initializeFormulaTranslationX(formula);
Annie Chind0f87d22016-10-24 09:04:12 -070096
Annie Chinbc001882016-11-09 19:41:21 -080097 mAnimationController.initializeFormulaTranslationY(formula, result);
Annie Chind0f87d22016-10-24 09:04:12 -070098
Annie Chinbc001882016-11-09 19:41:21 -080099 mAnimationController.initializeResultTranslationX(result);
Annie Chind0f87d22016-10-24 09:04:12 -0700100
Annie Chinbc001882016-11-09 19:41:21 -0800101 mAnimationController.initializeResultTranslationY(result);
Annie Chind0f87d22016-10-24 09:04:12 -0700102
103 mAnimationInitialized = true;
104 }
105
106 if (mAnimationInitialized) {
Annie Chinbc001882016-11-09 19:41:21 -0800107 result.setScaleX(mAnimationController.getResultScale(yFraction));
108 result.setScaleY(mAnimationController.getResultScale(yFraction));
109
110 formula.setScaleX(mAnimationController.getFormulaScale(yFraction));
111 formula.setScaleY(mAnimationController.getFormulaScale(yFraction));
112
Annie Chind0f87d22016-10-24 09:04:12 -0700113 formula.setPivotX(formula.getWidth() - formula.getPaddingEnd());
114 formula.setPivotY(formula.getHeight() - formula.getPaddingBottom());
115
116 result.setPivotX(result.getWidth() - result.getPaddingEnd());
117 result.setPivotY(result.getHeight() - result.getPaddingBottom());
118
Annie Chinbc001882016-11-09 19:41:21 -0800119 formula.setTranslationX(mAnimationController.getFormulaTranslationX(yFraction));
120 formula.setTranslationY(mAnimationController.getFormulaTranslationY(yFraction));
Annie Chind0f87d22016-10-24 09:04:12 -0700121
Annie Chinbc001882016-11-09 19:41:21 -0800122 result.setTranslationX(mAnimationController.getResultTranslationX(yFraction));
123 result.setTranslationY(mAnimationController.getResultTranslationY(yFraction));
Annie Chind0f87d22016-10-24 09:04:12 -0700124
Annie Chinbc001882016-11-09 19:41:21 -0800125 date.setTranslationY(mAnimationController.getDateTranslationY(yFraction));
126 }
Annie Chin91796232016-11-16 17:27:36 -0800127 } else if (EvaluatorStateUtils.isDisplayEmpty(mEvaluator)) {
Annie Chinbc001882016-11-09 19:41:21 -0800128 // There is no current expression but we still need to collect information
129 // to translate the other viewholders.
130 if (!mAnimationInitialized) {
131 mAnimationController.initializeDisplayHeight();
Annie Chind0f87d22016-10-24 09:04:12 -0700132
Annie Chinbc001882016-11-09 19:41:21 -0800133 mAnimationInitialized = true;
134 }
135 }
Annie Chind0f87d22016-10-24 09:04:12 -0700136
Annie Chinbc001882016-11-09 19:41:21 -0800137 // Move up all ViewHolders above the current expression; if there is no current expression,
138 // we're translating all the viewholders.
139 for (int i = recyclerView.getChildCount() - 1;
140 i >= mAnimationController.getFirstTranslatedViewHolderIndex();
141 --i) {
142 final RecyclerView.ViewHolder vh2 =
143 recyclerView.getChildViewHolder(recyclerView.getChildAt(i));
144 if (vh2 != null) {
145 final View view = vh2.itemView;
146 if (view != null) {
147 view.setTranslationY(
148 mAnimationController.getHistoryElementTranslationY(yFraction));
Annie Chind0f87d22016-10-24 09:04:12 -0700149 }
150 }
151 }
152 }
Annie Chinbc001882016-11-09 19:41:21 -0800153
154 /**
Annie Chinb2e96182016-11-28 13:14:54 -0800155 * Reset all initialized values.
156 * If the DragLayout is closed, set recyclerview to INVISIBLE to avoid flickering.
Annie Chinbc001882016-11-09 19:41:21 -0800157 */
Annie Chinb2e96182016-11-28 13:14:54 -0800158 public void initializeAnimation(RecyclerView recyclerView, boolean isResult, boolean isOpen) {
159 recyclerView.setVisibility(isOpen ? View.VISIBLE : View.INVISIBLE);
Annie Chinbc001882016-11-09 19:41:21 -0800160 mAnimationInitialized = false;
Annie Chin94c1bd92016-11-23 13:39:56 -0800161 initializeController(isResult);
Annie Chinbc001882016-11-09 19:41:21 -0800162 }
163
164 public interface AnimateTextInterface {
165
166 void initializeDisplayHeight();
167
Annie Chin8149c8c2016-11-28 13:44:09 -0800168 void initializeScales(AlignedTextView formula, CalculatorResult result);
Annie Chinbc001882016-11-09 19:41:21 -0800169
Annie Chin8149c8c2016-11-28 13:44:09 -0800170 void initializeFormulaTranslationX(AlignedTextView formula);
Annie Chinbc001882016-11-09 19:41:21 -0800171
Annie Chin8149c8c2016-11-28 13:44:09 -0800172 void initializeFormulaTranslationY(AlignedTextView formula, CalculatorResult result);
Annie Chinbc001882016-11-09 19:41:21 -0800173
174 void initializeResultTranslationX(CalculatorResult result);
175
176 void initializeResultTranslationY(CalculatorResult result);
177
178 float getResultTranslationX(float yFraction);
179
180 float getResultTranslationY(float yFraction);
181
182 float getResultScale(float yFraction);
183
184 float getFormulaScale(float yFraction);
185
186 float getFormulaTranslationX(float yFraction);
187
188 float getFormulaTranslationY(float yFraction);
189
190 float getDateTranslationY(float yFraction);
191
192 float getHistoryElementTranslationY(float yFraction);
193
194 // Return the lowest index of the first Viewholder to be translated upwards.
195 // If there is no current expression, we translate all the viewholders; otherwise,
196 // we start at index 1.
197 int getFirstTranslatedViewHolderIndex();
198 }
199
200 // The default AnimationController when Display is in INPUT state and DisplayFormula is not
201 // empty. There may or may not be a quick result.
202 public class AnimationController implements DragController.AnimateTextInterface {
203
204 public void initializeDisplayHeight() {
205 // no-op
206 }
207
Annie Chin8149c8c2016-11-28 13:44:09 -0800208 public void initializeScales(AlignedTextView formula, CalculatorResult result) {
Annie Chinbc001882016-11-09 19:41:21 -0800209 // Calculate the scale for the text
210 mFormulaScale = (mDisplayFormula.getTextSize() * 1.0f) / formula.getTextSize();
211 }
212
Annie Chin8149c8c2016-11-28 13:44:09 -0800213 public void initializeFormulaTranslationY(AlignedTextView formula,
Annie Chinbc001882016-11-09 19:41:21 -0800214 CalculatorResult result) {
215 // Baseline of formula moves by the difference in formula bottom padding and the
216 // difference in result height.
217 mFormulaTranslationY =
218 mDisplayFormula.getPaddingBottom() - formula.getPaddingBottom()
219 + mDisplayResult.getHeight() - result.getHeight();
220
221 }
222
Annie Chin8149c8c2016-11-28 13:44:09 -0800223 public void initializeFormulaTranslationX(AlignedTextView formula) {
Annie Chinbc001882016-11-09 19:41:21 -0800224 // Right border of formula moves by the difference in formula end padding.
225 mFormulaTranslationX = mDisplayFormula.getPaddingEnd() - formula.getPaddingEnd();
226 }
227
228 public void initializeResultTranslationY(CalculatorResult result) {
229 // Baseline of result moves by the difference in result bottom padding.
230 mResultTranslationY = mDisplayResult.getPaddingBottom() - result.getPaddingBottom();
231 }
232
233 public void initializeResultTranslationX(CalculatorResult result) {
234 mResultTranslationX = mDisplayResult.getPaddingEnd() - result.getPaddingEnd();
235 }
236
237 public float getResultTranslationX(float yFraction) {
238 return (mResultTranslationX * yFraction) - mResultTranslationX;
239 }
240
241 public float getResultTranslationY(float yFraction) {
242 return (mResultTranslationY * yFraction) - mResultTranslationY;
243 }
244
245 public float getResultScale(float yFraction) {
246 return 1;
247 }
248
249 public float getFormulaScale(float yFraction) {
250 return mFormulaScale - (mFormulaScale * yFraction) + yFraction;
251 }
252
253 public float getFormulaTranslationX(float yFraction) {
254 return (mFormulaTranslationX * yFraction) -
255 mFormulaTranslationX;
256 }
257
258 public float getFormulaTranslationY(float yFraction) {
259 // Scale linearly between -FormulaTranslationY and 0.
260 return (mFormulaTranslationY * yFraction) - mFormulaTranslationY;
261 }
262
263 public float getDateTranslationY(float yFraction) {
264 // We also want the date to start out above the visible screen with
265 // this distance decreasing as it's pulled down.
266 return -mToolbar.getHeight() * (1 - yFraction)
267 + getResultTranslationY(yFraction)
268 - mDisplayFormula.getPaddingTop() +
269 (mDisplayFormula.getPaddingTop() * yFraction);
270 }
271
272 public float getHistoryElementTranslationY(float yFraction) {
273 return getDateTranslationY(yFraction);
274 }
275
276 public int getFirstTranslatedViewHolderIndex() {
277 return 1;
278 }
279 }
280
281 // The default AnimationController when Display is in RESULT state.
282 public class ResultAnimationController extends AnimationController
283 implements DragController.AnimateTextInterface {
284 @Override
Annie Chin8149c8c2016-11-28 13:44:09 -0800285 public void initializeScales(AlignedTextView formula, CalculatorResult result) {
Annie Chinbc001882016-11-09 19:41:21 -0800286 final float textSize = mDisplayResult.getTextSize() * mDisplayResult.getScaleX();
287 mResultScale = textSize / result.getTextSize();
288
289 mFormulaScale = 1;
290 }
291
292 @Override
Annie Chin8149c8c2016-11-28 13:44:09 -0800293 public void initializeFormulaTranslationY(AlignedTextView formula,
Annie Chinbc001882016-11-09 19:41:21 -0800294 CalculatorResult result) {
295 // Baseline of formula moves by the difference in formula bottom padding and the
296 // difference in the result height.
297 mFormulaTranslationY = mDisplayFormula.getPaddingBottom() - formula.getPaddingBottom()
298 + mDisplayResult.getHeight() - result.getHeight();
299 }
300
301 @Override
Annie Chin8149c8c2016-11-28 13:44:09 -0800302 public void initializeFormulaTranslationX(AlignedTextView formula) {
Annie Chinbc001882016-11-09 19:41:21 -0800303 // Right border of formula moves by the difference in formula end padding.
304 mFormulaTranslationX = mDisplayFormula.getPaddingEnd() - formula.getPaddingEnd();
305 }
306
307 @Override
308 public void initializeResultTranslationY(CalculatorResult result) {
309 // Baseline of result moves by the difference in result bottom padding.
310 mResultTranslationY = mDisplayResult.getBottom() - result.getBottom() +
311 mDisplayResult.getPaddingBottom() - result.getPaddingBottom();
312 }
313
314 @Override
315 public void initializeResultTranslationX(CalculatorResult result) {
316 mResultTranslationX = mDisplayResult.getPaddingEnd() - result.getPaddingEnd();
317 }
318
319 @Override
320 public float getResultTranslationX(float yFraction) {
321 return (mResultTranslationX * yFraction) - mResultTranslationX;
322 }
323
324 @Override
325 public float getResultTranslationY(float yFraction) {
326 return (mResultTranslationY * yFraction) - mResultTranslationY;
327 }
328
329 @Override
330 public float getFormulaTranslationX(float yFraction) {
331 return (mFormulaTranslationX * yFraction) -
332 mFormulaTranslationX;
333 }
334
335 @Override
336 public float getFormulaTranslationY(float yFraction) {
337 return getDateTranslationY(yFraction);
338 }
339
340 @Override
341 public float getResultScale(float yFraction) {
342 return mResultScale - (mResultScale * yFraction) + yFraction;
343 }
344
345 @Override
346 public float getFormulaScale(float yFraction) {
347 return 1;
348 }
349
350 @Override
351 public float getDateTranslationY(float yFraction) {
352 // We also want the date to start out above the visible screen with
353 // this distance decreasing as it's pulled down.
354 return -mToolbar.getHeight() * (1 - yFraction)
355 + (mResultTranslationY * yFraction) - mResultTranslationY
356 - mDisplayFormula.getPaddingTop() +
357 (mDisplayFormula.getPaddingTop() * yFraction);
358 }
359
360 @Override
361 public int getFirstTranslatedViewHolderIndex() {
362 return 1;
363 }
364 }
365
366 // The default AnimationController when Display is completely empty.
367 public class EmptyAnimationController extends AnimationController
368 implements DragController.AnimateTextInterface {
369 @Override
370 public void initializeDisplayHeight() {
371 mDisplayHeight = mToolbar.getHeight() + mDisplayResult.getHeight()
372 + mDisplayFormula.getHeight();
373 }
374
375 @Override
Annie Chin8149c8c2016-11-28 13:44:09 -0800376 public void initializeScales(AlignedTextView formula, CalculatorResult result) {
Annie Chinbc001882016-11-09 19:41:21 -0800377 // no-op
378 }
379
380 @Override
Annie Chin8149c8c2016-11-28 13:44:09 -0800381 public void initializeFormulaTranslationY(AlignedTextView formula,
Annie Chinbc001882016-11-09 19:41:21 -0800382 CalculatorResult result) {
383 // no-op
384 }
385
386 @Override
Annie Chin8149c8c2016-11-28 13:44:09 -0800387 public void initializeFormulaTranslationX(AlignedTextView formula) {
Annie Chinbc001882016-11-09 19:41:21 -0800388 // no-op
389 }
390
391 @Override
392 public void initializeResultTranslationY(CalculatorResult result) {
393 // no-op
394 }
395
396 @Override
397 public void initializeResultTranslationX(CalculatorResult result) {
398 // no-op
399 }
400
401 @Override
402 public float getResultTranslationX(float yFraction) {
403 return 0;
404 }
405
406 @Override
407 public float getResultTranslationY(float yFraction) {
408 return 0;
409 }
410
411 @Override
412 public float getFormulaScale(float yFraction) {
413 return 1;
414 }
415
416 @Override
417 public float getDateTranslationY(float yFraction) {
418 return 0;
419 }
420
421 @Override
422 public float getHistoryElementTranslationY(float yFraction) {
423 return -mDisplayHeight * (1 - yFraction);
424 }
425
426 @Override
427 public int getFirstTranslatedViewHolderIndex() {
428 return 0;
429 }
430 }
Annie Chind0f87d22016-10-24 09:04:12 -0700431}