diff options
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 Binary files differnew file mode 100644 index 00000000..0a0a5ff2 --- /dev/null +++ b/res/drawable-hdpi/ic_social_person.png diff --git a/res/drawable-mdpi/ic_social_person.png b/res/drawable-mdpi/ic_social_person.png Binary files differnew file mode 100644 index 00000000..c09313d8 --- /dev/null +++ b/res/drawable-mdpi/ic_social_person.png diff --git a/res/drawable-xhdpi/ic_social_person.png b/res/drawable-xhdpi/ic_social_person.png Binary files differnew file mode 100644 index 00000000..ed333afe --- /dev/null +++ b/res/drawable-xhdpi/ic_social_person.png diff --git a/res/drawable-xxhdpi/ic_social_person.png b/res/drawable-xxhdpi/ic_social_person.png Binary files differnew file mode 100644 index 00000000..f81dc6a4 --- /dev/null +++ b/res/drawable-xxhdpi/ic_social_person.png 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());
}
}
|