Clover Coverage Report - Subsonic-Android Coverage Report
Coverage timestamp: ven dic 19 2014 17:57:13 EST
../../../../../img/srcFileCovDistChart4.png 78% of files have more coverage
116   272   41   8,92
30   206   0,35   4,33
13     3,15  
3    
This report was generated with an evaluation server license. Purchase Clover or configure your license.
 
  DownloadServiceLifecycleSupport       Line # 45 102 35 34,3% 0.34285715
  DownloadServiceLifecycleSupport.MyPhoneStateListener       Line # 240 14 6 31,6% 0.31578946
  DownloadServiceLifecycleSupport.State       Line # 265 0 0 - -1.0
 
No Tests
 
1    /*
2    This file is part of Subsonic.
3   
4    Subsonic is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8   
9    Subsonic is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12    GNU General Public License for more details.
13   
14    You should have received a copy of the GNU General Public License
15    along with Subsonic. If not, see <http://www.gnu.org/licenses/>.
16   
17    Copyright 2009 (C) Sindre Mehus
18    */
19    package net.sourceforge.subsonic.androidapp.service;
20   
21    import java.io.Serializable;
22    import java.util.ArrayList;
23    import java.util.List;
24    import java.util.concurrent.Executors;
25    import java.util.concurrent.ScheduledExecutorService;
26    import java.util.concurrent.TimeUnit;
27   
28    import android.content.BroadcastReceiver;
29    import android.content.Context;
30    import android.content.Intent;
31    import android.content.IntentFilter;
32    import android.telephony.PhoneStateListener;
33    import android.telephony.TelephonyManager;
34    import android.util.Log;
35    import android.view.KeyEvent;
36    import net.sourceforge.subsonic.androidapp.domain.MusicDirectory;
37    import net.sourceforge.subsonic.androidapp.domain.PlayerState;
38    import net.sourceforge.subsonic.androidapp.util.CacheCleaner;
39    import net.sourceforge.subsonic.androidapp.util.FileUtil;
40    import net.sourceforge.subsonic.androidapp.util.Util;
41   
42    /**
43    * @author Sindre Mehus
44    */
 
