Reverted DownloadActivity and ImageLoader changes, updated icons, tweaked icon padding

This commit is contained in:
Joshua Bahnsen 2012-06-08 00:05:57 -07:00
parent 0bbe59d814
commit 1aa3e1139c
18 changed files with 278 additions and 269 deletions

View File

@ -2,7 +2,7 @@
<manifest xmlns:a="http://schemas.android.com/apk/res/android" <manifest xmlns:a="http://schemas.android.com/apk/res/android"
package="net.sourceforge.subsonic.androidapp" package="net.sourceforge.subsonic.androidapp"
a:versionCode="47" a:versionCode="47"
a:versionName="3.9.9" a:installLocation="auto"> a:versionName="3.9.9.1" a:installLocation="auto">
<uses-permission a:name="android.permission.INTERNET"/> <uses-permission a:name="android.permission.INTERNET"/>
<uses-permission a:name="android.permission.READ_PHONE_STATE"/> <uses-permission a:name="android.permission.READ_PHONE_STATE"/>

11
default.properties Normal file
View File

@ -0,0 +1,11 @@
# This file is automatically generated by Android Tools.
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
#
# This file must be checked in Version Control Systems.
#
# To customize properties used by the Ant build system use,
# "ant.properties", and override values to adapt the script to your
# project structure.
# Project target.
target=android-15

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 923 B

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -189,13 +189,12 @@
a:layout_alignParentLeft="true" a:layout_alignParentLeft="true"
a:layout_toLeftOf="@+id/download_control_layout" a:layout_toLeftOf="@+id/download_control_layout"
> >
<FrameLayout
a:id="@+id/download_album_art_layout"
a:layout_width="fill_parent"
a:layout_height="fill_parent"
a:background="@drawable/pinstripe_tile" >
<FrameLayout
a:id="@+id/download_album_art_layout"
a:layout_width="fill_parent"
a:layout_height="fill_parent"
a:background="@drawable/album_art_background">
<ImageView <ImageView
a:id="@+id/download_album_art_image" a:id="@+id/download_album_art_image"
a:layout_width="wrap_content" a:layout_width="wrap_content"

View File

@ -9,14 +9,14 @@
a:layout_width="fill_parent" a:layout_width="fill_parent"
a:layout_height="0dip" a:layout_height="0dip"
a:layout_weight="1"> a:layout_weight="1">
<RelativeLayout <RelativeLayout
a:id="@+id/download_album_art_layout" a:id="@+id/download_album_art_layout"
a:layout_width="fill_parent" a:orientation="vertical"
a:layout_height="fill_parent" a:layout_width="fill_parent"
a:layout_weight="1" a:layout_height="fill_parent"
a:background="@drawable/pinstripe_tile" a:layout_weight="1"
a:orientation="vertical" > a:background="@drawable/album_art_background">
<ImageButton <ImageButton
a:id="@+id/download_repeat" a:id="@+id/download_repeat"
@ -40,7 +40,7 @@
a:layout_gravity="top|right" a:layout_gravity="top|right"
a:layout_below="@+id/download_repeat" a:layout_below="@+id/download_repeat"
a:paddingTop="12dip" a:paddingTop="12dip"
a:paddingLeft="14dip" a:paddingLeft="0dip"
a:paddingBottom="15dip"/> a:paddingBottom="15dip"/>
<Button <Button
@ -140,7 +140,7 @@
a:background="@drawable/list_selector_holo_dark" a:background="@drawable/list_selector_holo_dark"
a:paddingBottom="12dip" a:paddingBottom="12dip"
a:paddingLeft="8dip" a:paddingLeft="8dip"
a:paddingTop="24dip" a:paddingTop="12dip"
a:src="@drawable/ic_menu_moreoverflow" /> a:src="@drawable/ic_menu_moreoverflow" />
<ImageButton <ImageButton

View File

