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