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