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