Avoid potential negative array index access to cached text.
[LibreOffice.git] / android / source / src / java / org / libreoffice / overlay / CalcHeadersController.java
blob8b99c292cbc543012f7ed326f0ae5303d568c2fa
1 package org.libreoffice.overlay;
3 import android.content.Context;
4 import android.graphics.PointF;
5 import android.graphics.RectF;
6 import android.graphics.drawable.ColorDrawable;
7 import com.google.android.material.snackbar.Snackbar;
8 import android.util.Log;
9 import android.view.KeyEvent;
10 import android.view.Gravity;
11 import android.view.LayoutInflater;
12 import android.view.View;
13 import android.view.ViewGroup.LayoutParams;
14 import android.view.inputmethod.EditorInfo;
15 import android.widget.EditText;
16 import android.widget.Button;
17 import android.widget.PopupWindow;
18 import android.widget.TextView;
20 import org.json.JSONArray;
21 import org.json.JSONException;
22 import org.json.JSONObject;
23 import org.libreoffice.LOEvent;
24 import org.libreoffice.LOKitShell;
25 import org.libreoffice.LibreOfficeMainActivity;
26 import org.libreoffice.R;
27 import org.mozilla.gecko.gfx.LayerView;
29 import java.math.BigDecimal;
30 import java.util.ArrayList;
32 import static org.libreoffice.SearchController.addProperty;
34 public class CalcHeadersController {
35 private static final String LOGTAG = CalcHeadersController.class.getSimpleName();
37 private final CalcHeadersView mCalcRowHeadersView;
38 private final CalcHeadersView mCalcColumnHeadersView;
40 private LibreOfficeMainActivity mContext;
42 public CalcHeadersController(LibreOfficeMainActivity context, final LayerView layerView) {
43 mContext = context;
44 mContext.getDocumentOverlay().setCalcHeadersController(this);
45 mCalcRowHeadersView = context.findViewById(R.id.calc_header_row);
46 mCalcColumnHeadersView = context.findViewById(R.id.calc_header_column);
47 if (mCalcColumnHeadersView == null || mCalcRowHeadersView == null) {
48 Log.e(LOGTAG, "Failed to initialize Calc headers - View is null");
49 } else {
50 mCalcRowHeadersView.initialize(layerView, true);
51 mCalcColumnHeadersView.initialize(layerView, false);
53 LOKitShell.sendEvent(new LOEvent(LOEvent.UPDATE_CALC_HEADERS));
54 context.findViewById(R.id.calc_header_top_left).setOnClickListener(new View.OnClickListener() {
55 @Override
56 public void onClick(View v) {
57 LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:SelectAll"));
58 if (mCalcColumnHeadersView == null) return;
59 mCalcColumnHeadersView.showHeaderPopup(new PointF());
61 });
62 ((EditText)context.findViewById(R.id.calc_address)).setOnEditorActionListener(new TextView.OnEditorActionListener() {
63 @Override
64 public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
65 if (actionId == EditorInfo.IME_ACTION_DONE || actionId == EditorInfo.IME_ACTION_GO) {
66 String text = v.getText().toString();
67 JSONObject rootJson = new JSONObject();
68 try {
69 addProperty(rootJson, "ToPoint", "string", text);
70 } catch (JSONException e) {
71 e.printStackTrace();
73 LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:GoToCell", rootJson.toString()));
74 mContext.hideSoftKeyboard();
75 layerView.requestFocus();
77 return true;
79 });
80 ((EditText)context.findViewById(R.id.calc_formula)).setOnEditorActionListener(new TextView.OnEditorActionListener() {
81 @Override
82 public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
83 if (actionId == EditorInfo.IME_ACTION_DONE || actionId == EditorInfo.IME_ACTION_GO) {
84 String text = v.getText().toString();
85 JSONObject rootJson = new JSONObject();
86 try {
87 addProperty(rootJson, "StringName", "string", text);
88 addProperty(rootJson, "DontCommit", "boolean", String.valueOf(false));
89 } catch (JSONException e) {
90 e.printStackTrace();
92 LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:EnterString", rootJson.toString()));
93 mContext.hideSoftKeyboard();
94 layerView.requestFocus();
95 mContext.setDocumentChanged(true);
97 return true;
99 });
100 // manually select A1 for address bar and formula bar to update when calc first opens
101 JSONObject rootJson = new JSONObject();
102 try {
103 addProperty(rootJson, "ToPoint", "string", "A1");
104 } catch (JSONException e) {
105 e.printStackTrace();
107 LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:GoToCell", rootJson.toString()));
110 public void setupHeaderPopupView() {
111 LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
112 String[] rowOrColumn = {"Row","Column"};
113 CalcHeadersView[] headersViews= {mCalcRowHeadersView, mCalcColumnHeadersView};
114 for (int i = 0; i < rowOrColumn.length; i++) {
115 // create popup window
116 final String tempName = rowOrColumn[i];
117 final CalcHeadersView tempView = headersViews[i];
118 final View headerPopupView = inflater.inflate(R.layout.calc_header_popup, null);
119 final PopupWindow popupWindow = new PopupWindow(headerPopupView, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
120 popupWindow.setOnDismissListener(new PopupWindow.OnDismissListener() {
121 @Override
122 public void onDismiss() {
123 headerPopupView.findViewById(R.id.calc_header_popup_optimal_length_dialog).setVisibility(View.GONE);
124 popupWindow.setFocusable(false);
127 popupWindow.setOutsideTouchable(true);
128 popupWindow.setBackgroundDrawable(new ColorDrawable());
129 popupWindow.setAnimationStyle(android.R.style.Animation_Dialog);
130 tempView.setHeaderPopupWindow(popupWindow);
131 // set up child views in the popup window
132 headerPopupView.findViewById(R.id.calc_header_popup_insert).setOnClickListener(new View.OnClickListener() {
133 @Override
134 public void onClick(View v) {
135 LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:Insert"+tempName+"s"));
136 tempView.dismissPopupWindow();
137 mContext.setDocumentChanged(true);
140 headerPopupView.findViewById(R.id.calc_header_popup_delete).setOnClickListener(new View.OnClickListener() {
141 @Override
142 public void onClick(View v) {
143 LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:Delete"+tempName+"s"));
144 tempView.dismissPopupWindow();
145 mContext.setDocumentChanged(true);
148 headerPopupView.findViewById(R.id.calc_header_popup_hide).setOnClickListener(new View.OnClickListener() {
149 @Override
150 public void onClick(View v) {
151 LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:Hide"+tempName));
152 tempView.dismissPopupWindow();
153 mContext.setDocumentChanged(true);
156 headerPopupView.findViewById(R.id.calc_header_popup_show).setOnClickListener(new View.OnClickListener() {
157 @Override
158 public void onClick(View v) {
159 LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:Show"+tempName));
160 tempView.dismissPopupWindow();
161 mContext.setDocumentChanged(true);
164 headerPopupView.findViewById(R.id.calc_header_popup_optimal_length).setOnClickListener(new View.OnClickListener() {
165 @Override
166 public void onClick(View v) {
167 View view = headerPopupView.findViewById(R.id.calc_header_popup_optimal_length_dialog);
168 if (view.getVisibility() == View.VISIBLE) {
169 view.setVisibility(View.GONE);
170 popupWindow.setFocusable(false);
171 popupWindow.update();
172 } else {
173 popupWindow.dismiss();
174 view.setVisibility(View.VISIBLE);
175 popupWindow.setFocusable(true);
176 popupWindow.showAtLocation(tempView, Gravity.CENTER, 0, 0);
177 LOKitShell.getMainHandler().post(new Runnable() {
178 @Override
179 public void run() {
180 Snackbar.make(tempView, R.string.calc_alert_double_click_optimal_length, Snackbar.LENGTH_LONG).show();
186 headerPopupView.findViewById(R.id.calc_header_popup_optimal_length_button).setOnClickListener(new View.OnClickListener() {
187 @Override
188 public void onClick(View v) {
189 String text = ((EditText)headerPopupView.findViewById(R.id.calc_header_popup_optimal_length_text)).getText().toString();
190 tempView.sendOptimalLengthRequest(text);
191 tempView.dismissPopupWindow();
192 mContext.setDocumentChanged(true);
195 headerPopupView.findViewById(R.id.calc_header_popup_adjust_length).setOnClickListener(new View.OnClickListener() {
196 @Override
197 public void onClick(View v) {
198 mContext.getDocumentOverlay().showAdjustLengthLine(tempView == mCalcRowHeadersView, tempView);
199 tempView.dismissPopupWindow();
200 mContext.setDocumentChanged(true);
203 ((Button)headerPopupView.findViewById(R.id.calc_header_popup_adjust_length))
204 .setText(tempView == mCalcRowHeadersView ? R.string.calc_adjust_height : R.string.calc_adjust_width);
205 ((Button)headerPopupView.findViewById(R.id.calc_header_popup_optimal_length))
206 .setText(tempView == mCalcRowHeadersView ? R.string.calc_optimal_height : R.string.calc_optimal_width);
211 public void setHeaders(String headers) {
212 HeaderInfo parsedHeaders = parseHeaderInfo(headers);
213 if (parsedHeaders != null) {
214 mCalcRowHeadersView.setHeaders(parsedHeaders.rowLabels, parsedHeaders.rowDimens);
215 mCalcColumnHeadersView.setHeaders(parsedHeaders.columnLabels, parsedHeaders.columnDimens);
216 showHeaders();
217 } else {
218 Log.e(LOGTAG, "Parse header info JSON failed.");
222 public void showHeaders() {
223 LOKitShell.getMainHandler().post(new Runnable() {
224 @Override
225 public void run() {
226 mCalcColumnHeadersView.invalidate();
227 mCalcRowHeadersView.invalidate();
232 private HeaderInfo parseHeaderInfo(String headers) {
233 HeaderInfo headerInfo = new HeaderInfo();
234 try {
235 JSONObject collectiveResult = new JSONObject(headers);
236 JSONArray rowResult = collectiveResult.getJSONArray("rows");
237 for (int i = 0; i < rowResult.length(); i++) {
238 headerInfo.rowLabels.add(rowResult.getJSONObject(i).getString("text"));
239 headerInfo.rowDimens.add(BigDecimal.valueOf(rowResult.getJSONObject(i).getLong("size")).floatValue());
241 JSONArray columnResult = collectiveResult.getJSONArray("columns");
242 for (int i = 0; i < columnResult.length(); i++) {
243 headerInfo.columnLabels.add(columnResult.getJSONObject(i).getString("text"));
244 headerInfo.columnDimens.add(BigDecimal.valueOf(columnResult.getJSONObject(i).getLong("size")).floatValue());
246 return headerInfo;
247 } catch (JSONException e) {
248 e.printStackTrace();
250 return null;
253 public void showHeaderSelection(RectF cellCursorRect) {
254 mCalcRowHeadersView.setHeaderSelection(cellCursorRect);
255 mCalcColumnHeadersView.setHeaderSelection(cellCursorRect);
256 showHeaders();
259 public void setPendingRowOrColumnSelectionToShowUp(boolean b) {
260 mCalcRowHeadersView.setPendingRowOrColumnSelectionToShowUp(b);
261 mCalcColumnHeadersView.setPendingRowOrColumnSelectionToShowUp(b);
264 public boolean pendingRowOrColumnSelectionToShowUp() {
265 return mCalcColumnHeadersView.pendingRowOrColumnSelectionToShowUp()
266 || mCalcRowHeadersView.pendingRowOrColumnSelectionToShowUp();
269 private class HeaderInfo {
270 ArrayList<String> rowLabels;
271 ArrayList<Float> rowDimens;
272 ArrayList<String> columnLabels;
273 ArrayList<Float> columnDimens;
274 private HeaderInfo() {
275 rowLabels = new ArrayList<String>();
276 rowDimens = new ArrayList<Float>();
277 columnDimens = new ArrayList<Float>();
278 columnLabels = new ArrayList<String>();