1 /***
2 * Ambient - A music player for the Android platform
3 Copyright (C) 2007 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.ambient.views.gesturelist.keypad;
20
21 import java.util.List;
22
23 import sk.baka.ambient.R;
24 import sk.baka.ambient.views.ContainedPopupWindow;
25 import sk.baka.ambient.views.Iconify;
26 import sk.baka.ambient.views.gesturelist.GesturesListView;
27 import android.text.SpannableStringBuilder;
28 import android.view.KeyEvent;
29 import android.widget.AdapterView;
30 import android.widget.TextView;
31
32 /***
33 * Handles search using the DPAD only.
34 *
35 * @author Martin Vysny
36 */
37 public final class KeypadDpadSearch extends AbstractKeypadHandler {
38 /***
39 * Creates new instance
40 *
41 * @param owner
42 * the owner.
43 */
44 public KeypadDpadSearch(final GesturesListView owner) {
45 super(owner);
46 queryWindow = new ContainedPopupWindow(owner,
47 new ContainedPopupWindow.IPlaceable() {
48 public int[] getCoordinates(ContainedPopupWindow popup,
49 CharSequence text, TextView textView) {
50 final int y = owner.getHeight()
51 - (int) textView.getPaint().getTextSize();
52 final int x = (owner.getWidth() - (int) textView
53 .getPaint().measureText(text.toString())) / 2;
54 return new int[] { x, y };
55 }
56 });
57 }
58
59 private char getNext(final char c) {
60 if (c >= 'a' && c <= 'z') {
61 if (c == 'z') {
62 return '0';
63 } else {
64 return (char) (c + 1);
65 }
66 }
67 if (c >= '0' && c <= '9') {
68 if (c == '9') {
69 return 'a';
70 } else {
71 return (char) (c + 1);
72 }
73 }
74 return 'a';
75 }
76
77 private char getPrev(final char c) {
78 if (c >= 'a' && c <= 'z') {
79 if (c == 'a') {
80 return '9';
81 } else {
82 return (char) (c - 1);
83 }
84 }
85 if (c >= '0' && c <= '9') {
86 if (c == '0') {
87 return 'z';
88 } else {
89 return (char) (c - 1);
90 }
91 }
92 return 'a';
93 }
94
95 private final StringBuilder term = new StringBuilder();
96 /***
97 * Displays the search query.
98 */
99 private final ContainedPopupWindow queryWindow;
100 /***
101 * <code>true</code> if entering the search term, <code>false</code> if
102 * searching.
103 */
104 private boolean enteringTerm = true;
105
106 @Override
107 public void start() {
108 owner.setTooltip(R.string.dpadSearch_hint1, this, false);
109 enteringTerm = true;
110 term.delete(0, term.length());
111 term.append('a');
112 update();
113 }
114
115 private void update() {
116 if (enteringTerm) {
117 final SpannableStringBuilder b = new SpannableStringBuilder(term);
118 b.insert(b.length() - 1, "#U#");
119 b.append("#D#");
120 Iconify.iconify(owner.getContext(), b);
121 queryWindow.setText(b);
122 selectFirst(0, 1);
123 } else {
124 queryWindow.setText(term);
125 }
126 }
127
128 @Override
129 public void stop() {
130 term.delete(0, term.length());
131 owner.clearTooltip(this);
132 queryWindow.dismiss();
133 }
134
135 @Override
136 public boolean isActivatedByKey(int keyCode, final KeyEvent event) {
137 return false;
138 }
139
140 @Override
141 public boolean isStarted() {
142 return term.length() > 0;
143 }
144
145 @Override
146 protected boolean onConfirm() {
147 if (enteringTerm) {
148 enteringTerm = false;
149 owner.setTooltip(R.string.dpadSearch_hint2, this, false);
150 update();
151 } else {
152 stop();
153 }
154 return true;
155 }
156
157 @Override
158 protected boolean onDown() {
159 if (enteringTerm) {
160 final int lastChar = term.length() - 1;
161 term.setCharAt(lastChar, getNext(term.charAt(lastChar)));
162 update();
163 } else {
164 selectFirst(owner.getSelectedItemPosition() + 1, 1);
165 }
166 return true;
167 }
168
169 @Override
170 protected boolean onLeft() {
171 if (enteringTerm) {
172 if (term.length() > 1) {
173 term.deleteCharAt(term.length() - 1);
174 update();
175 }
176 }
177 return true;
178 }
179
180 @Override
181 protected boolean onRight() {
182 if (enteringTerm) {
183 term.append('a');
184 update();
185 }
186 return true;
187 }
188
189 @Override
190 protected boolean onUp() {
191 if (enteringTerm) {
192 final int lastChar = term.length() - 1;
193 term.setCharAt(lastChar, getPrev(term.charAt(lastChar)));
194 update();
195 } else {
196 selectFirst(owner.getSelectedItemPosition() - 1, -1);
197 }
198 return true;
199 }
200
201 @Override
202 protected boolean onKey(int keyCode, int count, KeyEvent event) {
203 switch (keyCode) {
204 case KeyEvent.KEYCODE_BACK:
205 if (enteringTerm) {
206 stop();
207 } else {
208 enteringTerm = true;
209 owner.setTooltip(R.string.dpadSearch_hint1, this, false);
210 update();
211 }
212 return true;
213 }
214 return super.onKey(keyCode, count, event);
215 }
216
217 /***
218 * Finds first item which contains given substring.
219 *
220 * @param startIndex
221 * search from this item onwards, including the item.
222 */
223 private void selectFirst(final int startIndex, final int delta) {
224 final String q = term.toString().toLowerCase();
225 final List<String> strModel = owner.getModel().getModelAsString();
226 final int modelLen = strModel.size();
227 int startFrom = startIndex;
228 if ((startFrom < 0) || (startFrom >= modelLen)) {
229 startFrom = delta > 0 ? 0 : modelLen - 1;
230 }
231
232 for (int i = startFrom; (i < modelLen) && (i >= 0); i += delta) {
233 if (strModel.get(i).contains(q)) {
234 owner.setSelection(i);
235 return;
236 }
237 }
238 owner.setSelection(AdapterView.INVALID_POSITION);
239 }
240 }