Clover Coverage Report - Subsonic-Android Coverage Report
Coverage timestamp: ven dic 19 2014 17:57:13 EST
../../../../../img/srcFileCovDistChart9.png 32% of files have more coverage
311   809   116   5,27
76   571   0,37   59
59     1,97  
1    
This report was generated with an evaluation server license. Purchase Clover or configure your license.
 
  Util       Line # 80 311 116 84,8% 0.84753364
 
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.util;
20   
21    import android.app.Activity;
22    import android.app.AlertDialog;
23    import android.app.Notification;
24    import android.app.NotificationManager;
25    import android.app.PendingIntent;
26    import android.app.Service;
27    import android.appwidget.AppWidgetProvider;
28    import android.content.ComponentName;
29    import android.content.Context;
30    import android.content.DialogInterface;
31    import android.content.Intent;
32    import android.content.SharedPreferences;
33    import android.content.res.Resources;
34    import android.graphics.Bitmap;
35    import android.graphics.drawable.BitmapDrawable;
36    import android.graphics.drawable.Drawable;
37    import android.media.AudioManager;
38    import android.net.ConnectivityManager;
39    import android.net.NetworkInfo;
40    import android.os.Environment;
41    import android.os.Handler;
42    import android.util.Log;
43    import android.view.Gravity;
44    import android.view.KeyEvent;
45    import android.widget.RemoteViews;
46    import android.widget.Toast;
47    import net.sourceforge.subsonic.androidapp.R;
48    import net.sourceforge.subsonic.androidapp.activity.DownloadActivity;
49    import net.sourceforge.subsonic.androidapp.activity.MainActivity;
50    import net.sourceforge.subsonic.androidapp.domain.MusicDirectory;
51    import net.sourceforge.subsonic.androidapp.domain.PlayerState;
52    import net.sourceforge.subsonic.androidapp.domain.RepeatMode;
53    import net.sourceforge.subsonic.androidapp.domain.Version;
54    import net.sourceforge.subsonic.androidapp.provider.SubsonicAppWidgetProvider4x1;
55    import net.sourceforge.subsonic.androidapp.receiver.MediaButtonIntentReceiver;
56    import net.sourceforge.subsonic.androidapp.service.DownloadServiceImpl;
57    import org.apache.http.HttpEntity;
58   
59    import java.io.ByteArrayOutputStream;
60    import java.io.Closeable;
61    import java.io.File;
62    import java.io.FileInputStream;
63    import java.io.FileOutputStream;
64    import java.io.IOException;
65    import java.io.InputStream;
66    import java.io.OutputStream;
67    import java.io.UnsupportedEncodingException;
68    import java.lang.reflect.Constructor;
69    import java.lang.reflect.Method;
70    import java.security.MessageDigest;
71    import java.text.DecimalFormat;
72    import java.text.NumberFormat;
73    import java.util.Map;
74    import java.util.concurrent.ConcurrentHashMap;
75   
76    /**
77    * @author Sindre Mehus
78    * @version $Id$
79    */
 
