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.activity.search;
20  
21  import java.io.Serializable;
22  import java.util.HashMap;
23  import java.util.List;
24  import java.util.Map;
25  import java.util.concurrent.CopyOnWriteArrayList;
26  
27  import sk.baka.ambient.AmbientApplication;
28  import sk.baka.ambient.R;
29  import sk.baka.ambient.activity.main.MainActivity;
30  import sk.baka.ambient.collection.TrackMetadataBean;
31  import sk.baka.ambient.collection.local.MediaStoreCollection;
32  import sk.baka.ambient.commons.MiscUtils;
33  import sk.baka.ambient.views.LongOperationExecutor;
34  import android.app.Activity;
35  import android.app.SearchManager;
36  import android.content.ContentResolver;
37  import android.content.Intent;
38  import android.os.Bundle;
39  import android.view.View;
40  import android.view.View.OnClickListener;
41  import android.widget.TextView;
42  
43  /***
44   * The search activity, searches for miscellaneous data.
45   * 
46   * @author Martin Vysny
47   */
48  public final class SearchActivity extends Activity {
49  	/***
50  	 * When an intent is sent to the main activity, this key will hold the
51  	 * <code>{@link List}&lt;{@link TrackMetadataBean}&gt;</code> of results.
52  	 */
53  	public static final String INTENTKEY_MODEL = "MODEL";
54  
55  	/***
56  	 * A query string.
57  	 */
58  	public static final String INTENTKEY_QUERY = "QUERY";
59  
60  	/***
61  	 * A response string.
62  	 */
63  	public static final String INTENTKEY_STRING = "STRING";
64  
65  	/***
66  	 * Holds type of model data - the {@link SearchType} enum.
67  	 */
68  	public static final String INTENTKEY_RESULTTYPE = "RESULTTYPE";
69  
70  	private final AmbientApplication app = AmbientApplication.getInstance();
71  
72  	/***
73  	 * Maps button id to the search engine instance.
74  	 */
75  	private final Map<Integer, ISearchEngine> engines = new HashMap<Integer, ISearchEngine>();
76  
77  	@Override
78  	protected void onCreate(Bundle icicle) {
79  		super.onCreate(icicle);
80  		setContentView(R.layout.search);
81  		engines.put(R.id.searchEngineSkreemR, new SkreemrEngine());
82  //		engines.put(R.id.searchEngineCollection, new LibraryEngine());
83  		final ContentResolver resolver=getContentResolver();
84  		engines.put(R.id.searchEngineCollection, new CollectionEngine(new MediaStoreCollection(resolver)));
85  		engines.put(R.id.searchEngineStoredPlaylistNames, new PlaylistEngine());
86  		engines.put(R.id.searchEngineShoutcastGenres, new ShoutcastEngine());
87  		engines.put(R.id.searchEngineMagnatune, new MagnatuneEngine());
88  		engines.put(R.id.searchEngineAmpache, new AmpacheEngine());
89  		findViewById(R.id.searchTrackPadded).setPadding(5, 2, 5, 3);
90  		findViewById(R.id.searchTrackCaptionPadded).setPadding(5, 4, 5, 0);
91  		findViewById(R.id.searchClose).setOnClickListener(listener);
92  		findViewById(R.id.searchEmbedToPlayer).setOnClickListener(listener);
93  		for (final Integer buttonId : engines.keySet()) {
94  			findViewById(buttonId).setOnClickListener(listener);
95  		}
96  		final Intent queryIntent = getIntent();
97  		processIntent(queryIntent);
98  	}
99  
100 	@Override
101 	protected void onDestroy() {
102 		passivateEngine();
103 		super.onDestroy();
104 	}
105 
106 	private void processIntent(final Intent queryIntent) {
107 		setSelectorVisibility(true);
108 		passivateEngine();
109 		final String queryAction = queryIntent.getAction();
110 		if (!Intent.ACTION_SEARCH.equals(queryAction)) {
111 			throw new IllegalArgumentException("Intended for search only");
112 		}
113 		queryString = queryIntent.getStringExtra(SearchManager.QUERY);
114 		final TextView caption = (TextView) findViewById(R.id.searchPickEngineText);
115 		caption.setText(getString(R.string.searchPickEngine, queryString));
116 	}
117 
118 	@Override
119 	protected void onNewIntent(Intent intent) {
120 		processIntent(intent);
121 	}
122 
123 	private void setSelectorVisibility(boolean visible) {
124 		findViewById(R.id.searchPickEngine).setVisibility(
125 				visible ? View.VISIBLE : View.GONE);
126 		findViewById(R.id.searchResultList).setVisibility(
127 				visible ? View.GONE : View.VISIBLE);
128 		for (final Integer buttonId : engines.keySet()) {
129 			findViewById(buttonId).setEnabled(
130 					engines.get(buttonId).canPerformSearch());
131 		}
132 	}
133 
134 	private String queryString;
135 
136 	private final OnClickListener listener = new OnClickListener() {
137 		public void onClick(View arg0) {
138 			if (engines.containsKey(arg0.getId())) {
139 				activateSearchEngine(engines.get(arg0.getId()));
140 				return;
141 			}
142 			switch (arg0.getId()) {
143 			case R.id.searchClose:
144 				passivateEngine();
145 				finish();
146 				break;
147 			case R.id.searchEmbedToPlayer:
148 				final SearchType type = engine.getType();
149 				passivateEngine();
150 				finish();
151 				final Intent intent = new Intent(SearchActivity.this,
152 						MainActivity.class);
153 				intent.putExtra(INTENTKEY_QUERY, queryString);
154 				intent.putExtra(INTENTKEY_MODEL, MiscUtils
155 						.serializeToBytes((Serializable) searchResult));
156 				intent.putExtra(INTENTKEY_RESULTTYPE, MiscUtils
157 						.serializeToBytes(type));
158 				startActivity(intent);
159 				break;
160 			}
161 		}
162 	};
163 
164 	/***
165 	 * A string item was clicked. Close the activity and fire intent.
166 	 * 
167 	 * @param item
168 	 *            the item being clicked.
169 	 */
170 	void stringItemClicked(final String item) {
171 		final SearchType type = engine.getType();
172 		passivateEngine();
173 		finish();
174 		final Intent intent = new Intent(this, MainActivity.class);
175 		intent.putExtra(INTENTKEY_QUERY, queryString);
176 		intent.putExtra(INTENTKEY_STRING, item);
177 		intent.putExtra(INTENTKEY_RESULTTYPE, MiscUtils.serializeToBytes(type));
178 		startActivity(intent);
179 	}
180 
181 	/***
182 	 * Returns stored tracks from the bundle.
183 	 * 
184 	 * @param bundle
185 	 *            the bundle.
186 	 * @return stored tracks.
187 	 */
188 	@SuppressWarnings("unchecked")
189 	public static List<TrackMetadataBean> getModel(final Bundle bundle) {
190 		final byte[] serializedTracks = (byte[]) bundle
191 				.getSerializable(INTENTKEY_MODEL);
192 		if (serializedTracks == null)
193 			throw new IllegalArgumentException(
194 					"The bundle does not contain model");
195 		final List<TrackMetadataBean> tracks = (List<TrackMetadataBean>) MiscUtils
196 				.deserialize(serializedTracks);
197 		return tracks;
198 	}
199 
200 	/***
201 	 * This engine is active and performing the queries.
202 	 */
203 	private ISearchEngine engine = null;
204 
205 	private void passivateEngine() {
206 		if (engine != null) {
207 			engine.passivate();
208 			engine = null;
209 		}
210 	}
211 
212 	/***
213 	 * The result of the search.
214 	 */
215 	private final List<Object> searchResult = new CopyOnWriteArrayList<Object>();
216 
217 	/***
218 	 * Activates given search engine and switches to the list view.
219 	 * 
220 	 * @param engine
221 	 *            the engine to activate.
222 	 */
223 	private void activateSearchEngine(final ISearchEngine engine) {
224 		passivateEngine();
225 		if (!engine.canPerformSearch()) {
226 			return;
227 		}
228 		if (!engine.canSearchOffline()
229 				&& !app.getStateHandler().getStartupState().online) {
230 			app.error(SearchActivity.class, false,
231 					getString(R.string.inOfflineMode), null);
232 			return;
233 		}
234 		this.engine = engine;
235 		engine.init(this, R.id.searchResults);
236 		final Runnable searchProc = new Runnable() {
237 			public void run() {
238 				try {
239 					final List<? extends Object> result = engine
240 							.performSearch(queryString);
241 					if (Thread.currentThread().isInterrupted()) {
242 						return;
243 					}
244 					searchResult.clear();
245 					if (result != null) {
246 						searchResult.addAll(result);
247 					}
248 					AmbientApplication.getHandler().post(new Runnable() {
249 						public void run() {
250 							onSuccesfullSearch();
251 						}
252 					});
253 				} catch (final Exception e) {
254 					throw new RuntimeException(e);
255 				}
256 			}
257 		};
258 		final LongOperationExecutor executor = new LongOperationExecutor(this,
259 				R.string.searching, R.string.searchingFailed, searchProc);
260 		executor.start();
261 	}
262 
263 	private void onSuccesfullSearch() {
264 		engine.update(searchResult);
265 		findViewById(R.id.searchEmbedToPlayer).setVisibility(
266 				engine.getType().returnsTrackList ? View.VISIBLE : View.GONE);
267 		setSelectorVisibility(false);
268 	}
269 
270 	/***
271 	 * Returns stored query from the bundle.
272 	 * 
273 	 * @param bundle
274 	 *            the bundle.
275 	 * @return stored tracks.
276 	 */
277 	public static String getQuery(final Bundle bundle) {
278 		final String result = bundle.getString(INTENTKEY_QUERY);
279 		if (result == null) {
280 			throw new IllegalArgumentException("Null query");
281 		}
282 		return result;
283 	}
284 
285 	/***
286 	 * Returns stored query from the bundle.
287 	 * 
288 	 * @param bundle
289 	 *            the bundle.
290 	 * @return stored tracks.
291 	 */
292 	public static SearchType getType(final Bundle bundle) {
293 		final byte[] result = (byte[]) bundle
294 				.getSerializable(INTENTKEY_RESULTTYPE);
295 		if (result == null) {
296 			throw new IllegalArgumentException("Null query");
297 		}
298 		return (SearchType) MiscUtils.deserialize(result);
299 	}
300 }