View Javadoc

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 		// find such an item in the list view
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 }