blob: e1954638ca06a57316532a54fc1b1f13ad29e7f3 [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.calculator2;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.TextView;
/**
* Contains the logic for animating the recyclerview elements on drag.
*/
public final class DragController {
private static final String TAG = "DragController";
// References to views from the Calculator Display.
private CalculatorFormula mDisplayFormula;
private CalculatorResult mDisplayResult;
private View mToolbar;
private int mFormulaTranslationY;
private int mFormulaTranslationX;
private float mFormulaScale;
private float mResultScale;
private int mResultTranslationY;
private int mResultTranslationX;
private int mDisplayHeight;
private boolean mAnimationInitialized;
private AnimationController mAnimationController;
private Evaluator mEvaluator;
public void setEvaluator(Evaluator evaluator) {
mEvaluator = evaluator;
}
public void initializeController(boolean isResult) {
if (EvaluatorStateUtils.isDisplayEmpty(mEvaluator)) {
// Empty display
mAnimationController = new EmptyAnimationController();
} else if (isResult) {
// Result
mAnimationController = new ResultAnimationController();
} else {
// There is something in the formula field. There may or may not be
// a quick result.
mAnimationController = new AnimationController();
}
}
public void setDisplayFormula(CalculatorFormula formula) {
mDisplayFormula = formula;
}
public void setDisplayResult(CalculatorResult result) {
mDisplayResult = result;
}
public void setToolbar(View toolbar) {
mToolbar = toolbar;
}
public void animateViews(float yFraction, RecyclerView recyclerView, int itemCount) {
final HistoryAdapter.ViewHolder vh = (HistoryAdapter.ViewHolder)
recyclerView.findViewHolderForAdapterPosition(0);
if (yFraction > 0) {
recyclerView.setVisibility(View.VISIBLE);
}
if (vh != null && !EvaluatorStateUtils.isDisplayEmpty(mEvaluator)) {
final AlignedTextView formula = vh.getFormula();
final CalculatorResult result = vh.getResult();
final TextView date = vh.getDate();
if (!mAnimationInitialized) {
mAnimationController.initializeScales(formula, result);
mAnimationController.initializeFormulaTranslationX(formula);
mAnimationController.initializeFormulaTranslationY(formula, result);
mAnimationController.initializeResultTranslationX(result);
mAnimationController.initializeResultTranslationY(result);
mAnimationInitialized = true;
}
if (mAnimationInitialized) {
result.setScaleX(mAnimationController.getResultScale(yFraction));
result.setScaleY(mAnimationController.getResultScale(yFraction));
formula.setScaleX(mAnimationController.getFormulaScale(yFraction));
formula.setScaleY(mAnimationController.getFormulaScale(yFraction));
formula.setPivotX(formula.getWidth() - formula.getPaddingEnd());
formula.setPivotY(formula.getHeight() - formula.getPaddingBottom());
result.setPivotX(result.getWidth() - result.getPaddingEnd());
result.setPivotY(result.getHeight() - result.getPaddingBottom());
formula.setTranslationX(mAnimationController.getFormulaTranslationX(yFraction));
formula.setTranslationY(mAnimationController.getFormulaTranslationY(yFraction));
result.setTranslationX(mAnimationController.getResultTranslationX(yFraction));
result.setTranslationY(mAnimationController.getResultTranslationY(yFraction));
date.setTranslationY(mAnimationController.getDateTranslationY(yFraction));
}
} else if (EvaluatorStateUtils.isDisplayEmpty(mEvaluator)) {
// There is no current expression but we still need to collect information
// to translate the other viewholders.
if (!mAnimationInitialized) {
mAnimationController.initializeDisplayHeight();
mAnimationInitialized = true;
}
}
// Move up all ViewHolders above the current expression; if there is no current expression,
// we're translating all the viewholders.
for (int i = recyclerView.getChildCount() - 1;
i >= mAnimationController.getFirstTranslatedViewHolderIndex();
--i) {
final RecyclerView.ViewHolder vh2 =
recyclerView.getChildViewHolder(recyclerView.getChildAt(i));
if (vh2 != null) {
final View view = vh2.itemView;
if (view != null) {
view.setTranslationY(
mAnimationController.getHistoryElementTranslationY(yFraction));
}
}
}
}
/**
* Reset all initialized values.
* If the DragLayout is closed, set recyclerview to INVISIBLE to avoid flickering.
*/
public void initializeAnimation(RecyclerView recyclerView, boolean isResult, boolean isOpen) {
recyclerView.setVisibility(isOpen ? View.VISIBLE : View.INVISIBLE);
mAnimationInitialized = false;
initializeController(isResult);
}
public interface AnimateTextInterface {
void initializeDisplayHeight();
void initializeScales(AlignedTextView formula, CalculatorResult result);
void initializeFormulaTranslationX(AlignedTextView formula);
void initializeFormulaTranslationY(AlignedTextView formula, CalculatorResult result);
void initializeResultTranslationX(CalculatorResult result);
void initializeResultTranslationY(CalculatorResult result);
float getResultTranslationX(float yFraction);
float getResultTranslationY(float yFraction);
float getResultScale(float yFraction);
float getFormulaScale(float yFraction);
float getFormulaTranslationX(float yFraction);
float getFormulaTranslationY(float yFraction);
float getDateTranslationY(float yFraction);
float getHistoryElementTranslationY(float yFraction);
// Return the lowest index of the first Viewholder to be translated upwards.
// If there is no current expression, we translate all the viewholders; otherwise,
// we start at index 1.
int getFirstTranslatedViewHolderIndex();
}
// The default AnimationController when Display is in INPUT state and DisplayFormula is not
// empty. There may or may not be a quick result.
public class AnimationController implements DragController.AnimateTextInterface {
public void initializeDisplayHeight() {
// no-op
}
public void initializeScales(AlignedTextView formula, CalculatorResult result) {
// Calculate the scale for the text
mFormulaScale = (mDisplayFormula.getTextSize() * 1.0f) / formula.getTextSize();
}
public void initializeFormulaTranslationY(AlignedTextView formula,
CalculatorResult result) {
// Baseline of formula moves by the difference in formula bottom padding and the
// difference in result height.
mFormulaTranslationY =
mDisplayFormula.getPaddingBottom() - formula.getPaddingBottom()
+ mDisplayResult.getHeight() - result.getHeight();
}
public void initializeFormulaTranslationX(AlignedTextView formula) {
// Right border of formula moves by the difference in formula end padding.
mFormulaTranslationX = mDisplayFormula.getPaddingEnd() - formula.getPaddingEnd();
}
public void initializeResultTranslationY(CalculatorResult result) {
// Baseline of result moves by the difference in result bottom padding.
mResultTranslationY = mDisplayResult.getPaddingBottom() - result.getPaddingBottom();
}
public void initializeResultTranslationX(CalculatorResult result) {
mResultTranslationX = mDisplayResult.getPaddingEnd() - result.getPaddingEnd();
}
public float getResultTranslationX(float yFraction) {
return (mResultTranslationX * yFraction) - mResultTranslationX;
}
public float getResultTranslationY(float yFraction) {
return (mResultTranslationY * yFraction) - mResultTranslationY;
}
public float getResultScale(float yFraction) {
return 1;
}
public float getFormulaScale(float yFraction) {
return mFormulaScale - (mFormulaScale * yFraction) + yFraction;
}
public float getFormulaTranslationX(float yFraction) {
return (mFormulaTranslationX * yFraction) -
mFormulaTranslationX;
}
public float getFormulaTranslationY(float yFraction) {
// Scale linearly between -FormulaTranslationY and 0.
return (mFormulaTranslationY * yFraction) - mFormulaTranslationY;
}
public float getDateTranslationY(float yFraction) {
// We also want the date to start out above the visible screen with
// this distance decreasing as it's pulled down.
return -mToolbar.getHeight() * (1 - yFraction)
+ getResultTranslationY(yFraction)
- mDisplayFormula.getPaddingTop() +
(mDisplayFormula.getPaddingTop() * yFraction);
}
public float getHistoryElementTranslationY(float yFraction) {
return getDateTranslationY(yFraction);
}
public int getFirstTranslatedViewHolderIndex() {
return 1;
}
}
// The default AnimationController when Display is in RESULT state.
public class ResultAnimationController extends AnimationController
implements DragController.AnimateTextInterface {
@Override
public void initializeScales(AlignedTextView formula, CalculatorResult result) {
final float textSize = mDisplayResult.getTextSize() * mDisplayResult.getScaleX();
mResultScale = textSize / result.getTextSize();
mFormulaScale = 1;
}
@Override
public void initializeFormulaTranslationY(AlignedTextView formula,
CalculatorResult result) {
// Baseline of formula moves by the difference in formula bottom padding and the
// difference in the result height.
mFormulaTranslationY = mDisplayFormula.getPaddingBottom() - formula.getPaddingBottom()
+ mDisplayResult.getHeight() - result.getHeight();
}
@Override
public void initializeFormulaTranslationX(AlignedTextView formula) {
// Right border of formula moves by the difference in formula end padding.
mFormulaTranslationX = mDisplayFormula.getPaddingEnd() - formula.getPaddingEnd();
}
@Override
public void initializeResultTranslationY(CalculatorResult result) {
// Baseline of result moves by the difference in result bottom padding.
mResultTranslationY = mDisplayResult.getBottom() - result.getBottom() +
mDisplayResult.getPaddingBottom() - result.getPaddingBottom();
}
@Override
public void initializeResultTranslationX(CalculatorResult result) {
mResultTranslationX = mDisplayResult.getPaddingEnd() - result.getPaddingEnd();
}
@Override
public float getResultTranslationX(float yFraction) {
return (mResultTranslationX * yFraction) - mResultTranslationX;
}
@Override
public float getResultTranslationY(float yFraction) {
return (mResultTranslationY * yFraction) - mResultTranslationY;
}
@Override
public float getFormulaTranslationX(float yFraction) {
return (mFormulaTranslationX * yFraction) -
mFormulaTranslationX;
}
@Override
public float getFormulaTranslationY(float yFraction) {
return getDateTranslationY(yFraction);
}
@Override
public float getResultScale(float yFraction) {
return mResultScale - (mResultScale * yFraction) + yFraction;
}
@Override
public float getFormulaScale(float yFraction) {
return 1;
}
@Override
public float getDateTranslationY(float yFraction) {
// We also want the date to start out above the visible screen with
// this distance decreasing as it's pulled down.
return -mToolbar.getHeight() * (1 - yFraction)
+ (mResultTranslationY * yFraction) - mResultTranslationY
- mDisplayFormula.getPaddingTop() +
(mDisplayFormula.getPaddingTop() * yFraction);
}
@Override
public int getFirstTranslatedViewHolderIndex() {
return 1;
}
}
// The default AnimationController when Display is completely empty.
public class EmptyAnimationController extends AnimationController
implements DragController.AnimateTextInterface {
@Override
public void initializeDisplayHeight() {
mDisplayHeight = mToolbar.getHeight() + mDisplayResult.getHeight()
+ mDisplayFormula.getHeight();
}
@Override
public void initializeScales(AlignedTextView formula, CalculatorResult result) {
// no-op
}
@Override
public void initializeFormulaTranslationY(AlignedTextView formula,
CalculatorResult result) {
// no-op
}
@Override
public void initializeFormulaTranslationX(AlignedTextView formula) {
// no-op
}
@Override
public void initializeResultTranslationY(CalculatorResult result) {
// no-op
}
@Override
public void initializeResultTranslationX(CalculatorResult result) {
// no-op
}
@Override
public float getResultTranslationX(float yFraction) {
return 0;
}
@Override
public float getResultTranslationY(float yFraction) {
return 0;
}
@Override
public float getFormulaScale(float yFraction) {
return 1;
}
@Override
public float getDateTranslationY(float yFraction) {
return 0;
}
@Override
public float getHistoryElementTranslationY(float yFraction) {
return -mDisplayHeight * (1 - yFraction);
}
@Override
public int getFirstTranslatedViewHolderIndex() {
return 0;
}
}
}