80    public class Util extends DownloadActivity {
81   
82    private static final String TAG = Util.class.getSimpleName();
83   
84    private static final DecimalFormat GIGA_BYTE_FORMAT = new DecimalFormat("0.00 GB");
85    private static final DecimalFormat MEGA_BYTE_FORMAT = new DecimalFormat("0.00 MB");
86    private static final DecimalFormat KILO_BYTE_FORMAT = new DecimalFormat("0 KB");
87   
88    private static DecimalFormat GIGA_BYTE_LOCALIZED_FORMAT = null;
89    private static DecimalFormat MEGA_BYTE_LOCALIZED_FORMAT = null;
90    private static DecimalFormat KILO_BYTE_LOCALIZED_FORMAT = null;
91    private static DecimalFormat BYTE_LOCALIZED_FORMAT = null;
92   
93    public static final String EVENT_META_CHANGED = "net.sourceforge.subsonic.androidapp.EVENT_META_CHANGED";
94    public static final String EVENT_PLAYSTATE_CHANGED = "net.sourceforge.subsonic.androidapp.EVENT_PLAYSTATE_CHANGED";
95   
96    private static final Map<Integer, Version> SERVER_REST_VERSIONS = new ConcurrentHashMap<Integer, Version>();
97   
98    // Used by hexEncode()
99    private static final char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
100    private static Toast toast;
101   
 
102  0 toggle private Util() {
103    }
104   
 
105  282 toggle public static boolean isOffline(Context context) {
106  282 return getActiveServer(context) == 0;
107    }
108   
 
109  21 toggle public static boolean isScreenLitOnDownload(Context context) {
110  21 SharedPreferences prefs = getPreferences(context);
111  21 return prefs.getBoolean(Constants.PREFERENCES_KEY_SCREEN_LIT_ON_DOWNLOAD, false);
112    }
113   
 
114  27 toggle public static RepeatMode getRepeatMode(Context context) {
115  27 SharedPreferences prefs = getPreferences(context);
116  27 return RepeatMode.valueOf(prefs.getString(Constants.PREFERENCES_KEY_REPEAT_MODE, RepeatMode.OFF.name()));
117    }
118   
 
119  5 toggle public static void setRepeatMode(Context context, RepeatMode repeatMode) {
120  5 SharedPreferences prefs = getPreferences(context);
121  5 SharedPreferences.Editor editor = prefs.edit();
122  5 editor.putString(Constants.PREFERENCES_KEY_REPEAT_MODE, repeatMode.name());
123  5 editor.commit();
124    }
125   
 
126  31 toggle public static boolean isScrobblingEnabled(Context context) {
127  31 if (isOffline(context)) {
128  8 return false;
129    }
130  23 SharedPreferences prefs = getPreferences(context);
131  23 return prefs.getBoolean(Constants.PREFERENCES_KEY_SCROBBLE, false);
132    }
133   
 
134  8 toggle public static void setActiveServer(Context context, int instance) {
135  8 SharedPreferences prefs = getPreferences(context);
136  8 SharedPreferences.Editor editor = prefs.edit();
137  8 editor.putInt(Constants.PREFERENCES_KEY_SERVER_INSTANCE, instance);
138  8 editor.commit();
139    }
140   
 
141  410 toggle public static int getActiveServer(Context context) {
142  410 SharedPreferences prefs = getPreferences(context);
143  410 return prefs.getInt(Constants.PREFERENCES_KEY_SERVER_INSTANCE, 1);
144    }
145   
 
146  26 toggle public static String getServerName(Context context, int instance) {
147  26 if (instance == 0) {
148  6 return context.getResources().getString(R.string.main_offline);
149    }
150  20 SharedPreferences prefs = getPreferences(context);
151  20 return prefs.getString(Constants.PREFERENCES_KEY_SERVER_NAME + instance, null);
152    }
153   
 
154  36 toggle public static void setServerRestVersion(Context context, Version version) {
155  36 SERVER_REST_VERSIONS.put(getActiveServer(context), version);
156    }
157   
 
158  9 toggle public static Version getServerRestVersion(Context context) {
159  9 return SERVER_REST_VERSIONS.get(getActiveServer(context));
160    }
161   
 
162  0 toggle public static void setSelectedMusicFolderId(Context context, String musicFolderId) {
163  0 int instance = getActiveServer(context);
164  0 SharedPreferences prefs = getPreferences(context);
165  0 SharedPreferences.Editor editor = prefs.edit();
166  0 editor.putString(Constants.PREFERENCES_KEY_MUSIC_FOLDER_ID + instance, musicFolderId);
167  0 editor.commit();
168    }
169   
 
170  7 toggle public static String getSelectedMusicFolderId(Context context) {
171  7 SharedPreferences prefs = getPreferences(context);
172  7 int instance = getActiveServer(context);
173  7 return prefs.getString(Constants.PREFERENCES_KEY_MUSIC_FOLDER_ID + instance, null);
174    }
175   
 
176  62 toggle public static String getTheme(Context context) {
177  62 SharedPreferences prefs = getPreferences(context);
178  62 return prefs.getString(Constants.PREFERENCES_KEY_THEME, null);
179    }
180   
 
181  164 toggle public static int getMaxBitrate(Context context) {
182  164 ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
183  164 NetworkInfo networkInfo = manager.getActiveNetworkInfo();
184  164 if (networkInfo == null) {
185  0 return 0;
186    }
187   
188  164 boolean wifi = networkInfo.getType() == ConnectivityManager.TYPE_WIFI;
189  164 SharedPreferences prefs = getPreferences(context);
190  164 return Integer.parseInt(prefs.getString(wifi ? Constants.PREFERENCES_KEY_MAX_BITRATE_WIFI : Constants.PREFERENCES_KEY_MAX_BITRATE_MOBILE, "0"));
191    }
192   
 
193  57 toggle public static int getPreloadCount(Context context) {
194  57 SharedPreferences prefs = getPreferences(context);
195  57 int preloadCount = Integer.parseInt(prefs.getString(Constants.PREFERENCES_KEY_PRELOAD_COUNT, "-1"));
196  57 return preloadCount == -1 ? Integer.MAX_VALUE : preloadCount;
197    }
198   
 
199  21 toggle public static int getCacheSizeMB(Context context) {
200  21 SharedPreferences prefs = getPreferences(context);
201  21 int cacheSize = Integer.parseInt(prefs.getString(Constants.PREFERENCES_KEY_CACHE_SIZE, "-1"));
202  21 return cacheSize == -1 ? Integer.MAX_VALUE : cacheSize;
203    }
204   
 
205  121 toggle public static String getRestUrl(Context context, String method) {
206  121 StringBuilder builder = new StringBuilder();
207   
208  121 SharedPreferences prefs = getPreferences(context);
209   
210  121 int instance = prefs.getInt(Constants.PREFERENCES_KEY_SERVER_INSTANCE, 1);
211  121 String serverUrl = prefs.getString(Constants.PREFERENCES_KEY_SERVER_URL + instance, null);
212  121 String username = prefs.getString(Constants.PREFERENCES_KEY_USERNAME + instance, null);
213  121 String password = prefs.getString(Constants.PREFERENCES_KEY_PASSWORD + instance, null);
214   
215    // Slightly obfuscate password
216  121 password = "enc:" + Util.utf8HexEncode(password);
217   
218  121 builder.append(serverUrl);
219  121 if (builder.charAt(builder.length() - 1) != '/') {
220  121 builder.append("/");
221    }
222  121 builder.append("rest/").append(method).append(".view");
223  121 builder.append("?u=").append(username);
224  121 builder.append("&p=").append(password);
225  121 builder.append("&v=").append(Constants.REST_PROTOCOL_VERSION);
226  121 builder.append("&c=").append(Constants.REST_CLIENT_ID);
227   
228  121 return builder.toString();
229    }
230   
 
231  1721 toggle public static SharedPreferences getPreferences(Context context) {
232  1721 return context.getSharedPreferences(Constants.PREFERENCES_FILE_NAME, 0);
233    }
234   
 
235  50 toggle public static String getContentType(HttpEntity entity) {
236  50 if (entity == null || entity.getContentType() == null) {
237  0 return null;
238    }
239  50 return entity.getContentType().getValue();
240    }
241   
 
242  0 toggle public static int getRemainingTrialDays(Context context) {
243  0 SharedPreferences prefs = getPreferences(context);
244  0 long installTime = prefs.getLong(Constants.PREFERENCES_KEY_INSTALL_TIME, 0L);
245   
246  0 if (installTime == 0L) {
247  0 installTime = System.currentTimeMillis();
248  0 SharedPreferences.Editor editor = prefs.edit();
249  0 editor.putLong(Constants.PREFERENCES_KEY_INSTALL_TIME, installTime);
250  0 editor.commit();
251    }
252   
253  0 long now = System.currentTimeMillis();
254  0 long millisPerDay = 24L * 60L * 60L * 1000L;
255  0 int daysSinceInstall = (int) ((now - installTime) / millisPerDay);
256  0 return Math.max(0, Constants.FREE_TRIAL_DAYS - daysSinceInstall);
257    }
258   
259    /**
260    * Get the contents of an <code>InputStream</code> as a <code>byte[]</code>.
261    * <p/>
262    * This method buffers the input internally, so there is no need to use a
263    * <code>BufferedInputStream</code>.
264    *
265    * @param input the <code>InputStream</code> to read from
266    * @return the requested byte array
267    * @throws NullPointerException if the input is null
268    * @throws IOException if an I/O error occurs
269    */
 
270  33 toggle public static byte[] toByteArray(InputStream input) throws IOException {
271  33 ByteArrayOutputStream output = new ByteArrayOutputStream();
272  33 copy(input, output);
273  33 return output.toByteArray();
274    }
275   
 
276  33 toggle public static long copy(InputStream input, OutputStream output)
277    throws IOException {
278  33 byte[] buffer = new byte[1024 * 4];
279  33 long count = 0;
280  33 int n;
281  ? while (-1 != (n = input.read(buffer))) {
282  193 output.write(buffer, 0, n);
283  193 count += n;
284    }
285  33 return count;
286    }
287   
 
288  6 toggle public static void atomicCopy(File from, File to) throws IOException {
289  6 FileInputStream in = null;
290  6 FileOutputStream out = null;
291  6 File tmp = null;
292  6 try {
293  6 tmp = new File(to.getPath() + ".tmp");
294  6 in = new FileInputStream(from);
295  6 out = new FileOutputStream(tmp);
296  6 in.getChannel().transferTo(0, from.length(), out.getChannel());
297  6 out.close();
298  6 if (!tmp.renameTo(to)) {
299  0 throw new IOException("Failed to rename " + tmp + " to " + to);
300    }
301  6 Log.i(TAG, "Copied " + from + " to " + to);
302    } catch (IOException x) {
303  0 close(out);
304  0 delete(to);
305  0 throw x;
306    } finally {
307  6 close(in);
308  6 close(out);
309  6 delete(tmp);
310    }
311    }
312   
 
313  218 toggle public static void close(Closeable closeable) {
314  218 try {
315  218 if (closeable != null) {
316  216 closeable.close();
317    }
318    } catch (Throwable x) {
319    // Ignored
320    }
321    }
322   
 
323  81 toggle public static boolean delete(File file) {
324  81 if (file != null && file.exists()) {
325  31 if (!file.delete()) {
326  0 Log.w(TAG, "Failed to delete file " + file);
327  0 return false;
328    }
329  31 Log.i(TAG, "Deleted file " + file);
330    }
331  81 return true;
332    }
333   
 
334  16 toggle public static void toast(Context context, int messageId) {
335  16 toast(context, messageId, true);
336    }
337   
 
338  25 toggle public static void toast(Context context, int messageId, boolean shortDuration) {
339  25 toast(context, context.getString(messageId), shortDuration);
340    }
341   
 
342  10 toggle public static void toast(Context context, String message) {
343  10 toast(context, message, true);
344    }
345   
 
346  95 toggle public static void toast(Context context, String message, boolean shortDuration) {
347  95 if (toast == null) {
348  1 toast = Toast.makeText(context, message, shortDuration ? Toast.LENGTH_SHORT : Toast.LENGTH_LONG);
349  1 toast.setGravity(Gravity.CENTER, 0, 0);
350    } else {
351  94 toast.setText(message);
352  94 toast.setDuration(shortDuration ? Toast.LENGTH_SHORT : Toast.LENGTH_LONG);
353    }
354  95 toast.show();
355    }
356   
357    /**
358    * Converts a byte-count to a formatted string suitable for display to the user.
359    * For instance:
360    * <ul>
361    * <li><code>format(918)</code> returns <em>"918 B"</em>.</li>
362    * <li><code>format(98765)</code> returns <em>"96 KB"</em>.</li>
363    * <li><code>format(1238476)</code> returns <em>"1.2 MB"</em>.</li>
364    * </ul>
365    * This method assumes that 1 KB is 1024 bytes.
366    * To get a localized string, please use formatLocalizedBytes instead.
367    *
368    * @param byteCount The number of bytes.
369    * @return The formatted string.
370    */
 
371  195 toggle public static synchronized String formatBytes(long byteCount) {
372   
373    // More than 1 GB?
374  195 if (byteCount >= 1024 * 1024 * 1024) {
375  42 NumberFormat gigaByteFormat = GIGA_BYTE_FORMAT;
376  42 return gigaByteFormat.format((double) byteCount / (1024 * 1024 * 1024));
377    }
378   
379    // More than 1 MB?
380  153 if (byteCount >= 1024 * 1024) {
381  104 NumberFormat megaByteFormat = MEGA_BYTE_FORMAT;
382  104 return megaByteFormat.format((double) byteCount / (1024 * 1024));
383    }
384   
385    // More than 1 KB?
386  49 if (byteCount >= 1024) {
387  12 NumberFormat kiloByteFormat = KILO_BYTE_FORMAT;
388  12 return kiloByteFormat.format((double) byteCount / 1024);
389    }
390   
391  37 return byteCount + " B";
392    }
393   
394    /**
395    * Converts a byte-count to a formatted string suitable for display to the user.
396    * For instance:
397    * <ul>
398    * <li><code>format(918)</code> returns <em>"918 B"</em>.</li>
399    * <li><code>format(98765)</code> returns <em>"96 KB"</em>.</li>
400    * <li><code>format(1238476)</code> returns <em>"1.2 MB"</em>.</li>
401    * </ul>
402    * This method assumes that 1 KB is 1024 bytes.
403    * This version of the method returns a localized string.
404    *
405    * @param byteCount The number of bytes.
406    * @return The formatted string.
407    */
 
408  89 toggle public static synchronized String formatLocalizedBytes(long byteCount, Context context) {
409   
410    // More than 1 GB?
411  89 if (byteCount >= 1024 * 1024 * 1024) {
412  0 if (GIGA_BYTE_LOCALIZED_FORMAT == null) {
413  0 GIGA_BYTE_LOCALIZED_FORMAT = new DecimalFormat(context.getResources().getString(R.string.util_bytes_format_gigabyte));
414    }
415   
416  0 return GIGA_BYTE_LOCALIZED_FORMAT.format((double) byteCount / (1024 * 1024 * 1024));
417    }
418   
419    // More than 1 MB?
420  89 if (byteCount >= 1024 * 1024) {
421  31 if (MEGA_BYTE_LOCALIZED_FORMAT == null) {
422  1 MEGA_BYTE_LOCALIZED_FORMAT = new DecimalFormat(context.getResources().getString(R.string.util_bytes_format_megabyte));
423    }
424   
425  31 return MEGA_BYTE_LOCALIZED_FORMAT.format((double) byteCount / (1024 * 1024));
426    }
427   
428    // More than 1 KB?
429  58 if (byteCount >= 1024) {
430  43 if (KILO_BYTE_LOCALIZED_FORMAT == null) {
431  1 KILO_BYTE_LOCALIZED_FORMAT = new DecimalFormat(context.getResources().getString(R.string.util_bytes_format_kilobyte));
432    }
433   
434  43 return KILO_BYTE_LOCALIZED_FORMAT.format((double) byteCount / 1024);
435    }
436   
437  15 if (BYTE_LOCALIZED_FORMAT == null) {
438  1 BYTE_LOCALIZED_FORMAT = new DecimalFormat(context.getResources().getString(R.string.util_bytes_format_byte));
439    }
440   
441  15 return BYTE_LOCALIZED_FORMAT.format((double) byteCount);
442    }
443   
 
444  697 toggle public static String formatDuration(Integer seconds) {
445  697 if (seconds == null) {
446  40 return null;
447    }
448   
449  657 int minutes = seconds / 60;
450  657 int secs = seconds % 60;
451   
452  657 StringBuilder builder = new StringBuilder(6);
453  657 builder.append(minutes).append(":");
454  657 if (secs < 10) {
455  282 builder.append("0");
456    }
457  657 builder.append(secs);
458  657 return builder.toString();
459    }
460   
 
461  29 toggle public static boolean equals(Object object1, Object object2) {
462  29 if (object1 == object2) {
463  0 return true;
464    }
465  29 if (object1 == null || object2 == null) {
466  1 return false;
467    }
468  28 return object1.equals(object2);
469   
470    }
471   
472    /**
473    * Encodes the given string by using the hexadecimal representation of its UTF-8 bytes.
474    *
475    * @param s The string to encode.
476    * @return The encoded string.
477    */
 
478  121 toggle public static String utf8HexEncode(String s) {
479  121 if (s == null) {
480  0 return null;
481    }
482  121 byte[] utf8;
483  121 try {
484  121 utf8 = s.getBytes(Constants.UTF_8);
485    } catch (UnsupportedEncodingException x) {
486  0 throw new RuntimeException(x);
487    }
488  121 return hexEncode(utf8);
489    }
490   
491    /**
492    * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order.
493    * The returned array will be double the length of the passed array, as it takes two characters to represent any
494    * given byte.
495    *
496    * @param data Bytes to convert to hexadecimal characters.
497    * @return A string containing hexadecimal characters.
498    */
 
499  301 toggle public static String hexEncode(byte[] data) {
500  301 int length = data.length;
501  301 char[] out = new char[length << 1];
502    // two characters form the hex value.
503  3786 for (int i = 0, j = 0; i < length; i++) {
504  3485 out[j++] = HEX_DIGITS[(0xF0 & data[i]) >>> 4];
505  3485 out[j++] = HEX_DIGITS[0x0F & data[i]];
506    }
507  301 return new String(out);
508    }
509   
510    /**
511    * Calculates the MD5 digest and returns the value as a 32 character hex string.
512    *
513    * @param s Data to digest.
514    * @return MD5 digest as a hex string.
515    */
 
516  180 toggle public static String md5Hex(String s) {
517  180 if (s == null) {
518  0 return null;
519    }
520   
521  180 try {
522  180 MessageDigest md5 = MessageDigest.getInstance("MD5");
523  180 return hexEncode(md5.digest(s.getBytes(Constants.UTF_8)));
524    } catch (Exception x) {
525  0 throw new RuntimeException(x.getMessage(), x);
526    }
527    }
528   
 
529  172 toggle public static boolean isNetworkConnected(Context context) {
530  172 ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
531  172 NetworkInfo networkInfo = manager.getActiveNetworkInfo();
532  172 boolean connected = networkInfo != null && networkInfo.isConnected();
533   
534  172 boolean wifiConnected = connected && networkInfo.getType() == ConnectivityManager.TYPE_WIFI;
535  172 boolean wifiRequired = isWifiRequiredForDownload(context);
536   
537  172 return connected && (!wifiRequired || wifiConnected);
538    }
539   
 
540  176 toggle public static boolean isExternalStoragePresent() {
541  176 return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState());
542    }
543   
 