@ -1,253 +1,252 @@
/* /*
This file is part of Subsonic. This file is part of Subsonic.
Subsonic is free software: you can redistribute it and/or modify Subsonic is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
Subsonic is distributed in the hope that it will be useful, Subsonic is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with Subsonic. If not, see <http://www.gnu.org/licenses/>. along with Subsonic. If not, see <http://www.gnu.org/licenses/>.
Copyright 2009 (C) Sindre Mehus Copyright 2009 (C) Sindre Mehus
*/ */
package net.sourceforge.subsonic.androidapp.util; package net.sourceforge.subsonic.androidapp.util;
import android.content.Context; import android.content.Context;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.LinearGradient; import android.graphics.LinearGradient;
import android.graphics.Matrix; import android.graphics.Matrix;
import android.graphics.Paint; import android.graphics.Paint;
import android.graphics.Shader; import android.graphics.Shader;
import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.graphics.drawable.TransitionDrawable; import android.graphics.drawable.TransitionDrawable;
import android.os.Handler; import android.os.Handler;
import android.util.DisplayMetrics; import android.util.DisplayMetrics;
import android.util.Log; import android.util.Log;
import android.view.View; import android.view.View;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import net.sourceforge.subsonic.androidapp.R; import net.sourceforge.subsonic.androidapp.R;
import net.sourceforge.subsonic.androidapp.domain.MusicDirectory; import net.sourceforge.subsonic.androidapp.domain.MusicDirectory;
import net.sourceforge.subsonic.androidapp.service.MusicService; import net.sourceforge.subsonic.androidapp.service.MusicService;
import net.sourceforge.subsonic.androidapp.service.MusicServiceFactory; import net.sourceforge.subsonic.androidapp.service.MusicServiceFactory;
import java.util.concurrent.BlockingQueue; import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.LinkedBlockingQueue;
/** /**
* Asynchronous loading of images, with caching. * Asynchronous loading of images, with caching.
* <p/> * <p/>
* There should normally be only one instance of this class. * There should normally be only one instance of this class.
* *
* @author Sindre Mehus * @author Sindre Mehus
*/ */
public class ImageLoader implements Runnable { public class ImageLoader implements Runnable {
private static final String TAG = ImageLoader.class.getSimpleName(); private static final String TAG = ImageLoader.class.getSimpleName();
private static final int CONCURRENCY = 5; private static final int CONCURRENCY = 5;
private final LRUCache<String, Drawable> cache = new LRUCache<String, Drawable>(100); private final LRUCache<String, Drawable> cache = new LRUCache<String, Drawable>(100);
private final BlockingQueue<Task> queue; private final BlockingQueue<Task> queue;
private final int imageSizeDefault; private final int imageSizeDefault;
private final int imageSizeLarge; private final int imageSizeLarge;
private Drawable largeUnknownImage; private Drawable largeUnknownImage;
public ImageLoader(Context context) { public ImageLoader(Context context) {
queue = new LinkedBlockingQueue<Task>(500); queue = new LinkedBlockingQueue<Task>(500);
// Determine the density-dependent image sizes. // Determine the density-dependent image sizes.
imageSizeDefault = context.getResources().getDrawable(R.drawable.unknown_album).getIntrinsicHeight(); imageSizeDefault = context.getResources().getDrawable(R.drawable.unknown_album).getIntrinsicHeight();
DisplayMetrics metrics = context.getResources().getDisplayMetrics(); DisplayMetrics metrics = context.getResources().getDisplayMetrics();
/*imageSizeLarge = (int) Math.round(Math.min(metrics.widthPixels, metrics.heightPixels) * 0.6);*/ imageSizeLarge = (int) Math.round(Math.min(metrics.widthPixels, metrics.heightPixels) * 0.6);
imageSizeLarge = (int) Math.round(Math.min(metrics.widthPixels, metrics.heightPixels));
for (int i = 0; i < CONCURRENCY; i++) {
for (int i = 0; i < CONCURRENCY; i++) { new Thread(this, "ImageLoader").start();
new Thread(this, "ImageLoader").start(); }
}
createLargeUnknownImage(context);
createLargeUnknownImage(context); }
}
private void createLargeUnknownImage(Context context) {
private void createLargeUnknownImage(Context context) { BitmapDrawable drawable = (BitmapDrawable) context.getResources().getDrawable(R.drawable.unknown_album_large);
BitmapDrawable drawable = (BitmapDrawable) context.getResources().getDrawable(R.drawable.unknown_album_large); Bitmap bitmap = Bitmap.createScaledBitmap(drawable.getBitmap(), imageSizeLarge, imageSizeLarge, true);
Bitmap bitmap = Bitmap.createScaledBitmap(drawable.getBitmap(), imageSizeLarge, imageSizeLarge, true); bitmap = createReflection(bitmap);
bitmap = createReflection(bitmap); largeUnknownImage = Util.createDrawableFromBitmap(context, bitmap);
largeUnknownImage = Util.createDrawableFromBitmap(context, bitmap); }
}
public void loadImage(View view, MusicDirectory.Entry entry, boolean large, boolean crossfade) {
public void loadImage(View view, MusicDirectory.Entry entry, boolean large, boolean crossfade) { if (entry == null || entry.getCoverArt() == null) {
if (entry == null || entry.getCoverArt() == null) { setUnknownImage(view, large);
setUnknownImage(view, large); return;
return; }
}
int size = large ? imageSizeLarge : imageSizeDefault;
int size = large ? imageSizeLarge : imageSizeDefault; Drawable drawable = cache.get(getKey(entry.getCoverArt(), size));
Drawable drawable = cache.get(getKey(entry.getCoverArt(), size)); if (drawable != null) {
if (drawable != null) { setImage(view, drawable, large);
setImage(view, drawable, large); return;
return; }
}
if (!large) {
if (!large) { setUnknownImage(view, large);
setUnknownImage(view, large); }
} queue.offer(new Task(view, entry, size, large, large, crossfade));
queue.offer(new Task(view, entry, size, false, large, crossfade)); }
}
private String getKey(String coverArtId, int size) {
private String getKey(String coverArtId, int size) { return coverArtId + size;
return coverArtId + size; }
}
private void setImage(View view, Drawable drawable, boolean crossfade) {
private void setImage(View view, Drawable drawable, boolean crossfade) { if (view instanceof TextView) {
if (view instanceof TextView) { // Cross-fading is not implemented for TextView since it's not in use. It would be easy to add it, though.
// Cross-fading is not implemented for TextView since it's not in use. It would be easy to add it, though. TextView textView = (TextView) view;
TextView textView = (TextView) view; textView.setCompoundDrawablesWithIntrinsicBounds(drawable, null, null, null);
textView.setCompoundDrawablesWithIntrinsicBounds(drawable, null, null, null); } else if (view instanceof ImageView) {
} else if (view instanceof ImageView) { ImageView imageView = (ImageView) view;
ImageView imageView = (ImageView) view; if (crossfade) {
if (crossfade) {
Drawable existingDrawable = imageView.getDrawable();
Drawable existingDrawable = imageView.getDrawable(); if (existingDrawable == null) {
if (existingDrawable == null) { Bitmap emptyImage = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
Bitmap emptyImage = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); existingDrawable = new BitmapDrawable(emptyImage);
existingDrawable = new BitmapDrawable(emptyImage); }
}
Drawable[] layers = new Drawable[]{existingDrawable, drawable};
Drawable[] layers = new Drawable[]{existingDrawable, drawable};
TransitionDrawable transitionDrawable = new TransitionDrawable(layers);
TransitionDrawable transitionDrawable = new TransitionDrawable(layers); imageView.setImageDrawable(transitionDrawable);
imageView.setImageDrawable(transitionDrawable); transitionDrawable.startTransition(250);
transitionDrawable.startTransition(250); } else {
} else { imageView.setImageDrawable(drawable);
imageView.setImageDrawable(drawable); }
} }
} }
}
private void setUnknownImage(View view, boolean large) {
private void setUnknownImage(View view, boolean large) { if (large) {
if (large) { setImage(view, largeUnknownImage, false);
setImage(view, largeUnknownImage, false); } else {
} else { if (view instanceof TextView) {
if (view instanceof TextView) { ((TextView) view).setCompoundDrawablesWithIntrinsicBounds(R.drawable.unknown_album, 0, 0, 0);
((TextView) view).setCompoundDrawablesWithIntrinsicBounds(R.drawable.unknown_album, 0, 0, 0); } else if (view instanceof ImageView) {
} else if (view instanceof ImageView) { ((ImageView) view).setImageResource(R.drawable.unknown_album);
((ImageView) view).setImageResource(R.drawable.unknown_album); }
} }
} }
}
public void clear() {
public void clear() { queue.clear();
queue.clear(); }
}
@Override
@Override public void run() {
public void run() { while (true) {
while (true) { try {
try { Task task = queue.take();
Task task = queue.take(); task.execute();
task.execute(); } catch (Throwable x) {
} catch (Throwable x) { Log.e(TAG, "Unexpected exception in ImageLoader.", x);
Log.e(TAG, "Unexpected exception in ImageLoader.", x); }
} }
} }
}
private Bitmap createReflection(Bitmap originalImage) {
private Bitmap createReflection(Bitmap originalImage) {
int width = originalImage.getWidth();
int width = originalImage.getWidth(); int height = originalImage.getHeight();
int height = originalImage.getHeight();
// The gap we want between the reflection and the original image
// The gap we want between the reflection and the original image final int reflectionGap = 4;
final int reflectionGap = 4;
// This will not scale but will flip on the Y axis
// This will not scale but will flip on the Y axis Matrix matrix = new Matrix();
Matrix matrix = new Matrix(); matrix.preScale(1, -1);
matrix.preScale(1, -1);
// Create a Bitmap with the flip matix applied to it.
// Create a Bitmap with the flip matix applied to it. // We only want the bottom half of the image
// We only want the bottom half of the image Bitmap reflectionImage = Bitmap.createBitmap(originalImage, 0, height / 2, width, height / 2, matrix, false);
Bitmap reflectionImage = Bitmap.createBitmap(originalImage, 0, height / 2, width, height / 2, matrix, false);
// Create a new bitmap with same width but taller to fit reflection
// Create a new bitmap with same width but taller to fit reflection Bitmap bitmapWithReflection = Bitmap.createBitmap(width, (height + height / 2), Bitmap.Config.ARGB_8888);
Bitmap bitmapWithReflection = Bitmap.createBitmap(width, (height + height / 2), Bitmap.Config.ARGB_8888);
// Create a new Canvas with the bitmap that's big enough for
// Create a new Canvas with the bitmap that's big enough for // the image plus gap plus reflection
// the image plus gap plus reflection Canvas canvas = new Canvas(bitmapWithReflection);
Canvas canvas = new Canvas(bitmapWithReflection);
// Draw in the original image
// Draw in the original image canvas.drawBitmap(originalImage, 0, 0, null);
canvas.drawBitmap(originalImage, 0, 0, null);
// Draw in the gap
// Draw in the gap Paint defaultPaint = new Paint();
Paint defaultPaint = new Paint(); canvas.drawRect(0, height, width, height + reflectionGap, defaultPaint);
canvas.drawRect(0, height, width, height + reflectionGap, defaultPaint);
// Draw in the reflection
// Draw in the reflection canvas.drawBitmap(reflectionImage, 0, height + reflectionGap, null);
canvas.drawBitmap(reflectionImage, 0, height + reflectionGap, null);
// Create a shader that is a linear gradient that covers the reflection
// Create a shader that is a linear gradient that covers the reflection Paint paint = new Paint();
Paint paint = new Paint(); LinearGradient shader = new LinearGradient(0, originalImage.getHeight(), 0,
LinearGradient shader = new LinearGradient(0, originalImage.getHeight(), 0, bitmapWithReflection.getHeight() + reflectionGap, 0x70000000, 0xff000000,
bitmapWithReflection.getHeight() + reflectionGap, 0x70000000, 0xff000000, Shader.TileMode.CLAMP);
Shader.TileMode.CLAMP);
// Set the paint to use this shader (linear gradient)
// Set the paint to use this shader (linear gradient) paint.setShader(shader);
paint.setShader(shader);
// Draw a rectangle using the paint with our linear gradient
// Draw a rectangle using the paint with our linear gradient canvas.drawRect(0, height, width, bitmapWithReflection.getHeight() + reflectionGap, paint);
canvas.drawRect(0, height, width, bitmapWithReflection.getHeight() + reflectionGap, paint);
return bitmapWithReflection;
return bitmapWithReflection; }
}
private class Task {
private class Task { private final View view;
private final View view; private final MusicDirectory.Entry entry;
private final MusicDirectory.Entry entry; private final Handler handler;
private final Handler handler; private final int size;
private final int size; private final boolean reflection;
private final boolean reflection; private final boolean saveToFile;
private final boolean saveToFile; private final boolean crossfade;
private final boolean crossfade;
public Task(View view, MusicDirectory.Entry entry, int size, boolean reflection, boolean saveToFile, boolean crossfade) {
public Task(View view, MusicDirectory.Entry entry, int size, boolean reflection, boolean saveToFile, boolean crossfade) { this.view = view;
this.view = view; this.entry = entry;
this.entry = entry; this.size = size;
this.size = size; this.reflection = reflection;
this.reflection = reflection; this.saveToFile = saveToFile;
this.saveToFile = saveToFile; this.crossfade = crossfade;
this.crossfade = crossfade; handler = new Handler();
handler = new Handler(); }
}
public void execute() {
public void execute() { try {
try { MusicService musicService = MusicServiceFactory.getMusicService(view.getContext());
MusicService musicService = MusicServiceFactory.getMusicService(view.getContext()); Bitmap bitmap = musicService.getCoverArt(view.getContext(), entry, size, saveToFile, null);
Bitmap bitmap = musicService.getCoverArt(view.getContext(), entry, size, saveToFile, null);
if (reflection) {
if (reflection) { bitmap = createReflection(bitmap);
bitmap = createReflection(bitmap); }
}
final Drawable drawable = Util.createDrawableFromBitmap(view.getContext(), bitmap);
final Drawable drawable = Util.createDrawableFromBitmap(view.getContext(), bitmap); cache.put(getKey(entry.getCoverArt(), size), drawable);
cache.put(getKey(entry.getCoverArt(), size), drawable);
handler.post(new Runnable() {
handler.post(new Runnable() { @Override
@Override public void run() {
public void run() { setImage(view, drawable, crossfade);
setImage(view, drawable, crossfade); }
} });
}); } catch (Throwable x) {
} catch (Throwable x) { Log.e(TAG, "Failed to download album art.", x);
Log.e(TAG, "Failed to download album art.", x); }
} }
} }
} }
}