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