45    public class DownloadServiceLifecycleSupport {
46   
47    private static final String TAG = DownloadServiceLifecycleSupport.class.getSimpleName();
48    private static final String FILENAME_DOWNLOADS_SER = "downloadstate.ser";
49   
50    private final DownloadServiceImpl downloadService;
51    private ScheduledExecutorService executorService;
52    private BroadcastReceiver headsetEventReceiver;
53    private BroadcastReceiver ejectEventReceiver;
54    private PhoneStateListener phoneStateListener;
55    private boolean externalStorageAvailable= true;
56   
57    /**
58    * This receiver manages the intent that could come from other applications.
59    */
60    private BroadcastReceiver intentReceiver = new BroadcastReceiver() {
 
61  0 toggle @Override
62    public void onReceive(Context context, Intent intent) {
63  0 String action = intent.getAction();
64  0 Log.i(TAG, "intentReceiver.onReceive: " + action);
65  0 if (DownloadServiceImpl.CMD_PLAY.equals(action)) {
66  0 downloadService.play();
67  0 } else if (DownloadServiceImpl.CMD_NEXT.equals(action)) {
68  0 downloadService.next();
69  0 } else if (DownloadServiceImpl.CMD_PREVIOUS.equals(action)) {
70  0 downloadService.previous();
71  0 } else if (DownloadServiceImpl.CMD_TOGGLEPAUSE.equals(action)) {
72  0 downloadService.togglePlayPause();
73  0 } else if (DownloadServiceImpl.CMD_PAUSE.equals(action)) {
74  0 downloadService.pause();
75  0 } else if (DownloadServiceImpl.CMD_STOP.equals(action)) {
76  0 downloadService.pause();
77  0 downloadService.seekTo(0);
78    }
79    }
80    };
81   
82   
 
83  1 toggle public DownloadServiceLifecycleSupport(DownloadServiceImpl downloadService) {
84  1 this.downloadService = downloadService;
85    }
86   
 
87  1 toggle public void onCreate() {
88  1 Runnable downloadChecker = new Runnable() {
 
89  92 toggle @Override
90    public void run() {
91  92 try {
92  92 downloadService.checkDownloads();
93    } catch (Throwable x) {
94  0 Log.e(TAG, "checkDownloads() failed.", x);
95    }
96    }
97    };
98   
99  1 executorService = Executors.newScheduledThreadPool(2);
100  1 executorService.scheduleWithFixedDelay(downloadChecker, 5, 5, TimeUnit.SECONDS);
101   
102    // Pause when headset is unplugged.
103  1 headsetEventReceiver = new BroadcastReceiver() {
 
104  0 toggle @Override
105    public void onReceive(Context context, Intent intent) {
106  0 Log.i(TAG, "Headset event for: " + intent.getExtras().get("name"));
107  0 if (intent.getExtras().getInt("state") == 0) {
108  0 downloadService.pause();
109    }
110    }
111    };
112  1 downloadService.registerReceiver(headsetEventReceiver, new IntentFilter(Intent.ACTION_HEADSET_PLUG));
113   
114    // Stop when SD card is ejected.
115  1 ejectEventReceiver = new BroadcastReceiver() {
 
116  0 toggle @Override
117    public void onReceive(Context context, Intent intent) {
118  0 externalStorageAvailable = Intent.ACTION_MEDIA_MOUNTED.equals(intent.getAction());
119  0 if (!externalStorageAvailable) {
120  0 Log.i(TAG, "External media is ejecting. Stopping playback.");
121  0 downloadService.reset();
122    } else {
123  0 Log.i(TAG, "External media is available.");
124    }
125    }
126    };
127  1 IntentFilter ejectFilter = new IntentFilter(Intent.ACTION_MEDIA_EJECT);
128  1 ejectFilter.addAction(Intent.ACTION_MEDIA_MOUNTED);
129  1 ejectFilter.addDataScheme("file");
130  1 downloadService.registerReceiver(ejectEventReceiver, ejectFilter);
131   
132    // React to media buttons.
133  1 Util.registerMediaButtonEventReceiver(downloadService);
134   
135    // Pause temporarily on incoming phone calls.
136  1 phoneStateListener = new MyPhoneStateListener();
137  1 TelephonyManager telephonyManager = (TelephonyManager) downloadService.getSystemService(Context.TELEPHONY_SERVICE);
138  1 telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
139   
140    // Register the handler for outside intents.
141  1 IntentFilter commandFilter = new IntentFilter();
142  1 commandFilter.addAction(DownloadServiceImpl.CMD_PLAY);
143  1 commandFilter.addAction(DownloadServiceImpl.CMD_TOGGLEPAUSE);
144  1 commandFilter.addAction(DownloadServiceImpl.CMD_PAUSE);
145  1 commandFilter.addAction(DownloadServiceImpl.CMD_STOP);
146  1 commandFilter.addAction(DownloadServiceImpl.CMD_PREVIOUS);
147  1 commandFilter.addAction(DownloadServiceImpl.CMD_NEXT);
148  1 downloadService.registerReceiver(intentReceiver, commandFilter);
149   
150  1 deserializeDownloadQueue();
151   
152  1 new CacheCleaner(downloadService, downloadService).clean();
153    }
154   
 
155  38 toggle public void onStart(Intent intent) {
156  38 if (intent != null && intent.getExtras() != null) {
157  0 KeyEvent event = (KeyEvent) intent.getExtras().get(Intent.EXTRA_KEY_EVENT);
158  0 if (event != null) {
159  0 handleKeyEvent(event);
160    }
161    }
162    }
163   
 
164  0 toggle public void onDestroy() {
165  0 executorService.shutdown();
166  0 serializeDownloadQueue();
167  0 downloadService.clear(false);
168  0 downloadService.unregisterReceiver(ejectEventReceiver);
169  0 downloadService.unregisterReceiver(headsetEventReceiver);
170  0 downloadService.unregisterReceiver(intentReceiver);
171   
172  0 TelephonyManager telephonyManager = (TelephonyManager) downloadService.getSystemService(Context.TELEPHONY_SERVICE);
173  0 telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE);
174    }
175   
 
