View Javadoc

1   /***
2    *     Aedict - an EDICT browser for Android
3    Copyright (C) 2009 Martin Vysny
4    
5    This program is free software: you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation, either version 3 of the License, or
8    (at your option) any later version.
9    
10   This program is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   GNU General Public License for more details.
14  
15   You should have received a copy of the GNU General Public License
16   along with this program.  If not, see <http://www.gnu.org/licenses/>.
17   */
18  
19  package sk.baka.aedict;
20  
21  import java.io.IOException;
22  import java.io.Serializable;
23  import java.util.ArrayList;
24  import java.util.Iterator;
25  import java.util.List;
26  
27  import sk.baka.aedict.util.Constants;
28  import sk.baka.autils.AndroidUtils;
29  import sk.baka.autils.DialogUtils;
30  import android.app.Activity;
31  import android.app.AlertDialog;
32  import android.content.DialogInterface;
33  import android.graphics.Canvas;
34  import android.graphics.Paint;
35  import android.graphics.Rect;
36  import android.os.Bundle;
37  import android.view.MotionEvent;
38  import android.view.View;
39  import android.view.View.OnTouchListener;
40  import android.view.ViewGroup;
41  import android.widget.Button;
42  import android.widget.TextView;
43  import edu.arizona.cs.javadict.DrawPanel;
44  
45  /***
46   * Allows user to draw a kanji and perform a Kanji lookup.
47   * 
48   * @author Martin Vysny
49   */
50  public class KanjiDrawActivity extends AbstractActivity {
51  
52  	private static final class State implements Serializable {
53  		private static final long serialVersionUID = 1L;
54  		public final List<String> kanjis = new ArrayList<String>();
55  		public final List<Character> selected = new ArrayList<Character>();
56  
57  		public boolean isEmpty() {
58  			return kanjis.isEmpty();
59  		}
60  
61  		public String getSelectedWord() {
62  			final StringBuilder result = new StringBuilder();
63  			for (final Character c : selected) {
64  				result.append(c);
65  			}
66  			return result.toString();
67  		}
68  	}
69  
70  	private State state;
71  	private static final String BUNDLEKEY_STATE = "state";
72  
73  	@Override
74  	protected void onRestoreInstanceState(Bundle savedInstanceState) {
75  		state = (State) savedInstanceState.getSerializable(BUNDLEKEY_STATE);
76  	}
77  
78  	@Override
79  	protected void onSaveInstanceState(Bundle outState) {
80  		outState.putSerializable(BUNDLEKEY_STATE, state);
81  	}
82  
83  	@Override
84  	protected void onCreate(Bundle savedInstanceState) {
85  		super.onCreate(savedInstanceState);
86  		state = new State();
87  		setContentView(R.layout.kanjidraw);
88  		final PainterView view = new PainterView(this, R.id.textStrokes);
89  		((ViewGroup) findViewById(R.id.kanjidrawRoot)).addView(view);
90  		findViewById(R.id.btnKanjiClear).setOnClickListener(AndroidUtils.safe(this, new View.OnClickListener() {
91  
92  			public void onClick(View v) {
93  				view.clear();
94  			}
95  		}));
96  		findViewById(R.id.btnKanjiSearch).setOnClickListener(AndroidUtils.safe(this, new View.OnClickListener() {
97  
98  			public void onClick(View v) {
99  				try {
100 					final String kanjis = state.isEmpty() ? view.analyzeKanji() : state.getSelectedWord();
101 					KanjiAnalyzeActivity.launch(KanjiDrawActivity.this, kanjis, !state.isEmpty());
102 				} catch (IOException e) {
103 					throw new RuntimeException(e);
104 				}
105 			}
106 		}));
107 		findViewById(R.id.undo).setOnClickListener(AndroidUtils.safe(this, new View.OnClickListener() {
108 
109 			public void onClick(View v) {
110 				view.undoLastStroke();
111 			}
112 		}));
113 		findViewById(R.id.more).setOnClickListener(AndroidUtils.safe(this, new View.OnClickListener() {
114 
115 			public void onClick(View v) {
116 				try {
117 					final String kanjis = view.analyzeKanji();
118 					if (kanjis.length() > 0) {
119 						state.kanjis.add(kanjis);
120 						state.selected.add(kanjis.charAt(0));
121 						view.clear();
122 						update();
123 					}
124 				} catch (IOException e) {
125 					throw new RuntimeException(e);
126 				}
127 			}
128 		}));
129 		new DialogUtils(this).showInfoOnce(Constants.INFOONCE_KANJIDRAWWARNING, -1, R.string.kanjiDrawWarning);
130 	}
131 
132 	/***
133 	 * Uses the DrawPanel class to paint and recognize Kanjis.
134 	 * 
135 	 * @author Martin Vysny
136 	 */
137 	public static class PainterView extends View implements OnTouchListener {
138 		private final DrawPanel recognizer;
139 		private final Paint bg = new Paint();
140 		private final Paint fg1 = new Paint();
141 		private final Paint fg2 = new Paint();
142 		private final int textViewStrokes;
143 
144 		public PainterView(Activity context, final int textViewStrokes) {
145 			super(context);
146 			recognizer = new DrawPanel(context.getClassLoader());
147 			this.textViewStrokes = textViewStrokes;
148 			setFocusable(true);
149 			setFocusableInTouchMode(true);
150 			this.setOnTouchListener(this);
151 			bg.setARGB(255, 0, 0, 0);
152 			fg1.setARGB(255, 235, 255, 235);
153 			fg1.setAntiAlias(true);
154 			fg1.setStrokeWidth(8f);
155 			fg2.setARGB(255, 160, 160, 255);
156 			fg2.setAntiAlias(true);
157 			fg2.setStrokeWidth(8f);
158 			updateStrokes();
159 		}
160 
161 		public String analyzeKanji() throws IOException {
162 			return recognizer.analyzeKanji();
163 		}
164 
165 		public void undoLastStroke() {
166 			recognizer.undoLastStroke();
167 			updateStrokes();
168 			invalidate();
169 		}
170 
171 		public void clear() {
172 			recognizer.clear();
173 			updateStrokes();
174 			invalidate();
175 		}
176 
177 		@Override
178 		protected void onDraw(Canvas c) {
179 			Rect r = new Rect();
180 			getDrawingRect(r);
181 			c.drawRect(r, bg);
182 			Iterator<List<Integer>> xe = recognizer.xstrokes.iterator();
183 			Iterator<List<Integer>> ye = recognizer.ystrokes.iterator();
184 			while (xe.hasNext()) {
185 				List<Integer> xvec, yvec;
186 				xvec = xe.next();
187 				yvec = ye.next();
188 				final Iterator<Integer> xe2 = xvec.iterator();
189 				final Iterator<Integer> ye2 = yvec.iterator();
190 				final Paint p;
191 				if (xvec != recognizer.curxvec)
192 					p = fg2;
193 				else
194 					p = fg1;
195 				drawVec(c, xe2, ye2, p);
196 			} // while xe
197 		}
198 
199 		public boolean onTouch(View view, MotionEvent event) {
200 			final int x, y;
201 			x = (int) event.getX();
202 			y = (int) event.getY();
203 			if (event.getAction() == MotionEvent.ACTION_DOWN) {
204 				recognizer.curxvec = new ArrayList<Integer>();
205 				recognizer.curyvec = new ArrayList<Integer>();
206 				recognizer.xstrokes.add(recognizer.curxvec);
207 				recognizer.ystrokes.add(recognizer.curyvec);
208 				recognizer.curxvec.add(x);
209 				recognizer.curyvec.add(y);
210 				updateStrokes();
211 			} else if (event.getAction() == MotionEvent.ACTION_MOVE) {
212 				recognizer.curxvec.add(x);
213 				recognizer.curyvec.add(y);
214 			}
215 			invalidate();
216 			return true;
217 		}
218 
219 		private void updateStrokes() {
220 			((TextView) ((Activity) getContext()).findViewById(textViewStrokes)).setText(AedictApp.format(R.string.strokes, recognizer.xstrokes.size()));
221 		}
222 
223 		private void drawVec(Canvas g, Iterator<Integer> xe2, Iterator<Integer> ye2, final Paint p) {
224 			int lastx, lasty;
225 			lastx = -1;
226 			lasty = -1;
227 			while (xe2.hasNext()) {
228 				int x, y;
229 				x = xe2.next();
230 				y = ye2.next();
231 				if (lastx != -1)
232 					g.drawLine(lastx, lasty, x, y, p);
233 				lastx = x;
234 				lasty = y;
235 			} // while xe2
236 		}
237 	}
238 
239 	@Override
240 	protected void onResume() {
241 		super.onResume();
242 		update();
243 	}
244 
245 	private void update() {
246 		final ViewGroup buttonList = (ViewGroup) findViewById(R.id.kanjiButtonBar);
247 		buttonList.setVisibility(state.isEmpty() ? View.GONE : View.VISIBLE);
248 		buttonList.removeAllViews();
249 		for (int i = 0; i < state.kanjis.size(); i++) {
250 			final Button b = new Button(this);
251 			final int index = i;
252 			final String kanjiList = state.kanjis.get(i);
253 			b.setOnClickListener(new View.OnClickListener() {
254 
255 				public void onClick(View v) {
256 					final AlertDialog.Builder builder = new AlertDialog.Builder(KanjiDrawActivity.this);
257 					final CharSequence[] items = new CharSequence[kanjiList.length() + 1];
258 					items[0] = getString(R.string.delete);
259 					for (int i = 0; i < kanjiList.length(); i++) {
260 						items[i + 1] = Character.toString(kanjiList.charAt(i));
261 					}
262 					builder.setItems(items, new DialogInterface.OnClickListener() {
263 
264 						public void onClick(DialogInterface dialog, int which) {
265 							if (which == 0) {
266 								state.kanjis.remove(index);
267 								state.selected.remove(index);
268 							} else {
269 								state.selected.set(index, kanjiList.charAt(which - 1));
270 							}
271 							update();
272 						}
273 					});
274 					builder.setTitle(R.string.kanjiSearchMethod);
275 					final AlertDialog dlg = builder.create();
276 					dlg.setOwnerActivity(KanjiDrawActivity.this);
277 					dlg.show();
278 				}
279 			});
280 			b.setText(state.selected.get(i).toString());
281 			buttonList.addView(b);
282 		}
283 	}
284 }