From 14893daf30e01cd1d99bbcbb1d222450be853126 Mon Sep 17 00:00:00 2001 From: Scott Jackson Date: Sat, 7 Jun 2014 13:12:45 -0700 Subject: #171 Add basic admin tab --- .../dsub/activity/SubsonicActivity.java | 11 +- .../dsub/activity/SubsonicFragmentActivity.java | 3 + src/github/daneren2005/dsub/domain/User.java | 161 +++++++++++++++++++++ .../daneren2005/dsub/fragments/AdminFragment.java | 64 ++++++++ .../dsub/service/CachedMusicService.java | 33 +++++ .../daneren2005/dsub/service/MusicService.java | 5 + .../dsub/service/OfflineMusicService.java | 15 +- .../daneren2005/dsub/service/RESTMusicService.java | 29 ++++ .../dsub/service/parser/UserParser.java | 73 ++++++++++ src/github/daneren2005/dsub/util/Constants.java | 1 + src/github/daneren2005/dsub/view/UserAdapter.java | 49 +++++++ src/github/daneren2005/dsub/view/UserView.java | 50 +++++++ 12 files changed, 489 insertions(+), 5 deletions(-) create mode 100644 src/github/daneren2005/dsub/domain/User.java create mode 100644 src/github/daneren2005/dsub/fragments/AdminFragment.java create mode 100644 src/github/daneren2005/dsub/service/parser/UserParser.java create mode 100644 src/github/daneren2005/dsub/view/UserAdapter.java create mode 100644 src/github/daneren2005/dsub/view/UserView.java (limited to 'src') diff --git a/src/github/daneren2005/dsub/activity/SubsonicActivity.java b/src/github/daneren2005/dsub/activity/SubsonicActivity.java index 646fa295..edec6b90 100644 --- a/src/github/daneren2005/dsub/activity/SubsonicActivity.java +++ b/src/github/daneren2005/dsub/activity/SubsonicActivity.java @@ -75,7 +75,7 @@ public class SubsonicActivity extends ActionBarActivity implements OnItemSelecte private String[] drawerItemsDescriptions; private String[] drawerItems; private boolean drawerIdle = true; - private boolean[] enabledItems = {true, true, true, true}; + private boolean[] enabledItems = {true, true, true, true, true}; private boolean destroyed = false; private boolean finished = false; protected List backStack = new ArrayList(); @@ -386,8 +386,9 @@ public class SubsonicActivity extends ActionBarActivity implements OnItemSelecte boolean bookmarksEnabled = prefs.getBoolean(Constants.PREFERENCES_KEY_BOOKMARKS_ENABLED, true) && !Util.isOffline(this); boolean sharedEnabled = prefs.getBoolean(Constants.PREFERENCES_KEY_SHARED_ENABLED, true) && !Util.isOffline(this); boolean chatEnabled = prefs.getBoolean(Constants.PREFERENCES_KEY_CHAT_ENABLED, true) && !Util.isOffline(this); + boolean adminEnabled = prefs.getBoolean(Constants.PREFERENCES_KEY_ADMIN_ENABLED, true) && !Util.isOffline(this); - if(drawerItems == null || !enabledItems[0] == podcastsEnabled || !enabledItems[1] == bookmarksEnabled || !enabledItems[2] == sharedEnabled || !enabledItems[3] == chatEnabled) { + if(drawerItems == null || !enabledItems[0] == podcastsEnabled || !enabledItems[1] == bookmarksEnabled || !enabledItems[2] == sharedEnabled || !enabledItems[3] == chatEnabled || !enabledItems[4] == adminEnabled) { drawerItems = getResources().getStringArray(R.array.drawerItems); drawerItemsDescriptions = getResources().getStringArray(R.array.drawerItemsDescriptions); @@ -419,15 +420,19 @@ public class SubsonicActivity extends ActionBarActivity implements OnItemSelecte if(!chatEnabled) { drawerItemsVisibleList.set(6, false); } - if(!getIntent().hasExtra(Constants.INTENT_EXTRA_NAME_DOWNLOAD_VIEW)) { + if(!adminEnabled) { drawerItemsVisibleList.set(7, false); } + if(!getIntent().hasExtra(Constants.INTENT_EXTRA_NAME_DOWNLOAD_VIEW)) { + drawerItemsVisibleList.set(8, false); + } drawerList.setAdapter(drawerAdapter = new DrawerAdapter(this, drawerItemsList, drawerItemsIconsList, drawerItemsVisibleList)); enabledItems[0] = podcastsEnabled; enabledItems[1] = bookmarksEnabled; enabledItems[2] = sharedEnabled; enabledItems[3] = chatEnabled; + enabledItems[4] = adminEnabled; String fragmentType = getIntent().getStringExtra(Constants.INTENT_EXTRA_FRAGMENT_TYPE); if(fragmentType != null && lastSelectedPosition == 0) { diff --git a/src/github/daneren2005/dsub/activity/SubsonicFragmentActivity.java b/src/github/daneren2005/dsub/activity/SubsonicFragmentActivity.java index ebedf9b1..2afdaf3f 100644 --- a/src/github/daneren2005/dsub/activity/SubsonicFragmentActivity.java +++ b/src/github/daneren2005/dsub/activity/SubsonicFragmentActivity.java @@ -44,6 +44,7 @@ import java.util.concurrent.TimeUnit; import github.daneren2005.dsub.R; import github.daneren2005.dsub.domain.MusicDirectory; import github.daneren2005.dsub.domain.PlayerState; +import github.daneren2005.dsub.fragments.AdminFragment; import github.daneren2005.dsub.fragments.ChatFragment; import github.daneren2005.dsub.fragments.DownloadFragment; import github.daneren2005.dsub.fragments.MainFragment; @@ -392,6 +393,8 @@ public class SubsonicFragmentActivity extends SubsonicActivity { return new SelectBookmarkFragment(); } else if("Share".equals(fragmentType)) { return new SelectShareFragment(); + } else if("Admin".equals(fragmentType)) { + return new AdminFragment(); } else if("Download".equals(fragmentType)) { return new DownloadFragment(); } else { diff --git a/src/github/daneren2005/dsub/domain/User.java b/src/github/daneren2005/dsub/domain/User.java new file mode 100644 index 00000000..63ead042 --- /dev/null +++ b/src/github/daneren2005/dsub/domain/User.java @@ -0,0 +1,161 @@ +/* + This file is part of Subsonic. + Subsonic is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + Subsonic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with Subsonic. If not, see . + Copyright 2014 (C) Scott Jackson +*/ + +package github.daneren2005.dsub.domain; + +import java.io.Serializable; + +public class User implements Serializable { + private String username; + private String password; + private String email; + private Boolean scrobblingEnabled; + + private Boolean adminRole; + private Boolean settingsRole; + private Boolean downloadRole; + private Boolean uploadRole; + private Boolean playlistRole; + private Boolean coverArtRole; + private Boolean commentRole; + private Boolean podcastRole; + private Boolean streamRole; + private Boolean jukeboxRole; + private Boolean shareRole; + + public User() { + + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public Boolean getScrobblingEnabled() { + return scrobblingEnabled; + } + + public void setScrobblingEnabled(Boolean scrobblingEnabled) { + this.scrobblingEnabled = scrobblingEnabled; + } + + public Boolean getAdminRole() { + return adminRole; + } + + public void setAdminRole(Boolean adminRole) { + this.adminRole = adminRole; + } + + public Boolean getSettingsRole() { + return settingsRole; + } + + public void setSettingsRole(Boolean settingsRole) { + this.settingsRole = settingsRole; + } + + public Boolean getDownloadRole() { + return downloadRole; + } + + public void setDownloadRole(Boolean downloadRole) { + this.downloadRole = downloadRole; + } + + public Boolean getUploadRole() { + return uploadRole; + } + + public void setUploadRole(Boolean uploadRole) { + this.uploadRole = uploadRole; + } + + public Boolean getPlaylistRole() { + return playlistRole; + } + + public void setPlaylistRole(Boolean playlistRole) { + this.playlistRole = playlistRole; + } + + public Boolean getCoverArtRole() { + return coverArtRole; + } + + public void setCoverArtRole(Boolean coverArtRole) { + this.coverArtRole = coverArtRole; + } + + public Boolean getCommentRole() { + return commentRole; + } + + public void setCommentRole(Boolean commentRole) { + this.commentRole = commentRole; + } + + public Boolean getPodcastRole() { + return podcastRole; + } + + public void setPodcastRole(Boolean podcastRole) { + this.podcastRole = podcastRole; + } + + public Boolean getStreamRole() { + return streamRole; + } + + public void setStreamRole(Boolean streamRole) { + this.streamRole = streamRole; + } + + public Boolean getJukeboxRole() { + return jukeboxRole; + } + + public void setJukeboxRole(Boolean jukeboxRole) { + this.jukeboxRole = jukeboxRole; + } + + public Boolean getShareRole() { + return shareRole; + } + + public void setShareRole(Boolean shareRole) { + this.shareRole = shareRole; + } +} diff --git a/src/github/daneren2005/dsub/fragments/AdminFragment.java b/src/github/daneren2005/dsub/fragments/AdminFragment.java new file mode 100644 index 00000000..c43da8d4 --- /dev/null +++ b/src/github/daneren2005/dsub/fragments/AdminFragment.java @@ -0,0 +1,64 @@ +/* + This file is part of Subsonic. + Subsonic is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + Subsonic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with Subsonic. If not, see . + Copyright 2014 (C) Scott Jackson +*/ + +package github.daneren2005.dsub.fragments; + +import android.view.View; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; + +import java.util.ArrayList; +import java.util.List; + +import github.daneren2005.dsub.R; +import github.daneren2005.dsub.domain.User; +import github.daneren2005.dsub.service.MusicService; +import github.daneren2005.dsub.service.parser.SubsonicRESTException; +import github.daneren2005.dsub.util.ProgressListener; +import github.daneren2005.dsub.view.UserAdapter; + +public class AdminFragment extends SelectListFragment { + @Override + public int getOptionsMenu() { + return R.menu.admin; + } + + @Override + public ArrayAdapter getAdapter(List objs) { + return new UserAdapter(context, objs); + } + + @Override + public List getObjects(MusicService musicService, boolean refresh, ProgressListener listener) throws Exception { + try { + // Will only work if user is admin + return musicService.getUsers(refresh, context, listener); + } catch(SubsonicRESTException e) { + List users = new ArrayList(); + users.add(musicService.getUser(refresh, "scott", context, listener)); + return users; + } + } + + @Override + public int getTitleResource() { + return R.string.button_bar_admin; + } + + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + + } +} diff --git a/src/github/daneren2005/dsub/service/CachedMusicService.java b/src/github/daneren2005/dsub/service/CachedMusicService.java index d0740704..7dd5120a 100644 --- a/src/github/daneren2005/dsub/service/CachedMusicService.java +++ b/src/github/daneren2005/dsub/service/CachedMusicService.java @@ -41,6 +41,7 @@ import github.daneren2005.dsub.domain.PodcastChannel; import github.daneren2005.dsub.domain.SearchCritera; import github.daneren2005.dsub.domain.SearchResult; import github.daneren2005.dsub.domain.Share; +import github.daneren2005.dsub.domain.User; import github.daneren2005.dsub.domain.Version; import github.daneren2005.dsub.util.SilentBackgroundTask; import github.daneren2005.dsub.util.ProgressListener; @@ -506,6 +507,38 @@ public class CachedMusicService implements MusicService { musicService.deleteBookmark(id, context, progressListener); } + @Override + public User getUser(boolean refresh, String username, Context context, ProgressListener progressListener) throws Exception { + User result = null; + + if(!refresh) { + result = FileUtil.deserialize(context, getCacheName(context, "user"), User.class); + } + + if(result == null) { + result = musicService.getUser(refresh, username, context, progressListener); + FileUtil.serialize(context, result, getCacheName(context, "user")); + } + + return result; + } + + @Override + public List getUsers(boolean refresh, Context context, ProgressListener progressListener) throws Exception { + List result = null; + + if(!refresh) { + result = FileUtil.deserialize(context, getCacheName(context, "users"), ArrayList.class); + } + + if(result == null) { + result = musicService.getUsers(refresh, context, progressListener); + FileUtil.serialize(context, new ArrayList(result), getCacheName(context, "users")); + } + + return result; + } + @Override public int processOfflineSyncs(final Context context, final ProgressListener progressListener) throws Exception{ return musicService.processOfflineSyncs(context, progressListener); diff --git a/src/github/daneren2005/dsub/service/MusicService.java b/src/github/daneren2005/dsub/service/MusicService.java index 0522a4be..1164886d 100644 --- a/src/github/daneren2005/dsub/service/MusicService.java +++ b/src/github/daneren2005/dsub/service/MusicService.java @@ -38,6 +38,7 @@ import github.daneren2005.dsub.domain.PodcastChannel; import github.daneren2005.dsub.domain.SearchCritera; import github.daneren2005.dsub.domain.SearchResult; import github.daneren2005.dsub.domain.Share; +import github.daneren2005.dsub.domain.User; import github.daneren2005.dsub.domain.Version; import github.daneren2005.dsub.util.SilentBackgroundTask; import github.daneren2005.dsub.util.ProgressListener; @@ -160,6 +161,10 @@ public interface MusicService { void createBookmark(String id, int position, String comment, Context context, ProgressListener progressListener) throws Exception; void deleteBookmark(String id, Context context, ProgressListener progressListener) throws Exception; + + User getUser(boolean refresh, String username, Context context, ProgressListener progressListener) throws Exception; + + List getUsers(boolean refresh, 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 cf697441..a809f0d1 100644 --- a/src/github/daneren2005/dsub/service/OfflineMusicService.java +++ b/src/github/daneren2005/dsub/service/OfflineMusicService.java @@ -46,6 +46,7 @@ import github.daneren2005.dsub.domain.Playlist; import github.daneren2005.dsub.domain.PodcastChannel; import github.daneren2005.dsub.domain.SearchCritera; import github.daneren2005.dsub.domain.SearchResult; +import github.daneren2005.dsub.domain.User; import github.daneren2005.dsub.util.Constants; import github.daneren2005.dsub.util.FileUtil; import github.daneren2005.dsub.util.ProgressListener; @@ -688,8 +689,18 @@ public class OfflineMusicService extends RESTMusicService { public void deleteBookmark(String id, Context context, ProgressListener progressListener) throws Exception { throw new OfflineException("Deleting bookmarks not available in offline mode"); } - - @Override + + @Override + public User getUser(boolean refresh, String username, Context context, ProgressListener progressListener) throws Exception { + throw new OfflineException("Getting user not available in offline mode"); + } + + @Override + public List getUsers(boolean refresh, Context context, ProgressListener progressListener) throws Exception { + throw new OfflineException("Getting users not available in offline mode"); + } + + @Override public int processOfflineSyncs(final Context context, final ProgressListener progressListener) throws Exception{ throw new OfflineException("Offline scrobble cached can not be processes while in offline mode"); } diff --git a/src/github/daneren2005/dsub/service/RESTMusicService.java b/src/github/daneren2005/dsub/service/RESTMusicService.java index eeddcaa2..10647d99 100644 --- a/src/github/daneren2005/dsub/service/RESTMusicService.java +++ b/src/github/daneren2005/dsub/service/RESTMusicService.java @@ -88,6 +88,7 @@ import github.daneren2005.dsub.service.parser.SearchResult2Parser; import github.daneren2005.dsub.service.parser.SearchResultParser; import github.daneren2005.dsub.service.parser.ShareParser; import github.daneren2005.dsub.service.parser.StarredListParser; +import github.daneren2005.dsub.service.parser.UserParser; import github.daneren2005.dsub.service.parser.VersionParser; import github.daneren2005.dsub.service.ssl.SSLSocketFactory; import github.daneren2005.dsub.service.ssl.TrustSelfSignedStrategy; @@ -1189,6 +1190,34 @@ public class RESTMusicService implements MusicService { } } + @Override + public User getUser(boolean refresh, String username, Context context, ProgressListener progressListener) throws Exception { + Reader reader = getReader(context, progressListener, "getUser", null, Arrays.asList("username"), Arrays.asList(username)); + try { + List users = new UserParser(context).parse(reader, progressListener); + if(users.size() > 0) { + // Should only have returned one anyways + return users.get(0); + } else { + return null; + } + } finally { + Util.close(reader); + } + } + + @Override + public List getUsers(boolean refresh, Context context, ProgressListener progressListener) throws Exception { + checkServerVersion(context, "1.8", "Getting user list is not supported"); + + Reader reader = getReader(context, progressListener, "getUsers", null); + try { + return new UserParser(context).parse(reader, progressListener); + } finally { + Util.close(reader); + } + } + @Override public int processOfflineSyncs(final Context context, final ProgressListener progressListener) throws Exception{ return processOfflineScrobbles(context, progressListener) + processOfflineStars(context, progressListener); diff --git a/src/github/daneren2005/dsub/service/parser/UserParser.java b/src/github/daneren2005/dsub/service/parser/UserParser.java new file mode 100644 index 00000000..a66e0b63 --- /dev/null +++ b/src/github/daneren2005/dsub/service/parser/UserParser.java @@ -0,0 +1,73 @@ +/* + This file is part of Subsonic. + Subsonic is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + Subsonic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with Subsonic. If not, see . + Copyright 2014 (C) Scott Jackson +*/ + +package github.daneren2005.dsub.service.parser; + +import android.content.Context; + +import org.xmlpull.v1.XmlPullParser; + +import java.io.Reader; +import java.util.ArrayList; +import java.util.List; + +import github.daneren2005.dsub.domain.User; +import github.daneren2005.dsub.util.ProgressListener; + +public class UserParser extends AbstractParser { + + public UserParser(Context context) { + super(context); + } + + public List parse(Reader reader, ProgressListener progressListener) throws Exception { + init(reader); + List result = new ArrayList(); + int eventType; + + do { + eventType = nextParseEvent(); + if (eventType == XmlPullParser.START_TAG) { + String name = getElementName(); + if ("user".equals(name)) { + User user = new User(); + + user.setUsername(get("username")); + user.setEmail(get("email")); + user.setScrobblingEnabled(getBoolean("scrobblingEnabled")); + user.setAdminRole(getBoolean("adminRole")); + user.setSettingsRole(getBoolean("settingsRole")); + user.setDownloadRole(getBoolean("downloadRole")); + user.setUploadRole(getBoolean("uploadRole")); + user.setPlaylistRole(getBoolean("playlistRole")); + user.setCoverArtRole(getBoolean("coverArtRole")); + user.setCommentRole(getBoolean("commentRole")); + user.setPodcastRole(getBoolean("podcastRole")); + user.setStreamRole(getBoolean("streamRole")); + user.setJukeboxRole(getBoolean("jukeboxRole")); + user.setShareRole(getBoolean("shareRole")); + + result.add(user); + } else if ("error".equals(name)) { + handleError(); + } + } + } while (eventType != XmlPullParser.END_DOCUMENT); + + validate(); + + return result; + } +} \ No newline at end of file diff --git a/src/github/daneren2005/dsub/util/Constants.java b/src/github/daneren2005/dsub/util/Constants.java index 2b7f8757..34bf734d 100644 --- a/src/github/daneren2005/dsub/util/Constants.java +++ b/src/github/daneren2005/dsub/util/Constants.java @@ -141,6 +141,7 @@ public final class Constants { public static final String PREFERENCES_KEY_OVERRIDE_SYSTEM_LANGUAGE = "overrideSystemLanguage"; public static final String PREFERENCES_KEY_PLAY_NOW_AFTER = "playNowAfter"; public static final String PREFERENCES_KEY_LARGE_ALBUM_ART = "largeAlbumArt"; + public static final String PREFERENCES_KEY_ADMIN_ENABLED = "adminEnabled"; public static final String OFFLINE_SCROBBLE_COUNT = "scrobbleCount"; public static final String OFFLINE_SCROBBLE_ID = "scrobbleID"; diff --git a/src/github/daneren2005/dsub/view/UserAdapter.java b/src/github/daneren2005/dsub/view/UserAdapter.java new file mode 100644 index 00000000..8aaa261c --- /dev/null +++ b/src/github/daneren2005/dsub/view/UserAdapter.java @@ -0,0 +1,49 @@ +/* + This file is part of Subsonic. + Subsonic is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + Subsonic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with Subsonic. If not, see . + Copyright 2014 (C) Scott Jackson +*/ + +package github.daneren2005.dsub.view; + +import android.content.Context; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; + +import java.util.List; + +import github.daneren2005.dsub.R; +import github.daneren2005.dsub.domain.User; + +public class UserAdapter extends ArrayAdapter { + + private final Context activity; + + public UserAdapter(Context activity, List users) { + super(activity, R.layout.basic_list_item, users); + this.activity = activity; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + User entry = getItem(position); + UserView view; + if (convertView != null && convertView instanceof UserView) { + view = (UserView) convertView; + } else { + view = new UserView(activity); + } + view.setObject(entry); + 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 new file mode 100644 index 00000000..f966f28f --- /dev/null +++ b/src/github/daneren2005/dsub/view/UserView.java @@ -0,0 +1,50 @@ +/* + This file is part of Subsonic. + Subsonic is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + Subsonic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with Subsonic. If not, see . + Copyright 2014 (C) Scott Jackson +*/ + +package github.daneren2005.dsub.view; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.ImageView; +import android.widget.TextView; + +import github.daneren2005.dsub.R; +import github.daneren2005.dsub.domain.User; + +public class UserView extends UpdateView { + private User user; + + private TextView userNameView; + + public UserView(Context context) { + super(context); + this.context = context; + LayoutInflater.from(context).inflate(R.layout.basic_list_item, this, true); + + userNameView = (TextView) findViewById(R.id.item_name); + moreButton = (ImageView) findViewById(R.id.item_more); + moreButton.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + v.showContextMenu(); + } + }); + } + + protected void setObjectImpl(Object obj) { + this.user = (User) obj; + userNameView.setText(user.getUsername()); + } +} -- cgit v1.2.3