544  172 toggle private static boolean isWifiRequiredForDownload(Context context) {
545  172 SharedPreferences prefs = getPreferences(context);
546  172 return prefs.getBoolean(Constants.PREFERENCES_KEY_WIFI_REQUIRED_FOR_DOWNLOAD, false);
547    }
548   
 
549  1 toggle public static void info(Context context, int titleId, int messageId) {
550  1 showDialog(context, android.R.drawable.ic_dialog_info, titleId, messageId);
551    }
552   
 
553  1 toggle private static void showDialog(Context context, int icon, int titleId, int messageId) {
554  1 new AlertDialog.Builder(context)
555    .setIcon(icon)
556    .setTitle(titleId)
557    .setMessage(messageId)
558    .setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener() {
 
559  1 toggle @Override
560    public void onClick(DialogInterface dialog, int i) {
561  1 dialog.dismiss();
562    }
563    })
564    .show();
565    }
566   
 
567  29 toggle public static void showPlayingNotification(final Context context, final DownloadServiceImpl downloadService, Handler handler, MusicDirectory.Entry song) {
568   
569    // Use the same text for the ticker and the expanded notification
570  29 String title = song.getTitle();
571  29 String text = song.getArtist();
572  29 String album = song.getAlbum();
573   
574    // Set the icon, scrolling text and timestamp
575  29 final Notification notification = new Notification(R.drawable.ic_stat_subsonic, title, System.currentTimeMillis());
576  29 notification.flags |= Notification.FLAG_NO_CLEAR | Notification.FLAG_ONGOING_EVENT;
577   
578  29 RemoteViews contentView = new RemoteViews(context.getPackageName(), R.layout.notification);
579   
580    // Set the album art.
581  29 try {
582  29 int size = context.getResources().getDrawable(R.drawable.unknown_album).getIntrinsicHeight();
583  29 Bitmap bitmap = FileUtil.getAlbumArtBitmap(context, song, size);
584  29 if (bitmap == null) {
585    // set default album art
586  7 contentView.setImageViewResource(R.id.notification_image, R.drawable.unknown_album);
587    } else {
588  22 contentView.setImageViewBitmap(R.id.notification_image, bitmap);
589    }
590    } catch (Exception x) {
591  0 Log.w(TAG, "Failed to get notification cover art", x);
592  0 contentView.setImageViewResource(R.id.notification_image, R.drawable.unknown_album);
593    }
594   
595    // set the text for the notifications
596  29 contentView.setTextViewText(R.id.trackname, title);
597  29 contentView.setTextViewText(R.id.artist, text);
598  29 contentView.setTextViewText(R.id.album, album);
599   
600  29 notification.contentView = contentView;
601   
602  29 Intent notificationIntent = new Intent(context, DownloadActivity.class);
603  29 notification.contentIntent = PendingIntent.getActivity(context, 0, notificationIntent, 0);
604   
605    // Send the notification and put the service in the foreground.
606  29 handler.post(new Runnable() {
 
607  29 toggle @Override
608    public void run() {
609  29 startForeground(downloadService, Constants.NOTIFICATION_ID_PLAYING, notification);
610    }
611    });
612   
613    // Update widget
614  29 linkButtons(context, contentView, false);
615  29 SubsonicAppWidgetProvider4x1.getInstance().notifyChange(context, downloadService, true);
616    }
617   
 
