blob: eb2a325cc07058309e34cdcb2da87082f3fb6ab5 [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
Justin Klaassen39297782016-12-19 09:11:38 -080019import android.animation.Animator;
Annie Chinabd202f2016-10-14 14:23:45 -070020import android.app.Fragment;
Annie Chinabd202f2016-10-14 14:23:45 -070021import android.os.Bundle;
Aurimas Liutikas8c43f062018-03-28 08:10:28 -070022import androidx.core.content.ContextCompat;
23import androidx.recyclerview.widget.RecyclerView;
Annie Chinabd202f2016-10-14 14:23:45 -070024import android.view.LayoutInflater;
25import android.view.MenuItem;
26import android.view.View;
27import android.view.ViewGroup;
28import android.widget.Toolbar;
29
Annie Chin06fd3cf2016-11-07 16:04:33 -080030import java.util.ArrayList;
31
Aurimas Liutikas8c43f062018-03-28 08:10:28 -070032import static androidx.recyclerview.widget.RecyclerView.SCROLL_STATE_DRAGGING;
Christine Franks7485df52016-12-01 13:18:45 -080033
Annie Chin52eed7a2017-01-04 15:06:05 -080034public class HistoryFragment extends Fragment implements DragLayout.DragCallback {
Annie Chinabd202f2016-10-14 14:23:45 -070035
36 public static final String TAG = "HistoryFragment";
Annie Chin532b77e2016-12-06 13:30:35 -080037 public static final String CLEAR_DIALOG_TAG = "clear";
Annie Chinabd202f2016-10-14 14:23:45 -070038
Annie Chind0f87d22016-10-24 09:04:12 -070039 private final DragController mDragController = new DragController();
40
41 private RecyclerView mRecyclerView;
42 private HistoryAdapter mAdapter;
Annie Chinb2e96182016-11-28 13:14:54 -080043 private DragLayout mDragLayout;
Annie Chind0f87d22016-10-24 09:04:12 -070044
Annie Chin06fd3cf2016-11-07 16:04:33 -080045 private Evaluator mEvaluator;
46
47 private ArrayList<HistoryItem> mDataSet = new ArrayList<>();
48
Annie Chinbfffafd2017-01-25 15:51:11 -080049 private boolean mIsDisplayEmpty;
50
Annie Chind0f87d22016-10-24 09:04:12 -070051 @Override
52 public void onCreate(Bundle savedInstanceState) {
53 super.onCreate(savedInstanceState);
Annie Chin7d039632016-12-08 15:23:39 -080054 mAdapter = new HistoryAdapter(mDataSet);
Annie Chind0f87d22016-10-24 09:04:12 -070055 }
56
Annie Chinabd202f2016-10-14 14:23:45 -070057 @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
Justin Klaassen39297782016-12-19 09:11:38 -080063 mDragLayout = (DragLayout) container.getRootView().findViewById(R.id.drag_layout);
64 mDragLayout.addDragCallback(this);
65
Annie Chind0f87d22016-10-24 09:04:12 -070066 mRecyclerView = (RecyclerView) view.findViewById(R.id.history_recycler_view);
Christine Franks7485df52016-12-01 13:18:45 -080067 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 });
Annie Chind0f87d22016-10-24 09:04:12 -070076
77 // The size of the RecyclerView is not affected by the adapter's contents.
Annie Chin36147982016-12-01 15:07:34 -080078 mRecyclerView.setHasFixedSize(true);
Annie Chind0f87d22016-10-24 09:04:12 -070079 mRecyclerView.setAdapter(mAdapter);
80
Annie Chinabd202f2016-10-14 14:23:45 -070081 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) {
Annie Chin532b77e2016-12-06 13:30:35 -080087 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);
Annie Chinabd202f2016-10-14 14:23:45 -070092 return true;
93 }
94 return onOptionsItemSelected(item);
95 }
96 });
97 toolbar.setNavigationOnClickListener(new View.OnClickListener() {
98 @Override
99 public void onClick(View v) {
Annie Chin09547532016-10-14 10:59:07 -0700100 getActivity().onBackPressed();
Annie Chinabd202f2016-10-14 14:23:45 -0700101 }
102 });
Annie Chinabd202f2016-10-14 14:23:45 -0700103 return view;
104 }
105
106 @Override
Annie Chind0f87d22016-10-24 09:04:12 -0700107 public void onActivityCreated(Bundle savedInstanceState) {
108 super.onActivityCreated(savedInstanceState);
109
Annie Chin70ac8ea2016-11-18 14:43:56 -0800110 final Calculator activity = (Calculator) getActivity();
Christine Frankscbc51fa2017-01-04 21:00:36 -0800111 mEvaluator = Evaluator.getInstance(activity);
112 mAdapter.setEvaluator(mEvaluator);
Annie Chin7d039632016-12-08 15:23:39 -0800113
Annie Chin94c1bd92016-11-23 13:39:56 -0800114 final boolean isResultLayout = activity.isResultLayout();
Annie Chin88613232017-01-04 12:21:52 -0800115 final boolean isOneLine = activity.isOneLine();
Annie Chin70ac8ea2016-11-18 14:43:56 -0800116
Annie Chinbfffafd2017-01-25 15:51:11 -0800117 // 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);
Annie Chinbc001882016-11-09 19:41:21 -0800124
Annie Chine7c32322017-01-30 18:08:56 -0800125 final long maxIndex = mEvaluator.getMaxIndex();
Annie Chin06fd3cf2016-11-07 16:04:33 -0800126
Annie Chine7c32322017-01-30 18:08:56 -0800127 final ArrayList<HistoryItem> newDataSet = new ArrayList<>();
Annie Chinbc001882016-11-09 19:41:21 -0800128
Annie Chinbfffafd2017-01-25 15:51:11 -0800129 if (!mIsDisplayEmpty && !isResultLayout) {
Annie Chine7c32322017-01-30 18:08:56 -0800130 // 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)));
Annie Chin06fd3cf2016-11-07 16:04:33 -0800138 }
Annie Chine7c32322017-01-30 18:08:56 -0800139 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());
Annie Chinbfffafd2017-01-25 15:51:11 -0800152 mAdapter.setIsDisplayEmpty(mIsDisplayEmpty);
Annie Chinbc001882016-11-09 19:41:21 -0800153 mAdapter.notifyDataSetChanged();
Annie Chinefa259a2016-11-23 14:15:15 -0800154 }
Annie Chinbc001882016-11-09 19:41:21 -0800155
Annie Chinefa259a2016-11-23 14:15:15 -0800156 @Override
157 public void onStart() {
158 super.onStart();
Justin Klaassen39297782016-12-19 09:11:38 -0800159
Annie Chin88613232017-01-04 12:21:52 -0800160 final Calculator activity = (Calculator) getActivity();
Annie Chinb61d00b2017-01-31 12:33:25 -0800161 mDragController.initializeAnimation(activity.isResultLayout(), activity.isOneLine(),
Annie Chinbfffafd2017-01-25 15:51:11 -0800162 mIsDisplayEmpty);
Annie Chind0f87d22016-10-24 09:04:12 -0700163 }
164
165 @Override
Justin Klaassen39297782016-12-19 09:11:38 -0800166 public Animator onCreateAnimator(int transit, boolean enter, int nextAnim) {
Annie China8b31db2017-02-09 08:11:22 -0800167 return mDragLayout.createAnimator(enter);
Justin Klaassen39297782016-12-19 09:11:38 -0800168 }
169
170 @Override
171 public void onDestroy() {
172 super.onDestroy();
173
174 if (mDragLayout != null) {
175 mDragLayout.removeDragCallback(this);
Annie Chind0f87d22016-10-24 09:04:12 -0700176 }
Annie Chin06fd3cf2016-11-07 16:04:33 -0800177
Annie Chin1838cf72017-01-30 08:46:47 -0800178 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 }
Annie Chind0f87d22016-10-24 09:04:12 -0700183 }
184
Annie Chinbfffafd2017-01-25 15:51:11 -0800185 private void initializeController(boolean isResult, boolean isOneLine, boolean isDisplayEmpty) {
Annie Chind0f87d22016-10-24 09:04:12 -0700186 mDragController.setDisplayFormula(
187 (CalculatorFormula) getActivity().findViewById(R.id.formula));
Annie Chind0f87d22016-10-24 09:04:12 -0700188 mDragController.setDisplayResult(
189 (CalculatorResult) getActivity().findViewById(R.id.result));
Annie Chind0f87d22016-10-24 09:04:12 -0700190 mDragController.setToolbar(getActivity().findViewById(R.id.toolbar));
Annie Chinbc001882016-11-09 19:41:21 -0800191 mDragController.setEvaluator(mEvaluator);
Annie Chinbfffafd2017-01-25 15:51:11 -0800192 mDragController.initializeController(isResult, isOneLine, isDisplayEmpty);
Annie Chind0f87d22016-10-24 09:04:12 -0700193 }
194
Christine Franks7485df52016-12-01 13:18:45 -0800195 public boolean stopActionModeOrContextMenu() {
Annie Chin1fcfc312016-12-06 09:36:36 -0800196 if (mRecyclerView == null) {
197 return false;
198 }
Christine Franks7485df52016-12-01 13:18:45 -0800199 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);
Annie Chin532b77e2016-12-06 13:30:35 -0800203 if (viewHolder != null && viewHolder.getResult() != null
204 && viewHolder.getResult().stopActionModeOrContextMenu()) {
Christine Franks7485df52016-12-01 13:18:45 -0800205 return true;
206 }
207 }
208 return false;
209 }
Annie Chin52eed7a2017-01-04 15:06:05 -0800210
211 /* Begin override DragCallback methods. */
212
213 @Override
214 public void onStartDraggingOpen() {
215 // no-op
216 }
217
218 @Override
Justin Klaassen39297782016-12-19 09:11:38 -0800219 public void onInstanceStateRestored(boolean isOpen) {
220 if (isOpen) {
221 mRecyclerView.setVisibility(View.VISIBLE);
222 }
223 }
224
225 @Override
Annie Chin52eed7a2017-01-04 15:06:05 -0800226 public void whileDragging(float yFraction) {
Justin Klaassen39297782016-12-19 09:11:38 -0800227 if (isVisible() || isRemoving()) {
228 mDragController.animateViews(yFraction, mRecyclerView);
229 }
Annie Chin52eed7a2017-01-04 15:06:05 -0800230 }
231
232 @Override
233 public boolean shouldCaptureView(View view, int x, int y) {
Justin Klaassen39297782016-12-19 09:11:38 -0800234 return !mRecyclerView.canScrollVertically(1 /* scrolling down */);
Annie Chin52eed7a2017-01-04 15:06:05 -0800235 }
236
237 @Override
238 public int getDisplayHeight() {
239 return 0;
240 }
241
Annie Chin52eed7a2017-01-04 15:06:05 -0800242 /* End override DragCallback methods. */
Annie Chinabd202f2016-10-14 14:23:45 -0700243}