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
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
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
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
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
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
304 }
305 }