618  18 toggle public static void hidePlayingNotification(final Context context, final DownloadServiceImpl downloadService, Handler handler) {
619   
620    // Remove notification and remove the service from the foreground
621  18 handler.post(new Runnable() {
 
622  18 toggle @Override
623    public void run() {
624  18 stopForeground(downloadService, true);
625    }
626    });
627   
628    // Update widget
629  18 SubsonicAppWidgetProvider4x1.getInstance().notifyChange(context, downloadService, false);
630    }
631   
 
632  101 toggle public static void sleepQuietly(long millis) {
633  101 try {
634  101 Thread.sleep(millis);
635    } catch (InterruptedException x) {
636  0 Log.w(TAG, "Interrupted from sleep.", x);
637    }
638    }
639   
 
640  6 toggle public static void startActivityWithoutTransition(Activity currentActivity, Class<? extends Activity> newActivitiy) {
641  6 startActivityWithoutTransition(currentActivity, new Intent(currentActivity, newActivitiy));
642    }
643   
 
644  42 toggle public static void startActivityWithoutTransition(Activity currentActivity, Intent intent) {
645  42 currentActivity.startActivity(intent);
646  42 disablePendingTransition(currentActivity);
647    }
648   
 
649  46 toggle public static void disablePendingTransition(Activity activity) {
650   
651    // Activity.overridePendingTransition() was introduced in Android 2.0. Use reflection to maintain
652    // compatibility with 1.5.
653  46 try {
654  46 Method method = Activity.class.getMethod("overridePendingTransition", int.class, int.class);
655  46 method.invoke(activity, 0, 0);
656    } catch (Throwable x) {
657    // Ignored
658    }
659    }
660   
 
