aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--res/drawable-hdpi/ic_social_person.pngbin0 -> 4518 bytes
-rw-r--r--res/drawable-mdpi/ic_social_person.pngbin0 -> 2834 bytes
-rw-r--r--res/drawable-xhdpi/ic_social_person.pngbin0 -> 5960 bytes
-rw-r--r--res/drawable-xxhdpi/ic_social_person.pngbin0 -> 9169 bytes
-rw-r--r--res/layout/user_header.xml57
-rw-r--r--res/layout/user_list_item.xml45
-rw-r--r--src/github/daneren2005/dsub/fragments/AdminFragment.java2
-rw-r--r--src/github/daneren2005/dsub/fragments/UserFragment.java23
-rw-r--r--src/github/daneren2005/dsub/service/CachedMusicService.java4
-rw-r--r--src/github/daneren2005/dsub/service/MusicService.java2
-rw-r--r--src/github/daneren2005/dsub/service/OfflineMusicService.java2
-rw-r--r--src/github/daneren2005/dsub/service/RESTMusicService.java53
-rw-r--r--src/github/daneren2005/dsub/util/FileUtil.java28
-rw-r--r--src/github/daneren2005/dsub/util/ImageLoader.java59
-rw-r--r--src/github/daneren2005/dsub/view/UserAdapter.java7
-rw-r--r--src/github/daneren2005/dsub/view/UserView.java14
16 files changed, 277 insertions, 19 deletions
diff --git a/res/drawable-hdpi/ic_social_person.png b/res/drawable-hdpi/ic_social_person.png
new file mode 100644
index 00000000..0a0a5ff2
--- /dev/null
+++ b/res/drawable-hdpi/ic_social_person.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_social_person.png b/res/drawable-mdpi/ic_social_person.png
new file mode 100644
index 00000000..c09313d8
--- /dev/null
+++ b/res/drawable-mdpi/ic_social_person.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_social_person.png b/res/drawable-xhdpi/ic_social_person.png
new file mode 100644
index 00000000..ed333afe
--- /dev/null
+++ b/res/drawable-xhdpi/ic_social_person.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_social_person.png b/res/drawable-xxhdpi/ic_social_person.png
new file mode 100644
index 00000000..f81dc6a4
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_social_person.png
Binary files differ
diff --git a/res/layout/user_header.xml b/res/layout/user_header.xml
new file mode 100644
index 00000000..5966e0ed
--- /dev/null
+++ b/res/layout/user_header.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/select_album_header"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content">
+
+ <ImageView
+ android:id="@+id/user_avatar"
+ android:src="@drawable/ic_social_person"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentTop="true"
+ android:layout_alignParentLeft="true"
+ android:layout_marginRight="10dip"
+ android:scaleType="fitCenter"
+ android:contentDescription="@null"/>
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_toRightOf="@+id/user_avatar"
+ android:orientation="vertical"
+ android:layout_centerVertical="true">
+
+ <TextView
+ android:text="Username"
+ android:id="@+id/user_username"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:textStyle="bold"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:marqueeRepeatLimit="marquee_forever"
+ android:scrollHorizontally="true"
+ android:focusable="true"
+ android:focusableInTouchMode="true">
+
+ <requestFocus android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:duplicateParentState="true" />
+ </TextView>
+
+ <TextView
+ android:text="Email"
+ android:id="@+id/user_email"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:autoLink="email"/>
+
+ </LinearLayout>
+</RelativeLayout>
+
+
diff --git a/res/layout/user_list_item.xml b/res/layout/user_list_item.xml
new file mode 100644
index 00000000..40dcf06d
--- /dev/null
+++ b/res/layout/user_list_item.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:background="@android:color/transparent">
+
+ <ImageView
+ android:id="@+id/item_avatar"
+ android:src="@drawable/ic_social_person"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_gravity="left|center_vertical"/>
+
+ <TextView
+ android:id="@+id/item_name"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:gravity="left|center_vertical"
+ android:paddingLeft="6dip"
+ android:paddingRight="6dip"
+ android:minHeight="50dip"
+ android:background="@android:color/transparent"/>
+
+ <ImageButton
+ android:id="@+id/item_star"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="right|center_vertical"
+ android:src="@drawable/ic_stat_star"
+ android:background="@android:color/transparent"
+ android:focusable="false"
+ android:visibility="gone"/>
+
+ <ImageView
+ android:id="@+id/item_more"
+ android:src="?attr/download_none"
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:layout_gravity="right|center_vertical"
+ android:paddingRight="10dip"
+ android:background="@drawable/menubar_button"/>
+</LinearLayout> \ No newline at end of file
diff --git a/src/github/daneren2005/dsub/fragments/AdminFragment.java b/src/github/daneren2005/dsub/fragments/AdminFragment.java
index e0989e43..216ac000 100644
--- a/src/github/daneren2005/dsub/fragments/AdminFragment.java
+++ b/src/github/daneren2005/dsub/fragments/AdminFragment.java
@@ -91,7 +91,7 @@ public class AdminFragment extends SelectListFragment<User> {
@Override
public ArrayAdapter getAdapter(List<User> objs) {
- return new UserAdapter(context, objs);
+ return new UserAdapter(context, objs, getImageLoader());
}
@Override
diff --git a/src/github/daneren2005/dsub/fragments/UserFragment.java b/src/github/daneren2005/dsub/fragments/UserFragment.java
index 6423ebcd..fbd672c0 100644
--- a/src/github/daneren2005/dsub/fragments/UserFragment.java
+++ b/src/github/daneren2005/dsub/fragments/UserFragment.java
@@ -26,6 +26,7 @@ import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
@@ -37,6 +38,7 @@ import github.daneren2005.dsub.service.MusicServiceFactory;
import github.daneren2005.dsub.service.OfflineException;
import github.daneren2005.dsub.service.ServerTooOldException;
import github.daneren2005.dsub.util.Constants;
+import github.daneren2005.dsub.util.ImageLoader;
import github.daneren2005.dsub.util.SilentBackgroundTask;
import github.daneren2005.dsub.util.UserUtil;
import github.daneren2005.dsub.util.Util;
@@ -57,6 +59,7 @@ public class UserFragment extends SubsonicFragment{
user = (User) args.getSerializable(Constants.INTENT_EXTRA_NAME_ID);
listView = (ListView)rootView.findViewById(R.id.fragment_list);
+ createHeader();
listView.setAdapter(new SettingsAdapter(context, user.getSettings(), UserUtil.isCurrentAdmin()));
setTitle(user.getUsername());
@@ -104,4 +107,24 @@ public class UserFragment extends SubsonicFragment{
return false;
}
+
+ private void createHeader() {
+ View header = LayoutInflater.from(context).inflate(R.layout.user_header, listView, false);
+
+ final ImageLoader imageLoader = getImageLoader();
+ ImageView coverArtView = (ImageView) header.findViewById(R.id.user_avatar);
+ imageLoader.loadAvatar(context, coverArtView, user.getUsername());
+
+ TextView usernameView = (TextView) header.findViewById(R.id.user_username);
+ usernameView.setText(user.getUsername());
+
+ final TextView emailView = (TextView) header.findViewById(R.id.user_email);
+ if(user.getEmail() != null) {
+ emailView.setText(user.getEmail());
+ } else {
+ emailView.setVisibility(View.GONE);
+ }
+
+ listView.addHeaderView(header);
+ }
}
diff --git a/src/github/daneren2005/dsub/service/CachedMusicService.java b/src/github/daneren2005/dsub/service/CachedMusicService.java
index 0049e0ac..bd43fbed 100644
--- a/src/github/daneren2005/dsub/service/CachedMusicService.java
+++ b/src/github/daneren2005/dsub/service/CachedMusicService.java
@@ -577,8 +577,8 @@ public class CachedMusicService implements MusicService {
}
@Override
- public Bitmap getAvatar(String username, Context context, ProgressListener progressListener) throws Exception {
- return musicService.getAvatar(username, context, progressListener);
+ public Bitmap getAvatar(String username, int size, Context context, ProgressListener progressListener) throws Exception {
+ return musicService.getAvatar(username, size, context, progressListener);
}
@Override
diff --git a/src/github/daneren2005/dsub/service/MusicService.java b/src/github/daneren2005/dsub/service/MusicService.java
index dc760f86..cefd0c23 100644
--- a/src/github/daneren2005/dsub/service/MusicService.java
+++ b/src/github/daneren2005/dsub/service/MusicService.java
@@ -176,7 +176,7 @@ public interface MusicService {
void changePassword(String username, String password, Context context, ProgressListener progressListener) throws Exception;
- Bitmap getAvatar(String username, Context context, ProgressListener progressListener) throws Exception;
+ Bitmap getAvatar(String username, int size, Context context, ProgressListener progressListener) throws Exception;
int processOfflineSyncs(final Context context, final ProgressListener progressListener) throws Exception;
diff --git a/src/github/daneren2005/dsub/service/OfflineMusicService.java b/src/github/daneren2005/dsub/service/OfflineMusicService.java
index 75a090c6..a92e41d1 100644
--- a/src/github/daneren2005/dsub/service/OfflineMusicService.java
+++ b/src/github/daneren2005/dsub/service/OfflineMusicService.java
@@ -726,7 +726,7 @@ public class OfflineMusicService extends RESTMusicService {
}
@Override
- public Bitmap getAvatar(String username, Context context, ProgressListener progressListener) throws Exception {
+ public Bitmap getAvatar(String username, int size, Context context, ProgressListener progressListener) throws Exception {
return null;
}
diff --git a/src/github/daneren2005/dsub/service/RESTMusicService.java b/src/github/daneren2005/dsub/service/RESTMusicService.java
index 371e309c..11431eaa 100644
--- a/src/github/daneren2005/dsub/service/RESTMusicService.java
+++ b/src/github/daneren2005/dsub/service/RESTMusicService.java
@@ -1284,8 +1284,57 @@ public class RESTMusicService implements MusicService {
}
@Override
- public Bitmap getAvatar(String username, Context context, ProgressListener progressListener) throws Exception {
- return null;
+ public Bitmap getAvatar(String username, int size, Context context, ProgressListener progressListener) throws Exception {
+ // Return silently if server is too old
+ if (!Util.checkServerVersion(context, "1.8")) {
+ return null;
+ }
+
+ // Synchronize on the username so that we don't download concurrently for
+ // the same user.
+ synchronized (username) {
+ // Use cached file, if existing.
+ Bitmap bitmap = FileUtil.getAvatarBitmap(context, username, size);
+ if(bitmap != null) {
+ return bitmap;
+ }
+
+ String url = Util.getRestUrl(context, "getAvatar");
+ InputStream in = null;
+ try
+ {
+ List<String> parameterNames;
+ List<Object> parameterValues;
+
+ parameterNames = Collections.singletonList("username");
+ parameterValues = Arrays.<Object>asList(username);
+
+ HttpEntity entity = getEntityForURL(context, url, null, parameterNames, parameterValues, progressListener);
+ in = entity.getContent();
+
+ // If content type is XML, an error occurred. Get it.
+ String contentType = Util.getContentType(entity);
+ if (contentType != null && contentType.startsWith("text/xml"))
+ {
+ new ErrorParser(context).parse(new InputStreamReader(in, Constants.UTF_8));
+ return null; // Never reached.
+ }
+
+ byte[] bytes = Util.toByteArray(in);
+ OutputStream out = null;
+ try {
+ out = new FileOutputStream(FileUtil.getAvatarFile(context, username));
+ out.write(bytes);
+ } finally {
+ Util.close(out);
+ }
+
+ return FileUtil.getSampledBitmap(bytes, size);
+ }
+ finally {
+ Util.close(in);
+ }
+ }
}
@Override
diff --git a/src/github/daneren2005/dsub/util/FileUtil.java b/src/github/daneren2005/dsub/util/FileUtil.java
index a68ded45..e84f6eb2 100644
--- a/src/github/daneren2005/dsub/util/FileUtil.java
+++ b/src/github/daneren2005/dsub/util/FileUtil.java
@@ -200,6 +200,34 @@ public class FileUtil {
}
return null;
}
+
+ public static File getAvatarDirectory(Context context) {
+ File avatarDir = new File(getSubsonicDirectory(context), "avatars");
+ ensureDirectoryExistsAndIsReadWritable(avatarDir);
+ ensureDirectoryExistsAndIsReadWritable(new File(avatarDir, ".nomedia"));
+ return avatarDir;
+ }
+
+ public static File getAvatarFile(Context context, String username) {
+ return new File(getAvatarDirectory(context), Util.md5Hex(username) + ".jpeg");
+ }
+
+ public static Bitmap getAvatarBitmap(Context context, String username, int size) {
+ File avatarFile = getAvatarFile(context, username);
+ if (avatarFile.exists()) {
+ final BitmapFactory.Options opt = new BitmapFactory.Options();
+ opt.inJustDecodeBounds = true;
+ BitmapFactory.decodeFile(avatarFile.getPath(), opt);
+ opt.inPurgeable = true;
+ opt.inSampleSize = Util.calculateInSampleSize(opt, size, Util.getScaledHeight(opt.outHeight, opt.outWidth, size));
+ opt.inJustDecodeBounds = false;
+
+ Bitmap bitmap = BitmapFactory.decodeFile(avatarFile.getPath(), opt);
+ return bitmap == null ? null : getScaledBitmap(bitmap, size);
+ }
+ return null;
+ }
+
public static Bitmap getSampledBitmap(byte[] bytes, int size) {
final BitmapFactory.Options opt = new BitmapFactory.Options();
opt.inJustDecodeBounds = true;
diff --git a/src/github/daneren2005/dsub/util/ImageLoader.java b/src/github/daneren2005/dsub/util/ImageLoader.java
index b1b00cc5..f9c5fed5 100644
--- a/src/github/daneren2005/dsub/util/ImageLoader.java
+++ b/src/github/daneren2005/dsub/util/ImageLoader.java
@@ -18,7 +18,6 @@
*/
package github.daneren2005.dsub.util;
-import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
@@ -26,7 +25,6 @@ import android.graphics.drawable.Drawable;
import android.graphics.drawable.TransitionDrawable;
import android.media.RemoteControlClient;
import android.os.Build;
-import android.os.Handler;
import android.util.DisplayMetrics;
import android.util.Log;
import android.support.v4.util.LruCache;
@@ -38,9 +36,6 @@ import github.daneren2005.dsub.domain.MusicDirectory;
import github.daneren2005.dsub.service.MusicService;
import github.daneren2005.dsub.service.MusicServiceFactory;
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.LinkedBlockingQueue;
-
/**
* Asynchronous loading of images, with caching.
* <p/>
@@ -56,6 +51,7 @@ public class ImageLoader {
private Bitmap nowPlaying;
private final int imageSizeDefault;
private final int imageSizeLarge;
+ private final int avatarSizeDefault;
private Drawable largeUnknownImage;
public ImageLoader(Context context) {
@@ -86,6 +82,7 @@ public class ImageLoader {
imageSizeDefault = context.getResources().getDrawable(R.drawable.unknown_album).getIntrinsicHeight();
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
imageSizeLarge = Math.round(Math.min(metrics.widthPixels, metrics.heightPixels));
+ avatarSizeDefault = context.getResources().getDrawable(R.drawable.ic_social_person).getIntrinsicHeight();
createLargeUnknownImage(context);
}
@@ -173,6 +170,17 @@ public class ImageLoader {
new RemoteControlClientImageTask(context, entry, imageSizeLarge, imageSizeLarge, false, remoteControl).execute();
}
+ public void loadAvatar(Context context, ImageView view, String username) {
+ Bitmap bitmap = cache.get(username);
+ if (bitmap != null && !bitmap.isRecycled()) {
+ Drawable drawable = Util.createDrawableFromBitmap(this.context, bitmap);
+ view.setImageDrawable(drawable);
+ return;
+ }
+
+ new AvatarTask(context, view, username).execute();
+ }
+
private String getKey(String coverArtId, int size) {
return coverArtId + size;
}
@@ -322,4 +330,45 @@ public class ImageLoader {
setImage(mRemoteControl, mDrawable);
}
}
+
+ private class AvatarTask extends SilentBackgroundTask<Void> {
+ private final Context mContext;
+ private final String mUsername;
+ private final ImageView mView;
+ private Drawable mDrawable;
+
+ public AvatarTask(Context context, ImageView view, String username) {
+ super(context);
+ mContext = context;
+ mView = view;
+ mUsername = username;
+ }
+
+ @Override
+ protected Void doInBackground() throws Throwable {
+ try {
+ MusicService musicService = MusicServiceFactory.getMusicService(mContext);
+ Bitmap bitmap = musicService.getAvatar(mUsername, avatarSizeDefault, mContext, null);
+ if(bitmap != null) {
+ cache.put(mUsername, bitmap);
+ // Make sure key is the most recently "used"
+ cache.get(mUsername);
+
+ mDrawable = Util.createDrawableFromBitmap(mContext, bitmap);
+ }
+ } catch (Throwable x) {
+ Log.e(TAG, "Failed to download album art.", x);
+ cancelled = true;
+ }
+
+ return null;
+ }
+
+ @Override
+ protected void done(Void result) {
+ if(mDrawable != null) {
+ mView.setImageDrawable(mDrawable);
+ }
+ }
+ }
}
diff --git a/src/github/daneren2005/dsub/view/UserAdapter.java b/src/github/daneren2005/dsub/view/UserAdapter.java
index 937e2e92..70a1748a 100644
--- a/src/github/daneren2005/dsub/view/UserAdapter.java
+++ b/src/github/daneren2005/dsub/view/UserAdapter.java
@@ -24,13 +24,16 @@ import java.util.List;
import github.daneren2005.dsub.R;
import github.daneren2005.dsub.domain.User;
+import github.daneren2005.dsub.util.ImageLoader;
public class UserAdapter extends ArrayAdapter<User> {
private final Context activity;
+ private final ImageLoader imageLoader;
- public UserAdapter(Context activity, List<User> users) {
+ public UserAdapter(Context activity, List<User> users, ImageLoader imageLoader) {
super(activity, R.layout.basic_list_item, users);
this.activity = activity;
+ this.imageLoader = imageLoader;
}
@Override
@@ -42,7 +45,7 @@ public class UserAdapter extends ArrayAdapter<User> {
} else {
view = new UserView(activity);
}
- view.setObject(entry);
+ view.setObject(entry, imageLoader);
return view;
}
} \ No newline at end of file
diff --git a/src/github/daneren2005/dsub/view/UserView.java b/src/github/daneren2005/dsub/view/UserView.java
index f966f28f..31e02d99 100644
--- a/src/github/daneren2005/dsub/view/UserView.java
+++ b/src/github/daneren2005/dsub/view/UserView.java
@@ -23,18 +23,21 @@ import android.widget.TextView;
import github.daneren2005.dsub.R;
import github.daneren2005.dsub.domain.User;
+import github.daneren2005.dsub.util.ImageLoader;
public class UserView extends UpdateView {
private User user;
- private TextView userNameView;
+ private TextView usernameView;
+ private ImageView avatarView;
public UserView(Context context) {
super(context);
this.context = context;
- LayoutInflater.from(context).inflate(R.layout.basic_list_item, this, true);
+ LayoutInflater.from(context).inflate(R.layout.user_list_item, this, true);
- userNameView = (TextView) findViewById(R.id.item_name);
+ usernameView = (TextView) findViewById(R.id.item_name);
+ avatarView = (ImageView) findViewById(R.id.item_avatar);
moreButton = (ImageView) findViewById(R.id.item_more);
moreButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
@@ -43,8 +46,9 @@ public class UserView extends UpdateView {
});
}
- protected void setObjectImpl(Object obj) {
+ protected void setObjectImpl(Object obj, Object obj2) {
this.user = (User) obj;
- userNameView.setText(user.getUsername());
+ usernameView.setText(user.getUsername());
+ ((ImageLoader)obj2).loadAvatar(context, avatarView, user.getUsername());
}
}