blob: 9c9c5737fafa705e5330671c77439c392af19459 [file] [log] [blame]
Annie Chinabd202f2016-10-14 14:23:45 -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.animation.Animator;
20import android.animation.ObjectAnimator;
21import android.app.Fragment;
Annie Chin1ff328d2016-11-22 12:57:46 -080022import android.app.FragmentTransaction;
Annie Chin36147982016-12-01 15:07:34 -080023import android.graphics.Color;
Annie Chinabd202f2016-10-14 14:23:45 -070024import android.os.Bundle;
Annie Chin36147982016-12-01 15:07:34 -080025import android.support.v4.content.ContextCompat;
Annie Chind0f87d22016-10-24 09:04:12 -070026import android.support.v7.widget.RecyclerView;
Annie Chinabd202f2016-10-14 14:23:45 -070027import android.view.LayoutInflater;
28import android.view.MenuItem;
29import android.view.View;
30import android.view.ViewGroup;
31import android.widget.Toolbar;
32
Annie Chin06fd3cf2016-11-07 16:04:33 -080033import java.util.ArrayList;
34
Christine Franks7485df52016-12-01 13:18:45 -080035import static android.support.v7.widget.RecyclerView.SCROLL_STATE_DRAGGING;
36
Annie Chinabd202f2016-10-14 14:23:45 -070037public class HistoryFragment extends Fragment {
38
39 public static final String TAG = "HistoryFragment";
Annie Chin532b77e2016-12-06 13:30:35 -080040 public static final String CLEAR_DIALOG_TAG = "clear";
Annie Chinabd202f2016-10-14 14:23:45 -070041
Annie Chind0f87d22016-10-24 09:04:12 -070042 private final DragLayout.DragCallback mDragCallback =
43 new DragLayout.DragCallback() {
44 @Override
Annie Chin9a211132016-11-30 12:52:06 -080045 public void onStartDraggingOpen() {
Annie Chind0f87d22016-10-24 09:04:12 -070046 // no-op
47 }
48
49 @Override
50 public void whileDragging(float yFraction) {
Annie Chin0e88baa2016-11-28 15:23:07 -080051 mDragController.animateViews(yFraction, mRecyclerView);
Annie Chind0f87d22016-10-24 09:04:12 -070052 }
53
54 @Override
Annie Chind3443222016-12-07 17:19:07 -080055 public boolean shouldCaptureView(View view, int x, int y) {
56 return view.getId() == R.id.history_frame
57 && mDragLayout.isViewUnder(view, x, y)
58 && !mRecyclerView.canScrollVertically(1 /* scrolling down */);
Annie Chind0f87d22016-10-24 09:04:12 -070059 }
60
61 @Override
62 public int getDisplayHeight() {
63 return 0;
64 }
65
66 @Override
67 public void onLayout(int translation) {
68 // no-op
69 }
70 };
71
72 private final DragController mDragController = new DragController();
73
74 private RecyclerView mRecyclerView;
75 private HistoryAdapter mAdapter;
Annie Chinb2e96182016-11-28 13:14:54 -080076 private DragLayout mDragLayout;
Annie Chind0f87d22016-10-24 09:04:12 -070077
Annie Chin06fd3cf2016-11-07 16:04:33 -080078 private Evaluator mEvaluator;
79
80 private ArrayList<HistoryItem> mDataSet = new ArrayList<>();
81
Annie Chind0f87d22016-10-24 09:04:12 -070082 @Override
83 public void onCreate(Bundle savedInstanceState) {
84 super.onCreate(savedInstanceState);
85
Annie Chin7d039632016-12-08 15:23:39 -080086 mAdapter = new HistoryAdapter(mDataSet);
Annie Chind0f87d22016-10-24 09:04:12 -070087 }
88
Annie Chinabd202f2016-10-14 14:23:45 -070089 @Override
90 public View onCreateView(LayoutInflater inflater, ViewGroup container,
91 Bundle savedInstanceState) {
92 final View view = inflater.inflate(
93 R.layout.fragment_history, container, false /* attachToRoot */);
94
Annie Chind0f87d22016-10-24 09:04:12 -070095 mRecyclerView = (RecyclerView) view.findViewById(R.id.history_recycler_view);
Christine Franks7485df52016-12-01 13:18:45 -080096 mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
97 @Override
98 public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
99 if (newState == SCROLL_STATE_DRAGGING) {
100 stopActionModeOrContextMenu();
101 }
102 super.onScrollStateChanged(recyclerView, newState);
103 }
104 });
Annie Chind0f87d22016-10-24 09:04:12 -0700105
106 // The size of the RecyclerView is not affected by the adapter's contents.
Annie Chin36147982016-12-01 15:07:34 -0800107 mRecyclerView.setHasFixedSize(true);
Annie Chind0f87d22016-10-24 09:04:12 -0700108 mRecyclerView.setAdapter(mAdapter);
109
Annie Chinabd202f2016-10-14 14:23:45 -0700110 final Toolbar toolbar = (Toolbar) view.findViewById(R.id.history_toolbar);
111 toolbar.inflateMenu(R.menu.fragment_history);
112 toolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() {
113 @Override
114 public boolean onMenuItemClick(MenuItem item) {
115 if (item.getItemId() == R.id.menu_clear_history) {
Annie Chin532b77e2016-12-06 13:30:35 -0800116 final Calculator calculator = (Calculator) getActivity();
117 AlertDialogFragment.showMessageDialog(calculator, "" /* title */,
118 getString(R.string.dialog_clear),
119 getString(R.string.menu_clear_history),
120 CLEAR_DIALOG_TAG);
Annie Chinabd202f2016-10-14 14:23:45 -0700121 return true;
122 }
123 return onOptionsItemSelected(item);
124 }
125 });
126 toolbar.setNavigationOnClickListener(new View.OnClickListener() {
127 @Override
128 public void onClick(View v) {
Annie Chin09547532016-10-14 10:59:07 -0700129 getActivity().onBackPressed();
Annie Chinabd202f2016-10-14 14:23:45 -0700130 }
131 });
Annie Chinabd202f2016-10-14 14:23:45 -0700132 return view;
133 }
134
135 @Override
Annie Chind0f87d22016-10-24 09:04:12 -0700136 public void onActivityCreated(Bundle savedInstanceState) {
137 super.onActivityCreated(savedInstanceState);
138
Annie Chin70ac8ea2016-11-18 14:43:56 -0800139 final Calculator activity = (Calculator) getActivity();
Christine Frankscbc51fa2017-01-04 21:00:36 -0800140 mEvaluator = Evaluator.getInstance(activity);
141 mAdapter.setEvaluator(mEvaluator);
Annie Chin7d039632016-12-08 15:23:39 -0800142
Annie Chin94c1bd92016-11-23 13:39:56 -0800143 final boolean isResultLayout = activity.isResultLayout();
Annie Chin70ac8ea2016-11-18 14:43:56 -0800144
Annie Chinb2e96182016-11-28 13:14:54 -0800145 mDragLayout = (DragLayout) activity.findViewById(R.id.drag_layout);
146 mDragLayout.removeDragCallback(mDragCallback);
147 mDragLayout.addDragCallback(mDragCallback);
148
Annie Chin06fd3cf2016-11-07 16:04:33 -0800149 if (mEvaluator != null) {
Annie Chin94c1bd92016-11-23 13:39:56 -0800150 initializeController(isResultLayout);
Annie Chinbc001882016-11-09 19:41:21 -0800151
Annie Chin06fd3cf2016-11-07 16:04:33 -0800152 final long maxIndex = mEvaluator.getMaxIndex();
153
154 final ArrayList<HistoryItem> newDataSet = new ArrayList<>();
Annie Chinbc001882016-11-09 19:41:21 -0800155
Annie Chin94c1bd92016-11-23 13:39:56 -0800156 if (!EvaluatorStateUtils.isDisplayEmpty(mEvaluator) && !isResultLayout) {
Annie Chin70ac8ea2016-11-18 14:43:56 -0800157 // Add the current expression as the first element in the list (the layout is
158 // reversed and we want the current expression to be the last one in the
Annie Chind3443222016-12-07 17:19:07 -0800159 // RecyclerView).
Annie Chin70ac8ea2016-11-18 14:43:56 -0800160 // If we are in the result state, the result will animate to the last history
161 // element in the list and there will be no "Current Expression."
Hans Boehm31ea2522016-11-23 17:47:02 -0800162 mEvaluator.copyMainToHistory();
163 newDataSet.add(new HistoryItem(Evaluator.HISTORY_MAIN_INDEX,
164 System.currentTimeMillis(), mEvaluator.getExprAsSpannable(0)));
Annie Chinbc001882016-11-09 19:41:21 -0800165 }
Annie Chin91796232016-11-16 17:27:36 -0800166 for (long i = 0; i < maxIndex; ++i) {
Annie Chin06fd3cf2016-11-07 16:04:33 -0800167 newDataSet.add(null);
168 }
Annie Chin36147982016-12-01 15:07:34 -0800169 final boolean isEmpty = newDataSet.isEmpty();
170 mRecyclerView.setBackgroundColor(isEmpty
171 ? ContextCompat.getColor(activity, R.color.empty_history_color)
172 : Color.TRANSPARENT);
173 if (isEmpty) {
Annie Chin06fd3cf2016-11-07 16:04:33 -0800174 newDataSet.add(new HistoryItem());
175 }
Annie Chin06fd3cf2016-11-07 16:04:33 -0800176 mDataSet = newDataSet;
177 mAdapter.setDataSet(mDataSet);
Annie Chin94c1bd92016-11-23 13:39:56 -0800178 mAdapter.setIsResultLayout(isResultLayout);
Annie Chin06fd3cf2016-11-07 16:04:33 -0800179 }
Annie Chinbc001882016-11-09 19:41:21 -0800180
181 mAdapter.notifyDataSetChanged();
Annie Chinefa259a2016-11-23 14:15:15 -0800182 }
Annie Chinbc001882016-11-09 19:41:21 -0800183
Annie Chinefa259a2016-11-23 14:15:15 -0800184 @Override
185 public void onStart() {
186 super.onStart();
187
188 // The orientation may have changed.
Annie Chin94c1bd92016-11-23 13:39:56 -0800189 mDragController.initializeAnimation(mRecyclerView,
Annie Chinb2e96182016-11-28 13:14:54 -0800190 ((Calculator) getActivity()).isResultLayout(), mDragLayout.isOpen());
Annie Chind0f87d22016-10-24 09:04:12 -0700191 }
192
193 @Override
Annie Chinabd202f2016-10-14 14:23:45 -0700194 public Animator onCreateAnimator(int transit, boolean enter, int nextAnim) {
195 final View view = getView();
196 final int height = getResources().getDisplayMetrics().heightPixels;
Annie Chin91796232016-11-16 17:27:36 -0800197 if (enter) {
Annie Chin1ff328d2016-11-22 12:57:46 -0800198 if (transit == FragmentTransaction.TRANSIT_FRAGMENT_OPEN) {
199 return ObjectAnimator.ofFloat(view, View.TRANSLATION_Y, -height, 0f);
200 } else {
201 return null;
202 }
Annie Chin91796232016-11-16 17:27:36 -0800203 } else {
204 return ObjectAnimator.ofFloat(view, View.TRANSLATION_Y, -height);
Annie Chinabd202f2016-10-14 14:23:45 -0700205 }
Annie Chinabd202f2016-10-14 14:23:45 -0700206 }
207
Annie Chind0f87d22016-10-24 09:04:12 -0700208 @Override
209 public void onDestroyView() {
210 final DragLayout dragLayout = (DragLayout) getActivity().findViewById(R.id.drag_layout);
211 if (dragLayout != null) {
212 dragLayout.removeDragCallback(mDragCallback);
213 }
Annie Chin06fd3cf2016-11-07 16:04:33 -0800214
Annie Chin9a211132016-11-30 12:52:06 -0800215 // Note that the view is destroyed when the fragment backstack is popped, so
216 // these are essentially called when the DragLayout is closed.
Hans Boehm31ea2522016-11-23 17:47:02 -0800217 mEvaluator.cancelNonMain();
Annie Chin9a211132016-11-30 12:52:06 -0800218
219 super.onDestroyView();
Annie Chind0f87d22016-10-24 09:04:12 -0700220 }
221
Annie Chin94c1bd92016-11-23 13:39:56 -0800222 private void initializeController(boolean isResult) {
Annie Chind0f87d22016-10-24 09:04:12 -0700223 mDragController.setDisplayFormula(
224 (CalculatorFormula) getActivity().findViewById(R.id.formula));
225
226 mDragController.setDisplayResult(
227 (CalculatorResult) getActivity().findViewById(R.id.result));
228
229 mDragController.setToolbar(getActivity().findViewById(R.id.toolbar));
Annie Chinbc001882016-11-09 19:41:21 -0800230
231 mDragController.setEvaluator(mEvaluator);
Annie Chin94c1bd92016-11-23 13:39:56 -0800232
233 mDragController.initializeController(isResult);
Annie Chind0f87d22016-10-24 09:04:12 -0700234 }
235
Christine Franks7485df52016-12-01 13:18:45 -0800236 public boolean stopActionModeOrContextMenu() {
Annie Chin1fcfc312016-12-06 09:36:36 -0800237 if (mRecyclerView == null) {
238 return false;
239 }
Christine Franks7485df52016-12-01 13:18:45 -0800240 for (int i = 0; i < mRecyclerView.getChildCount(); i++) {
241 final View view = mRecyclerView.getChildAt(i);
242 final HistoryAdapter.ViewHolder viewHolder =
243 (HistoryAdapter.ViewHolder) mRecyclerView.getChildViewHolder(view);
Annie Chin532b77e2016-12-06 13:30:35 -0800244 if (viewHolder != null && viewHolder.getResult() != null
245 && viewHolder.getResult().stopActionModeOrContextMenu()) {
Christine Franks7485df52016-12-01 13:18:45 -0800246 return true;
247 }
248 }
249 return false;
250 }
Annie Chinabd202f2016-10-14 14:23:45 -0700251}