661  36 toggle public static Drawable createDrawableFromBitmap(Context context, Bitmap bitmap) {
662    // BitmapDrawable(Resources, Bitmap) was introduced in Android 1.6. Use reflection to maintain
663    // compatibility with 1.5.
664  36 try {
665  36 Constructor<BitmapDrawable> constructor = BitmapDrawable.class.getConstructor(Resources.class, Bitmap.class);
666  36 return constructor.newInstance(context.getResources(), bitmap);
667    } catch (Throwable x) {
668  0 return new BitmapDrawable(bitmap);
669    }
670    }
671   
 
672  52 toggle public static void registerMediaButtonEventReceiver(Context context) {
673   
674    // Only do it if enabled in the settings.
675  52 SharedPreferences prefs = getPreferences(context);
676  52 boolean enabled = prefs.getBoolean(Constants.PREFERENCES_KEY_MEDIA_BUTTONS, true);
677   
678  52 if (enabled) {
679   
680    // AudioManager.registerMediaButtonEventReceiver() was introduced in Android 2.2.
681    // Use reflection to maintain compatibility with 1.5.
682  52 try {
683  52 AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
684  52 ComponentName componentName = new ComponentName(context.getPackageName(), MediaButtonIntentReceiver.class.getName());
685  52 Method method = AudioManager.class.getMethod("registerMediaButtonEventReceiver", ComponentName.class);
686  52 method.invoke(audioManager, componentName);
687    } catch (Throwable x) {
688    // Ignored.
689    }
690    }
691    }
692   
 
