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 sk.baka.ambient.collection.TrackMetadataBean;
22 import sk.baka.ambient.commons.Interval;
23 import sk.baka.ambient.commons.ThreadUnsafe;
24 import sk.baka.ambient.playerservice.PlayerStateEnum;
25 import sk.baka.ambient.playlist.PlaylistItem;
26 import sk.baka.ambient.playlist.Random;
27 import sk.baka.ambient.playlist.Repeat;
28 import android.content.BroadcastReceiver;
29 import android.content.Context;
30 import android.content.Intent;
31 import android.content.IntentFilter;
32 import android.net.ConnectivityManager;
33 import android.net.NetworkInfo;
34 import android.net.wifi.WifiManager;
35 import android.net.wifi.WifiManager.WifiLock;
36 import android.util.Log;
37
38 /***
39 * Ensures that the WiFi is not turned off while playing an online content.
40 * Moreover, monitors the network status info and fires online/offline events.
41 * Initially, the handler expects the phone to be offline - if not, an event is
42 * fired.
43 * <p/>
44 * Thread-unsafe, should be invoked from the main Handler instance only.
45 *
46 * @author Martin Vysny
47 */
48 @ThreadUnsafe
49 final class NetworkHandler extends BroadcastReceiver implements
50 IPlaylistPlayerListener {
51
52 private final AmbientApplication app;
53
54 /***
55 * Creates new power handler.
56 *
57 * @param app
58 * owner application.
59 */
60 public NetworkHandler(final AmbientApplication app) {
61 this.app = app;
62 app.getBus().addHandler(this);
63 app.registerReceiver(this, new IntentFilter(
64 ConnectivityManager.CONNECTIVITY_ACTION));
65 onNetworkStateChanged(computeOffline());
66 }
67
68 public void playbackStateChanged(PlayerStateEnum state) {
69 if (state == PlayerStateEnum.Stopped) {
70 freeWifiLock();
71 } else if (state == PlayerStateEnum.Playing) {
72 lockWifiIfNecessary(null, false);
73 }
74 }
75
76 public void playlistChanged(Interval target) {
77
78 }
79
80 public void randomChanged(Random random) {
81
82 }
83
84 public void repeatChanged(Repeat repeat) {
85
86 }
87
88 public void trackChanged(PlaylistItem track, boolean play,
89 int positionMillis) {
90 if (play) {
91 if (track.getTrack().isLocal()) {
92 freeWifiLock();
93 return;
94 }
95 lockWifiIfNecessary(track.getTrack(), false);
96 }
97 }
98
99 private void lockWifiIfNecessary(final TrackMetadataBean track,
100 final boolean wifiConnected) {
101 if (wifiLock != null) {
102
103 return;
104 }
105 if (track == null) {
106 final TrackMetadataBean t = app.getPlaylist()
107 .getCurrentlyPlayingTrack();
108 if (t != null) {
109 lockWifiIfNecessary(t, wifiConnected);
110 }
111 return;
112 }
113 if (track.isLocal()) {
114 return;
115 }
116 if (!wifiConnected && !isWifiConnected()) {
117 return;
118 }
119
120 final WifiManager wifiMan = (WifiManager) app
121 .getSystemService(Context.WIFI_SERVICE);
122 wifiLock = wifiMan.createWifiLock(NetworkHandler.class.getName());
123 Log.i(NetworkHandler.class.getSimpleName(), "Acquiring WiFi lock");
124 wifiLock.acquire();
125 }
126
127 private boolean isWifiConnected() {
128 final ConnectivityManager manager = (ConnectivityManager) app
129 .getSystemService(Context.CONNECTIVITY_SERVICE);
130 final NetworkInfo info = manager
131 .getNetworkInfo(ConnectivityManager.TYPE_WIFI);
132 return info != null && info.isConnected();
133 }
134
135 private WifiLock wifiLock = null;
136
137 public void trackPositionChanged(int position, boolean playing) {
138
139 }
140
141 /***
142 * Destroys this handler and drops any power locks being held.
143 */
144 void destroy() {
145 app.unregisterReceiver(this);
146 freeWifiLock();
147 }
148
149 private void freeWifiLock() {
150 if (wifiLock != null) {
151 Log.i(NetworkHandler.class.getSimpleName(), "Dropping WiFi lock");
152 wifiLock.release();
153 wifiLock = null;
154 }
155 }
156
157 @Override
158 public void onReceive(Context context, Intent intent) {
159 if (intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY,
160 false)) {
161 freeWifiLock();
162 onNetworkStateChanged(true);
163 return;
164 }
165 if (intent
166 .getBooleanExtra(ConnectivityManager.EXTRA_IS_FAILOVER, false)) {
167 onNetworkStateChanged(!app.getStateHandler().getStartupState().online);
168 final boolean isPlaying = app.getPlaylist().getPlaybackState() == PlayerStateEnum.Playing;
169 if (!isPlaying) {
170
171 return;
172 }
173 final NetworkInfo newInfo = intent
174 .getParcelableExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO);
175 if (newInfo == null) {
176
177 return;
178 }
179 if (newInfo.getType() == ConnectivityManager.TYPE_WIFI) {
180 lockWifiIfNecessary(null, true);
181 return;
182 }
183
184
185 if (!isWifiConnected()) {
186 freeWifiLock();
187 }
188 }
189 }
190
191 private boolean getNetworkOffline() {
192 final ConnectivityManager manager = (ConnectivityManager) app
193 .getSystemService(Context.CONNECTIVITY_SERVICE);
194 final NetworkInfo[] infos = manager.getAllNetworkInfo();
195 if (infos == null) {
196 return true;
197 }
198 for (final NetworkInfo info : infos) {
199 if (info == null) {
200 continue;
201 }
202 if (info.isConnected()) {
203 return false;
204 }
205 }
206 return true;
207 }
208
209 /***
210 * Computes the off-line status of the phone.
211 *
212 * @return computed off-line state.
213 */
214 private boolean computeOffline() {
215 if (!app.getStateHandler().getStartupState().online) {
216 return true;
217 }
218 return getNetworkOffline();
219 }
220
221 private boolean lastOfflineValue = true;
222
223 /***
224 * Sets the offline status. Note that this method may be used to force the
225 * offline state only - it cannot be used to force us being online.
226 *
227 * @param offline
228 * <code>true</code> forces the offline status,
229 * <code>false</code> resumes the network status auto-detection.
230 */
231 public void setForcedOffline(boolean offline) {
232 app.getStateHandler().getStartupState().online = !offline;
233 onNetworkStateChanged(computeOffline());
234 }
235
236 private void onNetworkStateChanged(final boolean networkOffline) {
237 if (networkOffline == lastOfflineValue) {
238 return;
239 }
240 lastOfflineValue = networkOffline;
241 app.getBackgroundTasks().setOffline(networkOffline);
242 app.getBus().getInvocator(IApplicationListener.class, true).offline(
243 networkOffline);
244 }
245
246 /***
247 * Checks if the player is currently off-line.
248 *
249 * @return <code>true</code> if the player is off-line, <code>false</code>
250 * if it is on-line.
251 */
252 public boolean isOffline() {
253 return lastOfflineValue;
254 }
255 }