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 android.support.v4.content.ContextCompat; 23 import android.support.v7.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 android.support.v7.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 onCreate(Bundle savedInstanceState)52 public void onCreate(Bundle savedInstanceState) { 53 super.onCreate(savedInstanceState); 54 mAdapter = new HistoryAdapter(mDataSet); 55 } 56 57 @Override onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)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 onActivityCreated(Bundle savedInstanceState)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 onStart()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 onCreateAnimator(int transit, boolean enter, int nextAnim)166 public Animator onCreateAnimator(int transit, boolean enter, int nextAnim) { 167 return mDragLayout.createAnimator(enter); 168 } 169 170 @Override onDestroy()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 initializeController(boolean isResult, boolean isOneLine, boolean isDisplayEmpty)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 stopActionModeOrContextMenu()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 onStartDraggingOpen()214 public void onStartDraggingOpen() { 215 // no-op 216 } 217 218 @Override onInstanceStateRestored(boolean isOpen)219 public void onInstanceStateRestored(boolean isOpen) { 220 if (isOpen) { 221 mRecyclerView.setVisibility(View.VISIBLE); 222 } 223 } 224 225 @Override whileDragging(float yFraction)226 public void whileDragging(float yFraction) { 227 if (isVisible() || isRemoving()) { 228 mDragController.animateViews(yFraction, mRecyclerView); 229 } 230 } 231 232 @Override shouldCaptureView(View view, int x, int y)233 public boolean shouldCaptureView(View view, int x, int y) { 234 return !mRecyclerView.canScrollVertically(1 /* scrolling down */); 235 } 236 237 @Override getDisplayHeight()238 public int getDisplayHeight() { 239 return 0; 240 } 241 242 /* End override DragCallback methods. */ 243 } 244