693  1 toggle public static void unregisterMediaButtonEventReceiver(Context context) {
694    // AudioManager.unregisterMediaButtonEventReceiver() was introduced in Android 2.2.
695    // Use reflection to maintain compatibility with 1.5.
696  1 try {
697  1 AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
698  1 ComponentName componentName = new ComponentName(context.getPackageName(), MediaButtonIntentReceiver.class.getName());
699  1 Method method = AudioManager.class.getMethod("unregisterMediaButtonEventReceiver", ComponentName.class);
700  1 method.invoke(audioManager, componentName);
701    } catch (Throwable x) {
702    // Ignored.
703    }
704    }
705   
 
706  29 toggle private static void startForeground(Service service, int notificationId, Notification notification) {
707    // Service.startForeground() was introduced in Android 2.0.
708    // Use reflection to maintain compatibility with 1.5.
709  29 try {
710  29 Method method = Service.class.getMethod("startForeground", int.class, Notification.class);
711  29 method.invoke(service, notificationId, notification);
712  29 Log.i(TAG, "Successfully invoked Service.startForeground()");
713    } catch (Throwable x) {
714  0 NotificationManager notificationManager = (NotificationManager) service.getSystemService(Context.NOTIFICATION_SERVICE);
715  0 notificationManager.notify(Constants.NOTIFICATION_ID_PLAYING, notification);
716  0 Log.i(TAG, "Service.startForeground() not available. Using work-around.");
717    }
718    }
719   
 
