Clover Coverage Report - Subsonic-Android Coverage Report
Coverage timestamp: ven dic 19 2014 17:57:13 EST
../../../../../img/srcFileCovDistChart1.png 85% of files have more coverage
84   238   25   9,33
28   148   0,3   9
9     2,78  
1    
This report was generated with an evaluation server license. Purchase Clover or configure your license.
 
  SubsonicAppWidgetProvider4x1       Line # 56 84 25 10,7% 0.10743801
 
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 2010 (C) Sindre Mehus
18    */
19    package net.sourceforge.subsonic.androidapp.provider;
20   
21    import android.app.PendingIntent;
22    import android.appwidget.AppWidgetManager;
23    import android.appwidget.AppWidgetProvider;
24    import android.content.ComponentName;
25    import android.content.Context;
26    import android.content.Intent;
27    import android.content.res.Resources;
28    import android.graphics.Bitmap;
29    import android.graphics.Bitmap.Config;
30    import android.graphics.Canvas;
31    import android.graphics.Paint;
32    import android.graphics.PorterDuff.Mode;
33    import android.graphics.PorterDuffXfermode;
34    import android.graphics.Rect;
35    import android.graphics.RectF;
36    import android.os.Environment;
37    import android.util.Log;
38    import android.view.KeyEvent;
39    import android.widget.RemoteViews;
40    import net.sourceforge.subsonic.androidapp.R;
41    import net.sourceforge.subsonic.androidapp.activity.DownloadActivity;
42    import net.sourceforge.subsonic.androidapp.activity.MainActivity;
43    import net.sourceforge.subsonic.androidapp.domain.MusicDirectory;
44    import net.sourceforge.subsonic.androidapp.service.DownloadService;
45    import net.sourceforge.subsonic.androidapp.service.DownloadServiceImpl;
46    import net.sourceforge.subsonic.androidapp.util.FileUtil;
47   
48    /**
49    * Simple widget to show currently playing album art along
50    * with play/pause and next track buttons.
51    * <p/>
52    * Based on source code from the stock Android Music app.
53    *
54    * @author Sindre Mehus
55    */
 
56    public class SubsonicAppWidgetProvider4x1 extends AppWidgetProvider {
57   
58    private static SubsonicAppWidgetProvider4x1 instance;
59    private static final String TAG = SubsonicAppWidgetProvider4x1.class.getSimpleName();
60   
 
61  47 toggle public static synchronized SubsonicAppWidgetProvider4x1 getInstance() {
62  47 if (instance == null) {
63  1 instance = new SubsonicAppWidgetProvider4x1();
64    }
65  47 return instance;
66    }
67   
 
68  0 toggle @Override
69    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
70  0 defaultAppWidget(context, appWidgetIds);
71    }
72   
73    /**
74    * Initialize given widgets to default state, where we launch Subsonic on default click
75    * and hide actions if service not running.
76    */
 
77  0 toggle private void defaultAppWidget(Context context, int[] appWidgetIds) {
78  0 final Resources res = context.getResources();
79  0 final RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.appwidget4x1);
80   
81  0 views.setTextViewText(R.id.artist, res.getText(R.string.widget_initial_text));
82   
83  0 linkButtons(context, views, false);
84  0 pushUpdate(context, appWidgetIds, views);
85    }
86   
 
87  0 toggle private void pushUpdate(Context context, int[] appWidgetIds, RemoteViews views) {
88    // Update specific list of appWidgetIds if given, otherwise default to all
89  0 final AppWidgetManager manager = AppWidgetManager.getInstance(context);
90  0 if (appWidgetIds != null) {
91  0 manager.updateAppWidget(appWidgetIds, views);
92    } else {
93  0 manager.updateAppWidget(new ComponentName(context, this.getClass()), views);
94    }
95    }
96   
97    /**
98    * Handle a change notification coming over from {@link DownloadService}
99    */
 
100  47 toggle public void notifyChange(Context context, DownloadService service, boolean playing) {
101  47 if (hasInstances(context)) {
102  0 performUpdate(context, service, null, playing);
103    }
104    }
105   
106    /**
107    * Check against {@link AppWidgetManager} if there are any instances of this widget.
108    */
 
109  47 toggle private boolean hasInstances(Context context) {
110  47 AppWidgetManager manager = AppWidgetManager.getInstance(context);
111  47 int[] appWidgetIds = manager.getAppWidgetIds(new ComponentName(context, getClass()));
112  47 return (appWidgetIds.length > 0);
113    }
114   
115    /**
116    * Update all active widget instances by pushing changes
117    */
 
