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;
20
21 import java.util.Collection;
22 import java.util.HashSet;
23 import java.util.Map;
24 import java.util.concurrent.Callable;
25 import java.util.concurrent.ConcurrentHashMap;
26
27 import sk.baka.ambient.collection.ICollection;
28 import sk.baka.ambient.collection.TrackOriginEnum;
29 import sk.baka.ambient.playerservice.PlayerService;
30 import sk.baka.ambient.playerservice.PlayerStateEnum;
31 import sk.baka.ambient.playlist.PlaylistItem;
32 import android.media.MediaPlayer;
33
34 /***
35 * Handles errors received by {@link MediaPlayer} and tries to recover from
36 * them.
37 *
38 * @author Martin Vysny
39 */
40 final class PlaylistPlayerErrorHandler {
41 private final PlaylistPlayer owner;
42 private final AmbientApplication app;
43
44 /***
45 * Creates new error handler.
46 *
47 * @param owner
48 * owner player.
49 */
50 PlaylistPlayerErrorHandler(final PlaylistPlayer owner) {
51 super();
52 this.owner = owner;
53 app = AmbientApplication.getInstance();
54 }
55
56 private volatile boolean isFixingLocations = false;
57 private volatile boolean afterFixingLocations = false;
58
59 /***
60 * Handles the playback error.
61 *
62 * @param error
63 * the error message from the player.
64 * @param missing
65 * if <code>true</code> then the error occurred because the file
66 * is missing.
67 * @param origin
68 * the track origin
69 */
70 void handleError(final String error, final boolean missing,
71 TrackOriginEnum origin) {
72 getBusInvocator().playbackStateChanged(PlayerStateEnum.Stopped);
73 final ICollection resolver = app.getProvider(origin);
74 if (resolver != null && !isFixingLocations && !afterFixingLocations) {
75 fixLocations(resolver, origin);
76 return;
77 }
78 cancelLocationResolving();
79 final String text = app.getString(R.string.error_playing_file) + " "
80 + error;
81 app.error(PlayerService.class, true, text, null);
82 }
83
84 /***
85 * A track playback was initiated successfully.
86 */
87 void handleSuccess() {
88 cancelLocationResolving();
89 }
90
91 private void fixLocations(ICollection resolver, final TrackOriginEnum origin) {
92 isFixingLocations = true;
93 final Collection<String> locations = new HashSet<String>();
94 for (final PlaylistItem item : owner.getPlayItems()) {
95 if (item.getTrack().getOrigin() == origin) {
96 locations.add(item.getTrack().getLocation());
97 }
98 }
99 if (locations.isEmpty()) {
100 isFixingLocations = false;
101 return;
102 }
103 final boolean scheduled = app.getBackgroundTasks().schedule(
104 new LocationFix(resolver, locations), LocationFix.class,
105 !resolver.isLocal(),
106 app.getString(R.string.refreshAmpacheSession)) != null;
107 if (!scheduled) {
108 isFixingLocations = false;
109 }
110 }
111
112 private final class LocationFix implements Callable<Void> {
113 private final Collection<String> locations;
114 private final ICollection resolver;
115
116 public LocationFix(final ICollection resolver,
117 final Collection<String> locations) {
118 this.resolver = resolver;
119 this.locations = locations;
120 }
121
122 public Void call() throws Exception {
123 final Map<String, String> fixedLocations;
124 try {
125 fixedLocations = new ConcurrentHashMap<String, String>(resolver
126 .fixLocations(locations));
127 } catch (Exception e) {
128 isFixingLocations = false;
129 throw e;
130 }
131 if (fixedLocations.isEmpty()) {
132 isFixingLocations = false;
133 return null;
134 }
135 AmbientApplication.getHandler().post(new Runnable() {
136 public void run() {
137 if (isFixingLocations) {
138 isFixingLocations = false;
139 afterFixingLocations = true;
140 owner.replaceLocations(fixedLocations);
141 owner.psPlay(owner.getCurrentlyPlaying(), 0);
142 }
143 }
144 });
145 return null;
146 }
147 }
148
149 private IPlaylistPlayerListener getBusInvocator() {
150 return app.getBus().getInvocator(IPlaylistPlayerListener.class, true);
151 }
152
153 /***
154 * Cancels all location resolving activities.
155 */
156 void cancelLocationResolving() {
157 if (isFixingLocations) {
158 isFixingLocations = false;
159 app.getBackgroundTasks().cancel(LocationFix.class);
160 }
161 afterFixingLocations = false;
162 }
163
164 /***
165 * Closes this handler and frees all resources.
166 */
167 public void close() {
168 cancelLocationResolving();
169 }
170 }