720  18 toggle private static void stopForeground(Service service, boolean removeNotification) {
721    // Service.stopForeground() was introduced in Android 2.0.
722    // Use reflection to maintain compatibility with 1.5.
723  18 try {
724  18 Method method = Service.class.getMethod("stopForeground", boolean.class);
725  18 method.invoke(service, removeNotification);
726  18 Log.i(TAG, "Successfully invoked Service.stopForeground()");
727    } catch (Throwable x) {
728  0 NotificationManager notificationManager = (NotificationManager) service.getSystemService(Context.NOTIFICATION_SERVICE);
729  0 notificationManager.cancel(Constants.NOTIFICATION_ID_PLAYING);
730  0 Log.i(TAG, "Service.stopForeground() not available. Using work-around.");
731    }
732    }
733   
734    /**
735    * <p>Broadcasts the given song info as the new song being played.</p>
736    */
 
737  38 toggle public static void broadcastNewTrackInfo(Context context, MusicDirectory.Entry song) {
738  38 Intent intent = new Intent(EVENT_META_CHANGED);
739   
740  38 if (song != null) {
741  26 intent.putExtra("title", song.getTitle());
742  26 intent.putExtra("artist", song.getArtist());
743  26 intent.putExtra("album", song.getAlbum());
744   
745  26 File albumArtFile = FileUtil.getAlbumArtFile(context, song);
746  26 intent.putExtra("coverart", albumArtFile.getAbsolutePath());
747    } else {
748  12 intent.putExtra("title", "");
749  12 intent.putExtra("artist", "");
750  12 intent.putExtra("album", "");
751  12 intent.putExtra("coverart", "");
752    }
753   
754  38 context.sendBroadcast(intent);
755    }
756   
757    /**
758    * <p>Broadcasts the given player state as the one being set.</p>
759    */
 
