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.IApplicationListener;
26  import sk.baka.ambient.R;
27  import sk.baka.ambient.collection.TrackMetadataBean;
28  import sk.baka.ambient.commons.Interval;
29  import sk.baka.ambient.commons.TagFormatter;
30  import sk.baka.ambient.views.gesturelist.GesturesListView;
31  import sk.baka.ambient.views.gesturelist.MutableListAdapter;
32  import android.app.Activity;
33  import android.graphics.Typeface;
34  import android.text.SpannableStringBuilder;
35  import android.text.Spanned;
36  import android.text.style.StyleSpan;
37  import android.view.View;
38  import android.widget.ImageView;
39  import android.widget.TextView;
40  
41  /***
42   * Groups functionality for a controller managing a playlist.
43   * 
44   * @author Martin Vysny
45   */
46  public abstract class AbstractPlaylistController extends AbstractListController
47  		implements IApplicationListener {
48  
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 String eop = mainActivity.getString(R.string.eop);
159 
160 	private final String playlistEmpty = mainActivity
161 			.getString(R.string.playlist_empty);
162 
163 	public final void update(GesturesListView listView, View itemView,
164 			int index, Object model) {
165 		final boolean isEOP = model == MutableListAdapter.EOP_MODEL_MARKER;
166 		final boolean currentlyPlayed = isEOP ? false
167 				: isCurrentlyPlayedTrack(index);
168 		if (isEOP) {
169 			itemView.setBackgroundColor(0);
170 		} else {
171 			// draw selections
172 			final boolean highlighted = listView.getHighlight().contains(index);
173 			final int bgColor;
174 			if (currentlyPlayed && highlighted) {
175 				bgColor = currentHighlightedColor;
176 			} else if (currentlyPlayed) {
177 				bgColor = currentTrackColor;
178 			} else if (highlighted) {
179 				bgColor = highlightColor;
180 			} else {
181 				bgColor = 0;
182 			}
183 			itemView.setBackgroundColor(bgColor);
184 		}
185 		// draw the track meta info
186 		final TextView text = (TextView) itemView
187 				.findViewById(R.id.playlist_item_text);
188 		final String caption;
189 		if (isEOP) {
190 			caption = listView.getCount() > 1 ? eop : playlistEmpty;
191 		} else {
192 			caption = (String) model;
193 		}
194 		if (currentlyPlayed) {
195 			final SpannableStringBuilder b = new SpannableStringBuilder(caption);
196 			b.setSpan(new StyleSpan(Typeface.ITALIC), 0, caption.length(),
197 					Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
198 			text.setText(b);
199 		} else {
200 			text.setText(caption);
201 		}
202 		text.setTextColor(isEOP ? textTranslucentColor : textColor);
203 		// update the queue info
204 		final int queue = isEOP ? 0 : getQueueNumber(index);
205 		final TextView queueView = (TextView) itemView
206 				.findViewById(R.id.playlist_item_queue);
207 		queueView.setVisibility(queue == 0 ? View.GONE : View.VISIBLE);
208 		if (queue != 0) {
209 			queueView.setText(String.valueOf(queue));
210 		}
211 		// update the track icon
212 		final ImageView trackIcon = (ImageView) itemView
213 				.findViewById(R.id.playlist_item_trackicon);
214 		final TrackMetadataBean track = isEOP ? null : getTrack(index);
215 		if (isEOP || track.isLocal()) {
216 			trackIcon.setVisibility(View.GONE);
217 		} else {
218 			trackIcon.setVisibility(View.VISIBLE);
219 			switch (track.getOrigin()) {
220 			case Magnatune:
221 				trackIcon.setImageResource(R.drawable.trackicon_magnatune);
222 				break;
223 			case Shoutcast:
224 				trackIcon.setImageResource(R.drawable.trackicon_shoutcast);
225 				break;
226 			case SkreemR:
227 			case Ampache:
228 				trackIcon.setImageResource(R.drawable.trackicon_http);
229 				break;
230 			case LocalFs:
231 				// should be handled
232 			}
233 		}
234 	}
235 
236 	/***
237 	 * Returns queue status of given track.
238 	 * 
239 	 * @param index
240 	 *            the track index
241 	 * @return queue number or 0 if the track is not queued.
242 	 */
243 	protected abstract int getQueueNumber(int index);
244 
245 	/***
246 	 * Checks if the track at given index is currently being played.
247 	 * 
248 	 * @param index
249 	 *            the track index
250 	 * @return <code>true</code> if it is being played, <code>false</code>
251 	 *         otherwise.
252 	 */
253 	protected abstract boolean isCurrentlyPlayedTrack(int index);
254 
255 	@Override
256 	public final boolean canComputeItems() {
257 		return true;
258 	}
259 
260 	@Override
261 	public boolean isComputeTracksLong(Interval interval) {
262 		return false;
263 	}
264 
265 	@Override
266 	public boolean isComputeTracksOnlineOp(Interval interval) {
267 		return false;
268 	}
269 
270 	@Override
271 	public boolean isReadOnly() {
272 		return false;
273 	}
274 
275 	@Override
276 	public abstract String getHint(Interval highlight);
277 
278 	public void configChanged(ConfigurationBean config) {
279 		try {
280 			formatter = new TagFormatter(
281 					app.getStateHandler().getConfig().playlistFormat);
282 		} catch (ParseException e) {
283 			throw new RuntimeException(e);
284 		}
285 		update(null);
286 	}
287 
288 	@Override
289 	public List<TrackMetadataBean> computeTracks(Interval highlight) {
290 		final List<TrackMetadataBean> result = new ArrayList<TrackMetadataBean>(
291 				highlight.length);
292 		for (int i = highlight.start; i <= highlight.end; i++) {
293 			result.add(getTrack(i));
294 		}
295 		return result;
296 	}
297 
298 	public void clipboardChanged() {
299 		listView.clipboardChanged();
300 	}
301 
302 	public void offline(boolean offline) {
303 		// by default do nothing
304 	}
305 }