Home | History | Annotate | Download | only in calculator2
      1 /*
      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 
     17 package com.android.calculator2;
     18 
     19 import android.animation.Animator;
     20 import android.app.Fragment;
     21 import android.os.Bundle;
     22 import androidx.core.content.ContextCompat;
     23 import androidx.recyclerview.widget.RecyclerView;
     24 import android.view.LayoutInflater;
     25 import android.view.MenuItem;
     26 import android.view.View;
     27 import android.view.ViewGroup;
     28 import android.widget.Toolbar;
     29 
     30 import java.util.ArrayList;
     31 
     32 import static androidx.recyclerview.widget.RecyclerView.SCROLL_STATE_DRAGGING;
     33 
     34 public class HistoryFragment extends Fragment implements DragLayout.DragCallback {
     35 
     36     public static final String TAG = "HistoryFragment";
     37     public static final String CLEAR_DIALOG_TAG = "clear";
     38 
     39     private final DragController mDragController = new DragController();
     40 
     41     private RecyclerView mRecyclerView;
     42     private HistoryAdapter mAdapter;
     43     private DragLayout mDragLayout;
     44 
     45     private Evaluator mEvaluator;
     46 
     47     private ArrayList<HistoryItem> mDataSet = new ArrayList<>();
     48 
     49     private boolean mIsDisplayEmpty;
     50 
     51     @Override
     52     public void onCreate(Bundle savedInstanceState) {
     53         super.onCreate(savedInstanceState);
     54         mAdapter = new HistoryAdapter(mDataSet);
     55     }
     56 
     57     @Override
     58     public View onCreateView(LayoutInflater inflater, ViewGroup container,
     59             Bundle savedInstanceState) {
     60         final View view = inflater.inflate(
     61                 R.layout.fragment_history, container, false /* attachToRoot */);
     62 
     63         mDragLayout = (DragLayout) container.getRootView().findViewById(R.id.drag_layout);
     64         mDragLayout.addDragCallback(this);
     65 
     66         mRecyclerView = (RecyclerView) view.findViewById(R.id.history_recycler_view);
     67         mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
     68             @Override
     69             public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
     70                 if (newState == SCROLL_STATE_DRAGGING) {
     71                     stopActionModeOrContextMenu();
     72                 }
     73                 super.onScrollStateChanged(recyclerView, newState);
     74             }
     75         });
     76 
     77         // The size of the RecyclerView is not affected by the adapter's contents.
     78         mRecyclerView.setHasFixedSize(true);
     79         mRecyclerView.setAdapter(mAdapter);
     80 
     81         final Toolbar toolbar = (Toolbar) view.findViewById(R.id.history_toolbar);
     82         toolbar.inflateMenu(R.menu.fragment_history);
     83         toolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() {
     84             @Override
     85             public boolean onMenuItemClick(MenuItem item) {
     86                 if (item.getItemId() == R.id.menu_clear_history) {
     87                     final Calculator calculator = (Calculator) getActivity();
     88                     AlertDialogFragment.showMessageDialog(calculator, "" /* title */,
     89                             getString(R.string.dialog_clear),
     90                             getString(R.string.menu_clear_history),
     91                             CLEAR_DIALOG_TAG);
     92                     return true;
     93                 }
     94                 return onOptionsItemSelected(item);
     95             }
     96         });
     97         toolbar.setNavigationOnClickListener(new View.OnClickListener() {
     98             @Override
     99             public void onClick(View v) {
    100                 getActivity().onBackPressed();
    101             }
    102         });
    103         return view;
    104     }
    105 
    106     @Override
    107     public void onActivityCreated(Bundle savedInstanceState) {
    108         super.onActivityCreated(savedInstanceState);
    109 
    110         final Calculator activity = (Calculator) getActivity();
    111         mEvaluator = Evaluator.getInstance(activity);
    112         mAdapter.setEvaluator(mEvaluator);
    113 
    114         final boolean isResultLayout = activity.isResultLayout();
    115         final boolean isOneLine = activity.isOneLine();
    116 
    117         // Snapshot display state here. For the rest of the lifecycle of this current
    118         // HistoryFragment, this is what we will consider the display state.
    119         // In rare cases, the display state can change after our adapter is initialized.
    120         final CalculatorExpr mainExpr = mEvaluator.getExpr(Evaluator.MAIN_INDEX);
    121         mIsDisplayEmpty = mainExpr == null || mainExpr.isEmpty();
    122 
    123         initializeController(isResultLayout, isOneLine, mIsDisplayEmpty);
    124 
    125         final long maxIndex = mEvaluator.getMaxIndex();
    126 
    127         final ArrayList<HistoryItem> newDataSet = new ArrayList<>();
    128 
    129         if (!mIsDisplayEmpty && !isResultLayout) {
    130             // Add the current expression as the first element in the list (the layout is
    131             // reversed and we want the current expression to be the last one in the
    132             // RecyclerView).
    133             // If we are in the result state, the result will animate to the last history
    134             // element in the list and there will be no "Current Expression."
    135             mEvaluator.copyMainToHistory();
    136             newDataSet.add(new HistoryItem(Evaluator.HISTORY_MAIN_INDEX,
    137                     System.currentTimeMillis(), mEvaluator.getExprAsSpannable(0)));
    138         }
    139         for (long i = 0; i < maxIndex; ++i) {
    140             newDataSet.add(null);
    141         }
    142         final boolean isEmpty = newDataSet.isEmpty();
    143         mRecyclerView.setBackgroundColor(ContextCompat.getColor(activity,
    144                 isEmpty ? R.color.empty_history_color : R.color.display_background_color));
    145         if (isEmpty) {
    146             newDataSet.add(new HistoryItem());
    147         }
    148         mDataSet = newDataSet;
    149         mAdapter.setDataSet(mDataSet);
    150         mAdapter.setIsResultLayout(isResultLayout);
    151         mAdapter.setIsOneLine(activity.isOneLine());
    152         mAdapter.setIsDisplayEmpty(mIsDisplayEmpty);
    153         mAdapter.notifyDataSetChanged();
    154     }
    155 
    156     @Override
    157     public void onStart() {
    158         super.onStart();
    159 
    160         final Calculator activity = (Calculator) getActivity();
    161         mDragController.initializeAnimation(activity.isResultLayout(), activity.isOneLine(),
    162                 mIsDisplayEmpty);
    163     }
    164 
    165     @Override
    166     public Animator onCreateAnimator(int transit, boolean enter, int nextAnim) {
    167         return mDragLayout.createAnimator(enter);
    168     }
    169 
    170     @Override
    171     public void onDestroy() {
    172         super.onDestroy();
    173 
    174         if (mDragLayout != null) {
    175             mDragLayout.removeDragCallback(this);
    176         }
    177 
    178         if (mEvaluator != null) {
    179             // Note that the view is destroyed when the fragment backstack is popped, so
    180             // these are essentially called when the DragLayout is closed.
    181             mEvaluator.cancelNonMain();
    182         }
    183     }
    184 
    185     private void initializeController(boolean isResult, boolean isOneLine, boolean isDisplayEmpty) {
    186         mDragController.setDisplayFormula(
    187                 (CalculatorFormula) getActivity().findViewById(R.id.formula));
    188         mDragController.setDisplayResult(
    189                 (CalculatorResult) getActivity().findViewById(R.id.result));
    190         mDragController.setToolbar(getActivity().findViewById(R.id.toolbar));
    191         mDragController.setEvaluator(mEvaluator);
    192         mDragController.initializeController(isResult, isOneLine, isDisplayEmpty);
    193     }
    194 
    195     public boolean stopActionModeOrContextMenu() {
    196         if (mRecyclerView == null) {
    197             return false;
    198         }
    199         for (int i = 0; i < mRecyclerView.getChildCount(); i++) {
    200             final View view = mRecyclerView.getChildAt(i);
    201             final HistoryAdapter.ViewHolder viewHolder =
    202                     (HistoryAdapter.ViewHolder) mRecyclerView.getChildViewHolder(view);
    203             if (viewHolder != null && viewHolder.getResult() != null
    204                     && viewHolder.getResult().stopActionModeOrContextMenu()) {
    205                 return true;
    206             }
    207         }
    208         return false;
    209     }
    210 
    211     /* Begin override DragCallback methods. */
    212 
    213     @Override
    214     public void onStartDraggingOpen() {
    215         // no-op
    216     }
    217 
    218     @Override
    219     public void onInstanceStateRestored(boolean isOpen) {
    220         if (isOpen) {
    221             mRecyclerView.setVisibility(View.VISIBLE);
    222         }
    223     }
    224 
    225     @Override
    226     public void whileDragging(float yFraction) {
    227         if (isVisible() || isRemoving()) {
    228             mDragController.animateViews(yFraction, mRecyclerView);
    229         }
    230     }
    231 
    232     @Override
    233     public boolean shouldCaptureView(View view, int x, int y) {
    234         return !mRecyclerView.canScrollVertically(1 /* scrolling down */);
    235     }
    236 
    237     @Override
    238     public int getDisplayHeight() {
    239         return 0;
    240     }
    241 
    242     /* End override DragCallback methods. */
    243 }
    244