760  197 toggle public static void broadcastPlaybackStatusChange(Context context, PlayerState state) {
761  197 Intent intent = new Intent(EVENT_PLAYSTATE_CHANGED);
762   
763  197 switch (state) {
764  27 case STARTED:
765  27 intent.putExtra("state", "play");
766  27 break;
767  0 case STOPPED:
768  0 intent.putExtra("state", "stop");
769  0 break;
770  6 case PAUSED:
771  6 intent.putExtra("state", "pause");
772  6 break;
773  4 case COMPLETED:
774  4 intent.putExtra("state", "complete");
775  4 break;
776  160 default:
777  160 return; // No need to broadcast.
778    }
779   
780  37 context.sendBroadcast(intent);
781    }
782   
 
783  29 toggle private static void linkButtons(Context context, RemoteViews views, boolean playerActive) {
784   
785  29 Intent intent = new Intent(context, playerActive ? DownloadActivity.class : MainActivity.class);
786  29 PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
787  29 views.setOnClickPendingIntent(R.id.appwidget_coverart, pendingIntent);
788  29 views.setOnClickPendingIntent(R.id.appwidget_top, pendingIntent);
789   
790    // Emulate media button clicks.
791  29 intent = new Intent("1");
792  29 intent.setComponent(new ComponentName(context, DownloadServiceImpl.class));
793  29 intent.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE));
794  29 pendingIntent = PendingIntent.getService(context, 0, intent, 0);
795  29 views.setOnClickPendingIntent(R.id.control_play, pendingIntent);
796   
797  29 intent = new Intent("2"); // Use a unique action name to ensure a different PendingIntent to be created.
798  29 intent.setComponent(new ComponentName(context, DownloadServiceImpl.class));
799  29 intent.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_NEXT));
800  29 pendingIntent = PendingIntent.getService(context, 0, intent, 0);
801  29 views.setOnClickPendingIntent(R.id.control_next, pendingIntent);
802   
803  29 intent = new Intent("3"); // Use a unique action name to ensure a different PendingIntent to be created.
804  29 intent.setComponent(new ComponentName(context, DownloadServiceImpl.class));
805  29 intent.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_PREVIOUS));
806  29 pendingIntent = PendingIntent.getService(context, 0, intent, 0);
807  29 views.setOnClickPendingIntent(R.id.control_previous, pendingIntent);
808    }
809    }