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  package sk.baka.ambient.activity.main;
19  
20  import java.text.ParseException;
21  import java.util.ArrayList;
22  import java.util.List;
23  
24  import sk.baka.ambient.ConfigurationBean;
25  import sk.baka.ambient.I18n;
26  import sk.baka.ambient.IApplicationListener;
27  import sk.baka.ambient.R;
28  import sk.baka.ambient.collection.TrackMetadataBean;
29  import sk.baka.ambient.commons.Interval;
30  import sk.baka.ambient.commons.TagFormatter;
31  import sk.baka.ambient.views.gesturelist.GesturesListView;
32  import sk.baka.ambient.views.gesturelist.MutableListAdapter;
33  import android.app.Activity;
34  import android.graphics.Typeface;
35  import android.text.SpannableStringBuilder;
36  import android.text.Spanned;
37  import android.text.style.StyleSpan;
38  import android.view.View;
39  import android.widget.ImageView;
40  import android.widget.TextView;
41  
42  /***
43   * Groups functionality for a controller managing a playlist.
44   * 
45   * @author Martin Vysny
46   */
47  public abstract class AbstractPlaylistController extends AbstractListController
48  		implements IApplicationListener {
49  	/***
50  	 * The playlist view id.
51  	 */
52  	public static final int PLAYLIST_VIEW_ID = android.R.id.list;
53  
54  	/***
55  	 * <p>
56  	 * Creates new controller.
57  	 * </p>
58  	 * <p>
59  	 * The contract: the extending class MUST invoke the {@link #initialize()}
60  	 * after it is done initializing.
61  	 * </p>
62  	 * 
63  	 * @param mainViewId
64  	 *            the view whose visibility is controlled.
65  	 * @param listViewId
66  	 *            the id of the {@link GesturesListView}
67  	 * @param mainActivity
68  	 */
69  	public AbstractPlaylistController(final int mainViewId, int listViewId,
70  			Activity mainActivity) {
71  		super(mainViewId, listViewId, mainActivity);
72  		try {
73  			formatter = new TagFormatter(
74  					app.getStateHandler().getConfig().playlistFormat);
75  		} catch (ParseException e) {
76  			throw new RuntimeException(e);
77  		}
78  		listView.dragDropViews.clear();
79  		listView.dragDropViews.add(listView);
80  		model = listView.getModel().getModel();
81  	}
82  
83  	/***
84  	 * Overriding classes must invoke this method at the end of their
85  	 * constructors.
86  	 */
87  	protected final void initialize() {
88  		update(Interval.EMPTY);
89  	}
90  
91  	/***
92  	 * The contract: when overriding this method, be sure to call this super
93  	 * method after you are done deleting items. The adapter must be
94  	 * invalidated, to prevent drawing old data.
95  	 * 
96  	 * @param remove
97  	 *            the interval to remove
98  	 */
99  	@Override
100 	public void removeItems(Interval remove) {
101 		update(Interval.EMPTY);
102 	}
103 
104 	/***
105 	 * Formats playlist items.
106 	 */
107 	private TagFormatter formatter;
108 
109 	/***
110 	 * The model, containing Strings - formatted track names.
111 	 */
112 	private final List<Object> model;
113 
114 	/***
115 	 * Retrieves a track from given position.
116 	 * 
117 	 * @param index
118 	 *            the index
119 	 * @return the track instance, never <code>null</code>.
120 	 */
121 	protected abstract TrackMetadataBean getTrack(final int index);
122 
123 	@Override
124 	protected void recomputeListItems() {
125 		final boolean nullEqualFields = app.getStateHandler().getConfig().dontRepeatSameTag;
126 		model.clear();
127 		final int playlistSize = getPlaylistSize();
128 		for (int i = 0; i < playlistSize; i++) {
129 			final TrackMetadataBean row = getTrack(i);
130 			final TrackMetadataBean prevRow = i == 0 ? TrackMetadataBean.EMPTY
131 					: getTrack(i - 1);
132 			final TrackMetadataBean diff = nullEqualFields ? row
133 					.nullsEqualFields(prevRow, true) : row;
134 			final String track = formatter.format(diff);
135 			model.add(track);
136 		}
137 	}
138 
139 	/***
140 	 * Returns the underlying playlist size.
141 	 * 
142 	 * @return the playlist size.
143 	 */
144 	protected abstract int getPlaylistSize();
145 
146 	private final int currentTrackColor = mainActivity.getResources().getColor(
147 			R.color.playlist_currenttrack);
148 
149 	private final int currentHighlightedColor = mainActivity.getResources()
150 			.getColor(R.color.currenttrack_highlight);
151 
152 	private final int textColor = mainActivity.getResources().getColor(
153 			R.color.textcolor);
154 
155 	private final int textTranslucentColor = mainActivity.getResources()
156 			.getColor(R.color.texttranslucent);
157 
158 	private final int historyTrackColor = mainActivity.getResources().getColor(
159 			R.color.dynamicplaylist_history);
160 	
161 	private final String eop = mainActivity.getString(R.string.eop);
162 
163 	private final String playlistEmpty = mainActivity
164 			.getString(R.string.playlist_empty);
165 
166 	public final void update(GesturesListView listView, View itemView,
167 			int index, Object model) {
168 		final boolean isEOP = model == MutableListAdapter.EOP_MODEL_MARKER;
169 		final boolean currentlyPlayed = isEOP ? false
170 				: isCurrentlyPlayedTrack(index);
171 		final boolean isHistoryTrack = isEOP ? false : isHistoryTrack(index);
172 		if (isEOP) {
173 			itemView.setBackgroundColor(0);
174 		} else {
175 			// draw selections
176 			final boolean highlighted = listView.getHighlight().contains(index);
177 			final int bgColor;
178 			if (currentlyPlayed && highlighted) {
179 				bgColor = currentHighlightedColor;
180 			} else if (currentlyPlayed) {
181 				bgColor = currentTrackColor;
182 			} else if (highlighted) {
183 				bgColor = highlightColor;
184 			} else {
185 				bgColor = 0;
186 			}
187 			itemView.setBackgroundColor(bgColor);
188 		}
189 		// draw the track meta info
190 		final TextView text = (TextView) itemView
191 				.findViewById(R.id.playlist_item_text);
192 		final String caption;
193 		if (isEOP) {
194 			caption = listView.getCount() > 1 ? eop : playlistEmpty;
195 		} else {
196 			caption = (String) model;
197 		}
198 		if (currentlyPlayed) {
199 			final SpannableStringBuilder b = new SpannableStringBuilder(caption);
200 			b.setSpan(new StyleSpan(Typeface.ITALIC), 0, caption.length(),
201 					Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
202 			text.setText(b);
203 		} else {
204 			text.setText(caption);
205 		}
206 		text.setTextColor(isEOP ? textTranslucentColor
207 				: isHistoryTrack ? historyTrackColor : textColor);
208 		// update the queue info
209 		final int queue = isEOP ? 0 : getQueueNumber(index);
210 		final TextView queueView = (TextView) itemView
211 				.findViewById(R.id.playlist_item_queue);
212 		queueView.setVisibility(queue == 0 ? View.GONE : View.VISIBLE);
213 		if (queue != 0) {
214 			queueView.setText(String.valueOf(queue));
215 		}
216 		// update the track icon
217 		final ImageView trackIcon = (ImageView) itemView
218 				.findViewById(R.id.playlist_item_trackicon);
219 		final TrackMetadataBean track = isEOP ? null : getTrack(index);
220 		if (isEOP || track.isLocal()) {
221 			trackIcon.setVisibility(View.GONE);
222 		} else {
223 			trackIcon.setVisibility(View.VISIBLE);
224 			switch (track.getOrigin()) {
225 			case Magnatune:
226 				trackIcon.setImageResource(R.drawable.trackicon_magnatune);
227 				break;
228 			case Shoutcast:
229 				trackIcon.setImageResource(R.drawable.trackicon_shoutcast);
230 				break;
231 			case SkreemR:
232 			case Ampache:
233 				trackIcon.setImageResource(R.drawable.trackicon_http);
234 				break;
235 			case LocalFs:
236 				// should be handled
237 			}
238 		}
239 	}
240 
241 	/***
242 	 * Checks if this track is a track from a dynamic playlist history.
243 	 * 
244 	 * @param index
245 	 *            the track index.
246 	 * @return <code>true</code> if history track, <code>false</code> otherwise.
247 	 */
248 	protected abstract boolean isHistoryTrack(int index);
249 
250 	/***
251 	 * Returns queue status of given track.
252 	 * 
253 	 * @param index
254 	 *            the track index
255 	 * @return queue number or 0 if the track is not queued.
256 	 */
257 	protected abstract int getQueueNumber(int index);
258 
259 	/***
260 	 * Checks if the track at given index is currently being played.
261 	 * 
262 	 * @param index
263 	 *            the track index
264 	 * @return <code>true</code> if it is being played, <code>false</code>
265 	 *         otherwise.
266 	 */
267 	protected abstract boolean isCurrentlyPlayedTrack(int index);
268 
269 	@Override
270 	public final boolean canComputeItems() {
271 		return true;
272 	}
273 
274 	@Override
275 	public boolean isComputeTracksLong(Interval interval) {
276 		return false;
277 	}
278 
279 	@Override
280 	public boolean isComputeTracksOnlineOp(Interval interval) {
281 		return false;
282 	}
283 
284 	@Override
285 	public boolean isReadOnly() {
286 		return false;
287 	}
288 
289 	public void configChanged(ConfigurationBean config) {
290 		try {
291 			formatter = new TagFormatter(
292 					app.getStateHandler().getConfig().playlistFormat);
293 		} catch (ParseException e) {
294 			throw new RuntimeException(e);
295 		}
296 		update(null);
297 	}
298 
299 	@Override
300 	public List<TrackMetadataBean> computeTracks(Interval highlight) {
301 		final List<TrackMetadataBean> result = new ArrayList<TrackMetadataBean>(
302 				highlight.length);
303 		for (int i = highlight.start; i <= highlight.end; i++) {
304 			result.add(getTrack(i));
305 		}
306 		return result;
307 	}
308 
309 	public void clipboardChanged() {
310 		listView.clipboardChanged();
311 	}
312 
313 	public void offline(boolean offline) {
314 		// by default do nothing
315 	}
316 
317 	@Override
318 	public String getHint(Interval highlight) {
319 		return I18n.format(I18n.newTracks(highlight.length));
320 	}
321 
322 	public String toString(Object model) {
323 		// should not be called as a list of strings is shown.
324 		throw new Error();
325 	}
326 }