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