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 }
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 }
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 }