118  0 toggle private void performUpdate(Context context, DownloadService service, int[] appWidgetIds, boolean playing) {
119  0 final Resources res = context.getResources();
120  0 final RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.appwidget4x1);
121   
122  0 MusicDirectory.Entry currentPlaying = service.getCurrentPlaying() == null ? null : service.getCurrentPlaying().getSong();
123  0 String title = currentPlaying == null ? null : currentPlaying.getTitle();
124  0 CharSequence artist = currentPlaying == null ? null : currentPlaying.getArtist();
125  0 CharSequence errorState = null;
126   
127    // Show error message?
128  0 String status = Environment.getExternalStorageState();
129  0 if (status.equals(Environment.MEDIA_SHARED) ||
130    status.equals(Environment.MEDIA_UNMOUNTED)) {
131  0 errorState = res.getText(R.string.widget_sdcard_busy);
132  0 } else if (status.equals(Environment.MEDIA_REMOVED)) {
133  0 errorState = res.getText(R.string.widget_sdcard_missing);
134  0 } else if (currentPlaying == null) {
135  0 errorState = res.getText(R.string.widget_initial_text);
136    }
137   
138  0 if (errorState != null) {
139    // Show error state to user
140  0 views.setTextViewText(R.id.title,null);
141  0 views.setTextViewText(R.id.artist, errorState);
142  0 views.setImageViewResource(R.id.appwidget_coverart, R.drawable.appwidget_art_default);
143    } else {
144    // No error, so show normal titles
145  0 views.setTextViewText(R.id.title, title);
146  0 views.setTextViewText(R.id.artist, artist);
147    }
148   
149    // Set correct drawable for pause state
150  0 if (playing) {
151  0 views.setImageViewResource(R.id.control_play, R.drawable.ic_appwidget_music_pause);
152    } else {
153  0 views.setImageViewResource(R.id.control_play, R.drawable.ic_appwidget_music_play);
154    }
155   
156    // Set the cover art
157  0 try {
158  0 int size = context.getResources().getDrawable(R.drawable.appwidget_art_default).getIntrinsicHeight();
159  0 Bitmap bitmap = currentPlaying == null ? null : FileUtil.getAlbumArtBitmap(context, currentPlaying, size);
160   
161  0 if (bitmap == null) {
162    // Set default cover art
163  0 views.setImageViewResource(R.id.appwidget_coverart, R.drawable.appwidget_art_default);
164    } else {
165  0 bitmap = getRoundedCornerBitmap(bitmap);
166  0 views.setImageViewBitmap(R.id.appwidget_coverart, bitmap);
167    }
168    } catch (Exception x) {
169  0 Log.e(TAG, "Failed to load cover art", x);
170  0 views.setImageViewResource(R.id.appwidget_coverart, R.drawable.appwidget_art_default);
171    }
172   
173    // Link actions buttons to intents
174  0 linkButtons(context, views, currentPlaying != null);
175   
176  0 pushUpdate(context, appWidgetIds, views);
177    }
178   
179    /**
180    * Round the corners of a bitmap for the cover art image
181    */
 
182  0 toggle private static Bitmap getRoundedCornerBitmap(Bitmap bitmap) {
183  0 Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Config.ARGB_8888);
184  0 Canvas canvas = new Canvas(output);
185   
186  0 final int color = 0xff424242;
187  0 final Paint paint = new Paint();
188  0 final float roundPx = 10;
189   
190    // Add extra width to the rect so the right side wont be rounded.
191  0 final Rect rect = new Rect(0, 0, bitmap.getWidth() + (int) roundPx, bitmap.getHeight());
192  0 final RectF rectF = new RectF(rect);
193   
194  0 paint.setAntiAlias(true);
195  0 canvas.drawARGB(0, 0, 0, 0);
196  0 paint.setColor(color);
197  0 canvas.drawRoundRect(rectF, roundPx, roundPx, paint);
198   
199  0 paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
200  0 canvas.drawBitmap(bitmap, rect, rect, paint);
201   
202  0 return output;
203    }
204   
205    /**
206    * Link up various button actions using {@link PendingIntent}.
207    *
208    * @param playerActive True if player is active in background, which means
209    * widget click will launch {@link DownloadActivity},
210    * otherwise we launch {@link MainActivity}.
211    */
 
212  0 toggle private void linkButtons(Context context, RemoteViews views, boolean playerActive) {
213   
214  0 Intent intent = new Intent(context, playerActive ? DownloadActivity.class : MainActivity.class);
215  0 PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
216  0 views.setOnClickPendingIntent(R.id.appwidget_coverart, pendingIntent);
217  0 views.setOnClickPendingIntent(R.id.appwidget_top, pendingIntent);
218   
219    // Emulate media button clicks.
220  0 intent = new Intent("1");
221  0 intent.setComponent(new ComponentName(context, DownloadServiceImpl.class));
222  0 intent.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE));
223  0 pendingIntent = PendingIntent.getService(context, 0, intent, 0);
224  0 views.setOnClickPendingIntent(R.id.control_play, pendingIntent);
225   
226  0 intent = new Intent("2"); // Use a unique action name to ensure a different PendingIntent to be created.
227  0 intent.setComponent(new ComponentName(context, DownloadServiceImpl.class));
228  0 intent.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_NEXT));
229  0 pendingIntent = PendingIntent.getService(context, 0, intent, 0);
230  0 views.setOnClickPendingIntent(R.id.control_next, pendingIntent);
231   
232  0 intent = new Intent("3"); // Use a unique action name to ensure a different PendingIntent to be created.
233  0 intent.setComponent(new ComponentName(context, DownloadServiceImpl.class));
234  0 intent.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_PREVIOUS));
235  0 pendingIntent = PendingIntent.getService(context, 0, intent, 0);
236  0 views.setOnClickPendingIntent(R.id.control_previous, pendingIntent);
237    }
238    }