176  128 toggle public boolean isExternalStorageAvailable() {
177  128 return externalStorageAvailable;
178    }
179   
 
180  56 toggle public void serializeDownloadQueue() {
181  56 State state = new State();
182  56 for (DownloadFile downloadFile : downloadService.getDownloads()) {
183  571 state.songs.add(downloadFile.getSong());
184    }
185  56 state.currentPlayingIndex = downloadService.getCurrentPlayingIndex();
186  56 state.currentPlayingPosition = downloadService.getPlayerPosition();
187   
188  56 Log.i(TAG, "Serialized currentPlayingIndex: " + state.currentPlayingIndex + ", currentPlayingPosition: " + state.currentPlayingPosition);
189  56 FileUtil.serialize(downloadService, state, FILENAME_DOWNLOADS_SER);
190    }
191   
 
192  1 toggle private void deserializeDownloadQueue() {
193  1 State state = FileUtil.deserialize(downloadService, FILENAME_DOWNLOADS_SER);
194  1 if (state == null) {
195  1 return;
196    }
197  0 Log.i(TAG, "Deserialized currentPlayingIndex: " + state.currentPlayingIndex + ", currentPlayingPosition: " + state.currentPlayingPosition);
198  0 downloadService.restore(state.songs, state.currentPlayingIndex, state.currentPlayingPosition);
199   
200    // Work-around: Serialize again, as the restore() method creates a serialization without current playing info.
201  0 serializeDownloadQueue();
202    }
203   
 
204  0 toggle private void handleKeyEvent(KeyEvent event) {
205  0 if (event.getAction() != KeyEvent.ACTION_DOWN || event.getRepeatCount() > 0) {
206  0 return;
207    }
208   
209  0 switch (event.getKeyCode()) {
210  0 case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
211  0 case KeyEvent.KEYCODE_HEADSETHOOK:
212  0 downloadService.togglePlayPause();
213  0 break;
214  0 case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
215  0 downloadService.previous();
216  0 break;
217  0 case KeyEvent.KEYCODE_MEDIA_NEXT:
218  0 if (downloadService.getCurrentPlayingIndex() < downloadService.size() - 1) {
219  0 downloadService.next();
220    }
221  0 break;
222  0 case KeyEvent.KEYCODE_MEDIA_STOP:
223  0 downloadService.reset();
224  0 break;
225  0 case KeyEvent.KEYCODE_MEDIA_PLAY:
226  0 downloadService.start();
227  0 break;
228  0 case KeyEvent.KEYCODE_MEDIA_PAUSE:
229  0 downloadService.pause();
230  0 break;
231  0 default:
232  0 break;
233    }
234    }
235   
236    /**
237    * Logic taken from packages/apps/Music. Will pause when an incoming
238    * call rings or if a call (incoming or outgoing) is connected.
239    */
 
240    private class MyPhoneStateListener extends PhoneStateListener {
241    private boolean resumeAfterCall;
242   
 
243  1 toggle @Override
244    public void onCallStateChanged(int state, String incomingNumber) {
245  1 switch (state) {
246  0 case TelephonyManager.CALL_STATE_RINGING:
247  0 case TelephonyManager.CALL_STATE_OFFHOOK:
248  0 if (downloadService.getPlayerState() == PlayerState.STARTED) {
249  0 resumeAfterCall = true;
250  0 downloadService.pause();
251    }
252  0 break;
253  1 case TelephonyManager.CALL_STATE_IDLE:
254  1 if (resumeAfterCall) {
255  0 resumeAfterCall = false;
256  0 downloadService.start();
257    }
258  1 break;
259  0 default:
260  0 break;
261    }
262    }
263    }
264   
 
265    private static class State implements Serializable {
266    private static final long serialVersionUID = -6346438781062572270L;
267   
268    private List<MusicDirectory.Entry> songs = new ArrayList<MusicDirectory.Entry>();
269    private int currentPlayingIndex;
270    private int currentPlayingPosition;
271    }
272    }