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.activity.main;
20  
21  import java.util.Arrays;
22  import java.util.List;
23  import java.util.Map;
24  
25  import sk.baka.ambient.ActionsEnum;
26  import sk.baka.ambient.AmbientApplication;
27  import sk.baka.ambient.AppState;
28  import sk.baka.ambient.ConfigurationBean;
29  import sk.baka.ambient.IApplicationListener;
30  import sk.baka.ambient.R;
31  import sk.baka.ambient.ZoomEnum;
32  import sk.baka.ambient.activity.AmpacheClientActivity;
33  import sk.baka.ambient.activity.AmpacheClientBean;
34  import sk.baka.ambient.activity.main.cb.AbstractCollectionController;
35  import sk.baka.ambient.activity.main.cb.CategoryManager;
36  import sk.baka.ambient.collection.CategoryItem;
37  import sk.baka.ambient.collection.ICollection;
38  import sk.baka.ambient.collection.TrackMetadataBean;
39  import sk.baka.ambient.collection.TrackOriginEnum;
40  import sk.baka.ambient.collection.ampache.AmpacheClient;
41  import sk.baka.ambient.collection.ampache.AmpacheCollection;
42  import sk.baka.ambient.collection.ampache.AmpacheServer;
43  import sk.baka.ambient.collection.local.MediaStoreCollection;
44  import sk.baka.ambient.collection.sync.CollectionSynchronizer;
45  import sk.baka.ambient.collection.sync.SyncUtils;
46  import sk.baka.ambient.commons.IOUtils;
47  import sk.baka.ambient.commons.Interval;
48  import sk.baka.ambient.commons.MiscUtils;
49  import sk.baka.ambient.views.gesturelist.GesturesListView;
50  import android.app.Activity;
51  import android.content.Context;
52  import android.content.Intent;
53  import android.net.wifi.WifiInfo;
54  import android.net.wifi.WifiManager;
55  import android.view.View;
56  import android.widget.TextView;
57  
58  /***
59   * Browses the Ampache server and optionally allows a synchronization of tracks.
60   * 
61   * @author Martin Vysny
62   */
63  public final class AmpacheController extends AbstractCollectionController
64  		implements IApplicationListener {
65  	/***
66  	 * The Embedded Ampache server.
67  	 */
68  	final private AmpacheServer server;
69  
70  	private final List<ActionsEnum> actions = Arrays.asList(ActionsEnum.Back,
71  			ActionsEnum.CollectionYear, ActionsEnum.AmpacheSynchronize,
72  			ActionsEnum.AmpacheDownloadSelected,
73  			ActionsEnum.AmpacheStartServer, ActionsEnum.AmpacheConfigureClient);
74  
75  	/***
76  	 * Creates new ampache controller.
77  	 * 
78  	 * @param mainActivity
79  	 *            the owning activity.
80  	 * @param playlistView
81  	 *            the playlist view.
82  	 */
83  	public AmpacheController(final Activity mainActivity,
84  			final GesturesListView playlistView) {
85  		super(R.id.ampacheBrowser, R.id.ampacheList, mainActivity,
86  				playlistView, new AmpacheCollection(new AmpacheClient(null),
87  						null, null), R.id.ampachePath);
88  		app.registerProvider(TrackOriginEnum.Ampache, collection);
89  		final ICollection serverBackend = new MediaStoreCollection(mainActivity
90  				.getContentResolver());
91  		server = new AmpacheServer(
92  				app.getStateHandler().getConfig().ampacheEmbeddedPassword,
93  				serverBackend);
94  		ampacheClientChanged();
95  		initButtonBar(R.id.ampacheButtons, actions);
96  	}
97  
98  	private void startEmbedded() {
99  		try {
100 			server.start(AmpacheServer.PORT, null);
101 			actions.set(4, ActionsEnum.AmpacheStopServer);
102 			initButtonBar(R.id.ampacheButtons, actions);
103 			checkButton(R.id.ampacheButtons, 4, true);
104 			setupWifiPolling(true);
105 		} catch (final Exception e) {
106 			app.error(AmpacheController.class, true, "Server failed to start",
107 					e);
108 		}
109 	}
110 
111 	private void stopEmbedded() {
112 		server.stop();
113 		actions.set(4, ActionsEnum.AmpacheStartServer);
114 		initButtonBar(R.id.ampacheButtons, actions);
115 		checkButton(R.id.ampacheButtons, 4, false);
116 		setupWifiPolling(false);
117 	}
118 
119 	@Override
120 	protected void onAction(ActionsEnum action) {
121 		if (action == ActionsEnum.AmpacheSynchronize) {
122 			downloadTracks(true);
123 		} else if (action == ActionsEnum.AmpacheDownloadSelected) {
124 			if (canComputeItems()) {
125 				downloadTracks(false);
126 			}
127 		} else if (action == ActionsEnum.AmpacheConfigureClient) {
128 			mainActivity.startActivity(new Intent(mainActivity,
129 					AmpacheClientActivity.class));
130 		} else if (action == ActionsEnum.AmpacheStartServer) {
131 			startEmbedded();
132 		} else if (action == ActionsEnum.AmpacheStopServer) {
133 			stopEmbedded();
134 		}
135 		super.onAction(action);
136 	}
137 
138 	private void downloadTracks(final boolean entireCollection) {
139 		final ICollection localColl = new MediaStoreCollection(mainActivity
140 				.getContentResolver());
141 		final CollectionSynchronizer synchronizer = new CollectionSynchronizer(
142 				mainActivity, localColl, collection, SyncUtils
143 						.newPathFormatter(), "/sdcard", app
144 						.getBackgroundTasks());
145 		if (!entireCollection) {
146 			Interval highlight = listView.getHighlight();
147 			if (highlight.isEmpty()) {
148 				highlight = listView.getModel().getAllItems();
149 			}
150 			if (isShowingTracks()) {
151 				final List<TrackMetadataBean> tracks = computeTracks(highlight);
152 				synchronizer.setTracksToDownload(tracks);
153 			} else {
154 				final List<CategoryItem> categories = ((CategoryManager) getCurrentManager())
155 						.getShownCategories();
156 				synchronizer.setCategoriesToDownload(categories.subList(
157 						highlight.start, highlight.end + 1));
158 			}
159 		}
160 		app.getBackgroundTasks().schedule(synchronizer,
161 				CollectionSynchronizer.class, true,
162 				app.getString(R.string.synchronizingCollection));
163 	}
164 
165 	/***
166 	 * Invoked when the Ampache client configuration changes.
167 	 */
168 	void ampacheClientChanged() {
169 		final AmpacheClientBean bean = app.getStateHandler().getStartupState().ampacheClient;
170 		final AmpacheCollection ampache = (AmpacheCollection) collection;
171 		ampache.reset(bean == null ? new AmpacheClientBean() : bean);
172 		reset();
173 	}
174 
175 	@Override
176 	public void destroy() {
177 		server.stop();
178 		setupWifiPolling(false);
179 		super.destroy();
180 	}
181 
182 	private final Runnable wifiStatus = new Runnable() {
183 		public void run() {
184 			AmbientApplication.getHandler().postDelayed(wifiStatus, 1000);
185 			final WifiManager manager = (WifiManager) mainActivity
186 					.getSystemService(Context.WIFI_SERVICE);
187 			final TextView status = (TextView) mainView
188 					.findViewById(R.id.ampacheServerWifiStatus);
189 			if (!manager.isWifiEnabled()) {
190 				status.setText(MiscUtils.format(R.string.wifiOff,
191 						AmpacheServer.PORT));
192 			} else {
193 				final WifiInfo info = manager.getConnectionInfo();
194 				if (info == null || info.getSSID() == null) {
195 					status.setText(MiscUtils.format(R.string.wifiNotConnected,
196 							AmpacheServer.PORT));
197 				} else {
198 					status.setText(MiscUtils.format(R.string.wifiConnected,
199 							AmpacheServer.PORT, info.getSSID(), IOUtils
200 									.formatIP(info.getIpAddress())));
201 				}
202 			}
203 		}
204 	};
205 
206 	private void setupWifiPolling(boolean enable) {
207 		AmbientApplication.getHandler().removeCallbacks(wifiStatus);
208 		mainView.findViewById(R.id.ampacheServerWifiStatus).setVisibility(
209 				enable ? View.VISIBLE : View.GONE);
210 		if (enable) {
211 			AmbientApplication.getHandler().post(wifiStatus);
212 		}
213 	}
214 
215 	public void clipboardChanged() {
216 		// do nothing
217 	}
218 
219 	public void configChanged(ConfigurationBean config) {
220 		server.reset(config.ampacheEmbeddedPassword);
221 	}
222 
223 	public void offline(boolean offline) {
224 		if (offline) {
225 			stopEmbedded();
226 		}
227 		mainView.findViewById(R.id.ampacheBrowserControls).setVisibility(
228 				offline ? View.GONE : View.VISIBLE);
229 		mainView.findViewById(R.id.ampacheOffline).setVisibility(
230 				offline ? View.VISIBLE : View.GONE);
231 	}
232 
233 	public void stateChanged(AppState state) {
234 		ampacheClientChanged();
235 	}
236 
237 	@Override
238 	protected void visibilityChanged(boolean visible) {
239 		if (!visible) {
240 			return;
241 		}
242 		final boolean offline = app.isOffline();
243 		offline(offline);
244 	}
245 
246 	@Override
247 	protected void performZoom(final Map<ZoomEnum, Integer> zoom) {
248 		super.performZoom(zoom);
249 		initButtonBar(R.id.ampacheButtons, actions);
250 	}
251 
252 	@Override
253 	protected void yearChanged(boolean year) {
254 		checkButton(R.id.ampacheButtons, 1, year);
255 	}
256 }