aboutsummaryrefslogtreecommitdiff
path: root/app/src/main/java/github
diff options
context:
space:
mode:
Diffstat (limited to 'app/src/main/java/github')
-rw-r--r--app/src/main/java/github/daneren2005/dsub/activity/DownloadActivity.java62
-rw-r--r--app/src/main/java/github/daneren2005/dsub/activity/EditPlayActionActivity.java2
-rw-r--r--app/src/main/java/github/daneren2005/dsub/activity/SettingsActivity.java55
-rw-r--r--app/src/main/java/github/daneren2005/dsub/activity/SubsonicActivity.java470
-rw-r--r--app/src/main/java/github/daneren2005/dsub/activity/SubsonicFragmentActivity.java431
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/AlbumGridAdapter.java73
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/AlbumListAdapter.java4
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/ArtistAdapter.java197
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/BasicListAdapter.java48
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/BookmarkAdapter.java68
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/DetailsAdapter.java62
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/DownloadFileAdapter.java37
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/DrawerAdapter.java126
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/EntryAdapter.java82
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/EntryGridAdapter.java145
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/EntryInfiniteGridAdapter.java146
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/GenreAdapter.java80
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/MainAdapter.java112
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/MergeAdapter.java290
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/PlaylistAdapter.java93
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/PodcastChannelAdapter.java79
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/SackOfViewsAdapter.java181
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/SearchAdapter.java129
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/SectionAdapter.java463
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/SettingsAdapter.java77
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/ShareAdapter.java75
-rw-r--r--app/src/main/java/github/daneren2005/dsub/adapter/UserAdapter.java34
-rw-r--r--app/src/main/java/github/daneren2005/dsub/domain/Playlist.java15
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/AdminFragment.java27
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/DownloadFragment.java111
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/EqualizerFragment.java1
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/LyricsFragment.java1
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/MainFragment.java396
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/NowPlayingFragment.java894
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/SearchFragment.java247
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/SelectArtistFragment.java187
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/SelectBookmarkFragment.java76
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java683
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/SelectGenreFragment.java52
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/SelectListFragment.java163
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/SelectPlaylistFragment.java140
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/SelectPodcastsFragment.java111
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/SelectRecyclerFragment.java197
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/SelectShareFragment.java54
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/SelectVideoFragment.java36
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/SelectYearFragment.java55
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/SimilarArtistFragment.java48
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/SubsonicFragment.java780
-rw-r--r--app/src/main/java/github/daneren2005/dsub/fragments/UserFragment.java106
-rw-r--r--app/src/main/java/github/daneren2005/dsub/provider/DSubWidgetProvider.java5
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/CachedMusicService.java18
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/DownloadService.java139
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/MusicService.java4
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/OfflineMusicService.java56
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/RESTMusicService.java6
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/parser/PlaylistsParser.java3
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/sync/MostRecentSyncAdapter.java6
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/Constants.java2
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/DrawableTint.java90
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/FileUtil.java45
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/ImageLoader.java52
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/MenuUtil.java55
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/Notifications.java3
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/UserUtil.java20
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/Util.java97
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/AlbumCell.java108
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/AlbumListCountView.java130
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/AlbumView.java77
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/ArtistEntryView.java15
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/ArtistView.java13
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/BasicHeaderView.java40
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/BasicListView.java44
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/ChangeLog.java2
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/ErrorDialog.java2
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/GenreView.java5
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/GridSpacingDecoration.java99
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/HeaderGridView.java836
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/PlaylistSongView.java16
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/PlaylistView.java32
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/PodcastChannelView.java11
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/SettingView.java61
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/ShareView.java5
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/SongView.java150
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/UnscrollableGridView.java128
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/UpdateView.java105
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/UpdateView2.java47
-rw-r--r--app/src/main/java/github/daneren2005/dsub/view/UserView.java10
87 files changed, 5225 insertions, 5313 deletions
diff --git a/app/src/main/java/github/daneren2005/dsub/activity/DownloadActivity.java b/app/src/main/java/github/daneren2005/dsub/activity/DownloadActivity.java
deleted file mode 100644
index e13a8b8c..00000000
--- a/app/src/main/java/github/daneren2005/dsub/activity/DownloadActivity.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- 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 <http://www.gnu.org/licenses/>.
-
- Copyright 2009 (C) Sindre Mehus
- */
-package github.daneren2005.dsub.activity;
-
-import github.daneren2005.dsub.R;
-import android.os.Bundle;
-import android.view.MotionEvent;
-import github.daneren2005.dsub.fragments.NowPlayingFragment;
-
-import android.widget.EditText;
-
-import github.daneren2005.dsub.util.Constants;
-
-public class DownloadActivity extends SubsonicActivity {
- private static final String TAG = DownloadActivity.class.getSimpleName();
- private EditText playlistNameView;
-
- /**
- * Called when the activity is first created.
- */
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.download_activity);
-
- if (findViewById(R.id.fragment_container) != null && savedInstanceState == null) {
- currentFragment = new NowPlayingFragment();
- if(getIntent().hasExtra(Constants.INTENT_EXTRA_NAME_DOWNLOAD_VIEW)) {
- Bundle args = new Bundle();
- args.putBoolean(Constants.INTENT_EXTRA_NAME_DOWNLOAD_VIEW, true);
- currentFragment.setArguments(args);
- }
- currentFragment.setPrimaryFragment(true);
- getSupportFragmentManager().beginTransaction().add(R.id.fragment_container, currentFragment, currentFragment.getSupportTag() + "").commit();
- }
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent me) {
- if(currentFragment != null && currentFragment.getGestureDetector() != null) {
- return currentFragment.getGestureDetector().onTouchEvent(me);
- } else {
- return false;
- }
- }
-}
diff --git a/app/src/main/java/github/daneren2005/dsub/activity/EditPlayActionActivity.java b/app/src/main/java/github/daneren2005/dsub/activity/EditPlayActionActivity.java
index e1f2cad3..0396f8a4 100644
--- a/app/src/main/java/github/daneren2005/dsub/activity/EditPlayActionActivity.java
+++ b/app/src/main/java/github/daneren2005/dsub/activity/EditPlayActionActivity.java
@@ -16,7 +16,7 @@
package github.daneren2005.dsub.activity;
import android.app.Activity;
-import android.app.AlertDialog;
+import android.support.v7.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
diff --git a/app/src/main/java/github/daneren2005/dsub/activity/SettingsActivity.java b/app/src/main/java/github/daneren2005/dsub/activity/SettingsActivity.java
index d5ac60d3..595529b7 100644
--- a/app/src/main/java/github/daneren2005/dsub/activity/SettingsActivity.java
+++ b/app/src/main/java/github/daneren2005/dsub/activity/SettingsActivity.java
@@ -19,61 +19,25 @@
package github.daneren2005.dsub.activity;
import android.annotation.TargetApi;
-import android.accounts.Account;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
-import android.preference.CheckBoxPreference;
-import android.preference.EditTextPreference;
-import android.preference.ListPreference;
-import android.preference.Preference;
-import android.preference.PreferenceCategory;
-import android.preference.PreferenceScreen;
-import android.support.v7.app.ActionBarActivity;
-import android.text.InputType;
-import android.util.Log;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.Button;
-import android.widget.EditText;
-import android.widget.FrameLayout;
+import android.support.v7.widget.Toolbar;
import github.daneren2005.dsub.R;
import github.daneren2005.dsub.fragments.PreferenceCompatFragment;
import github.daneren2005.dsub.fragments.SettingsFragment;
-import github.daneren2005.dsub.service.DownloadService;
-import github.daneren2005.dsub.service.MusicService;
-import github.daneren2005.dsub.service.MusicServiceFactory;
import github.daneren2005.dsub.util.Constants;
-import github.daneren2005.dsub.util.LoadingTask;
-import github.daneren2005.dsub.util.SyncUtil;
-import github.daneren2005.dsub.view.ErrorDialog;
-import github.daneren2005.dsub.util.FileUtil;
-import github.daneren2005.dsub.util.Util;
-
-import java.io.File;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Method;
-import java.net.URL;
-import java.text.DecimalFormat;
-import java.util.LinkedHashMap;
-import java.util.Map;
public class SettingsActivity extends SubsonicActivity {
- private static final String TAG = SettingsActivity.class.getSimpleName();
+ private static final String TAG = SettingsActivity.class.getSimpleName();
private PreferenceCompatFragment fragment;
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1)
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.download_activity);
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ lastSelectedPosition = R.id.drawer_settings;
+ setContentView(R.layout.settings_activity);
if (savedInstanceState == null) {
fragment = new SettingsFragment();
@@ -87,5 +51,8 @@ public class SettingsActivity extends SubsonicActivity {
currentFragment.setPrimaryFragment(true);
getSupportFragmentManager().beginTransaction().add(R.id.fragment_container, currentFragment, currentFragment.getSupportTag() + "").commit();
}
- }
+
+ Toolbar mainToolbar = (Toolbar) findViewById(R.id.main_toolbar);
+ setSupportActionBar(mainToolbar);
+ }
}
diff --git a/app/src/main/java/github/daneren2005/dsub/activity/SubsonicActivity.java b/app/src/main/java/github/daneren2005/dsub/activity/SubsonicActivity.java
index 4651eb0b..92ecf930 100644
--- a/app/src/main/java/github/daneren2005/dsub/activity/SubsonicActivity.java
+++ b/app/src/main/java/github/daneren2005/dsub/activity/SubsonicActivity.java
@@ -25,17 +25,19 @@ import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
-import android.content.res.TypedArray;
import android.media.AudioManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
+import android.os.Handler;
+import android.support.design.widget.NavigationView;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.widget.DrawerLayout;
-import android.support.v7.app.ActionBarActivity;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
@@ -51,14 +53,13 @@ import android.view.animation.AnimationUtils;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
-import android.widget.ListView;
+import android.widget.ImageView;
import android.widget.Spinner;
import android.widget.TextView;
import java.io.File;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
import github.daneren2005.dsub.R;
@@ -67,21 +68,23 @@ import github.daneren2005.dsub.fragments.SubsonicFragment;
import github.daneren2005.dsub.service.DownloadService;
import github.daneren2005.dsub.service.HeadphoneListenerService;
import github.daneren2005.dsub.util.Constants;
+import github.daneren2005.dsub.util.DrawableTint;
import github.daneren2005.dsub.util.ImageLoader;
+import github.daneren2005.dsub.util.SilentBackgroundTask;
import github.daneren2005.dsub.util.Util;
-import github.daneren2005.dsub.adapter.DrawerAdapter;
import github.daneren2005.dsub.view.UpdateView;
import github.daneren2005.dsub.util.UserUtil;
-public class SubsonicActivity extends ActionBarActivity implements OnItemSelectedListener {
+public class SubsonicActivity extends AppCompatActivity implements OnItemSelectedListener {
private static final String TAG = SubsonicActivity.class.getSimpleName();
private static ImageLoader IMAGE_LOADER;
protected static String theme;
protected static boolean fullScreen;
- private String[] drawerItemsDescriptions;
- private String[] drawerItems;
+ private static final int MENU_GROUP_SERVER = 10;
+ private static final int MENU_ITEM_SERVER_BASE = 100;
+
+ private final List<Runnable> afterServiceAvailable = new ArrayList<>();
private boolean drawerIdle = true;
- private boolean[] enabledItems = {true, true, true, true, true};
private boolean destroyed = false;
private boolean finished = false;
protected List<SubsonicFragment> backStack = new ArrayList<SubsonicFragment>();
@@ -90,16 +93,22 @@ public class SubsonicActivity extends ActionBarActivity implements OnItemSelecte
protected View secondaryContainer;
protected boolean tv = false;
protected boolean touchscreen = true;
+ protected Handler handler = new Handler();
Spinner actionBarSpinner;
ArrayAdapter<CharSequence> spinnerAdapter;
ViewGroup rootView;
DrawerLayout drawer;
ActionBarDrawerToggle drawerToggle;
- DrawerAdapter drawerAdapter;
- ListView drawerList;
- TextView lastSelectedView = null;
+ NavigationView drawerList;
+ View drawerHeader;
+ ImageView drawerUserAvatar;
+ ImageView drawerHeaderToggle;
+ TextView drawerServerName;
+ TextView drawerUserName;
int lastSelectedPosition = 0;
+ boolean showingTabs = true;
boolean drawerOpen = false;
+ SharedPreferences.OnSharedPreferenceChangeListener preferencesListener;
@Override
protected void onCreate(Bundle bundle) {
@@ -118,26 +127,49 @@ public class SubsonicActivity extends ActionBarActivity implements OnItemSelecte
super.onCreate(bundle);
startService(new Intent(this, DownloadService.class));
setVolumeControlStream(AudioManager.STREAM_MUSIC);
-
- View actionbar = getLayoutInflater().inflate(R.layout.actionbar_spinner, null);
- actionBarSpinner = (Spinner)actionbar.findViewById(R.id.spinner);
- spinnerAdapter = new ArrayAdapter(this, android.R.layout.simple_spinner_item);
- spinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
- actionBarSpinner.setOnItemSelectedListener(this);
- actionBarSpinner.setAdapter(spinnerAdapter);
-
- getSupportActionBar().setCustomView(actionbar);
- getSupportActionBar().setDisplayHomeAsUpEnabled(true);
- getSupportActionBar().setHomeButtonEnabled(true);
if(getIntent().hasExtra(Constants.FRAGMENT_POSITION)) {
lastSelectedPosition = getIntent().getIntExtra(Constants.FRAGMENT_POSITION, 0);
}
+
+ if(preferencesListener == null) {
+ preferencesListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
+ @Override
+ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
+ // When changing drawer settings change visibility
+ switch(key) {
+ case Constants.PREFERENCES_KEY_PODCASTS_ENABLED:
+ setDrawerItemVisible(R.id.drawer_podcasts, false);
+ break;
+ case Constants.PREFERENCES_KEY_BOOKMARKS_ENABLED:
+ setDrawerItemVisible(R.id.drawer_bookmarks, false);
+ break;
+ case Constants.PREFERENCES_KEY_SHARED_ENABLED:
+ setDrawerItemVisible(R.id.drawer_shares, false);
+ break;
+ case Constants.PREFERENCES_KEY_CHAT_ENABLED:
+ setDrawerItemVisible(R.id.drawer_chat, false);
+ break;
+ case Constants.PREFERENCES_KEY_ADMIN_ENABLED:
+ setDrawerItemVisible(R.id.drawer_admin, false);
+ break;
+ }
+ }
+ };
+ Util.getPreferences(this).registerOnSharedPreferenceChangeListener(preferencesListener);
+ }
}
@Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
+
+ if(spinnerAdapter == null) {
+ createCustomActionBarView();
+ }
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ getSupportActionBar().setHomeButtonEnabled(true);
+
// Sync the toggle state after onRestoreInstanceState has occurred.
if(drawerToggle != null) {
drawerToggle.syncState();
@@ -150,6 +182,17 @@ public class SubsonicActivity extends ActionBarActivity implements OnItemSelecte
}
}
+ protected void createCustomActionBarView() {
+ View customActionbar = getLayoutInflater().inflate(R.layout.actionbar_spinner, null);
+ actionBarSpinner = (Spinner)customActionbar.findViewById(R.id.spinner);
+ spinnerAdapter = new ArrayAdapter(this, android.R.layout.simple_spinner_item);
+ spinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ actionBarSpinner.setOnItemSelectedListener(this);
+ actionBarSpinner.setAdapter(spinnerAdapter);
+
+ getSupportActionBar().setCustomView(customActionbar);
+ }
+
@Override
protected void onResume() {
super.onResume();
@@ -159,9 +202,10 @@ public class SubsonicActivity extends ActionBarActivity implements OnItemSelecte
if (theme != null && !theme.equals(Util.getTheme(this)) || fullScreen != Util.getPreferences(this).getBoolean(Constants.PREFERENCES_KEY_FULL_SCREEN, false)) {
restart();
overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
+ DrawableTint.wipeTintCache();
}
-
- populateDrawer();
+
+ populateTabs();
UpdateView.addActiveActivity();
}
@@ -176,6 +220,7 @@ public class SubsonicActivity extends ActionBarActivity implements OnItemSelecte
protected void onDestroy() {
super.onDestroy();
destroyed = true;
+ Util.getPreferences(this).unregisterOnSharedPreferenceChangeListener(preferencesListener);
}
@Override
@@ -185,19 +230,6 @@ public class SubsonicActivity extends ActionBarActivity implements OnItemSelecte
}
@Override
- public void startActivity(Intent intent) {
- if(intent.getComponent() != null) {
- String name = intent.getComponent().getClassName();
- if(name != null && name.indexOf("DownloadActivity") != -1) {
- intent.putExtra(Constants.FRAGMENT_POSITION, lastSelectedPosition);
- } else if(name != null && name.indexOf("SettingsActivity") != -1) {
- intent.putExtra(Constants.FRAGMENT_POSITION, drawerItems.length - 1);
- }
- }
- super.startActivity(intent);
- }
-
- @Override
public void setContentView(int viewId) {
if(isTv()) {
super.setContentView(R.layout.static_drawer_activity);
@@ -210,34 +242,101 @@ public class SubsonicActivity extends ActionBarActivity implements OnItemSelecte
LayoutInflater layoutInflater = getLayoutInflater();
layoutInflater.inflate(viewId, rootView);
}
-
- drawerList = (ListView) findViewById(R.id.left_drawer);
- drawerList.setOnItemClickListener(new ListView.OnItemClickListener() {
+
+ drawerList = (NavigationView) findViewById(R.id.left_drawer);
+ drawerList.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
+ @Override
+ public boolean onNavigationItemSelected(final MenuItem menuItem) {
+ if(showingTabs) {
+ // Settings are on a different selectable track
+ if (menuItem.getItemId() != R.id.drawer_settings && menuItem.getItemId() != R.id.drawer_admin) {
+ menuItem.setChecked(true);
+ lastSelectedPosition = menuItem.getItemId();
+ }
+
+ switch (menuItem.getItemId()) {
+ case R.id.drawer_home:
+ drawerItemSelected("Home");
+ return true;
+ case R.id.drawer_library:
+ drawerItemSelected("Artist");
+ return true;
+ case R.id.drawer_playlists:
+ drawerItemSelected("Playlist");
+ return true;
+ case R.id.drawer_podcasts:
+ drawerItemSelected("Podcast");
+ return true;
+ case R.id.drawer_bookmarks:
+ drawerItemSelected("Bookmark");
+ return true;
+ case R.id.drawer_shares:
+ drawerItemSelected("Share");
+ return true;
+ case R.id.drawer_chat:
+ drawerItemSelected("Chat");
+ return true;
+ case R.id.drawer_admin:
+ if (UserUtil.isCurrentAdmin()) {
+ UserUtil.confirmCredentials(SubsonicActivity.this, new Runnable() {
+ @Override
+ public void run() {
+ drawerItemSelected("Admin");
+ menuItem.setChecked(true);
+ lastSelectedPosition = menuItem.getItemId();
+ }
+ });
+ } else {
+ drawerItemSelected("Admin");
+ menuItem.setChecked(true);
+ lastSelectedPosition = menuItem.getItemId();
+ }
+ return true;
+ case R.id.drawer_downloading:
+ drawerItemSelected("Download");
+ return true;
+ case R.id.drawer_settings:
+ startActivity(new Intent(SubsonicActivity.this, SettingsActivity.class));
+ drawer.closeDrawers();
+ return true;
+ }
+ } else {
+ int activeServer = menuItem.getItemId() - MENU_ITEM_SERVER_BASE;
+ SubsonicActivity.this.setActiveServer(activeServer);
+ populateTabs();
+ return true;
+ }
+
+ return false;
+ }
+ });
+ populateTabs();
+
+ drawerHeader = drawerList.inflateHeaderView(R.layout.drawer_header);
+ drawerHeader.setOnClickListener(new View.OnClickListener() {
@Override
- public void onItemClick(AdapterView<?> parent, final View view, final int position, long id) {
- final int actualPosition = drawerAdapter.getActualPosition(position);
- if("Settings".equals(drawerItemsDescriptions[actualPosition])) {
- startActivity(new Intent(SubsonicActivity.this, SettingsActivity.class));
- drawer.closeDrawers();
- } else if("Admin".equals(drawerItemsDescriptions[actualPosition]) && UserUtil.isCurrentAdmin()) {
- UserUtil.confirmCredentials(SubsonicActivity.this, new Runnable() {
- @Override
- public void run() {
- drawerItemSelected(actualPosition, view);
- }
- });
+ public void onClick(View v) {
+ if(showingTabs) {
+ populateServers();
} else {
- drawerItemSelected(actualPosition, view);
+ populateTabs();
}
}
});
+ drawerHeaderToggle = (ImageView) drawerHeader.findViewById(R.id.header_select_image);
+ drawerServerName = (TextView) drawerHeader.findViewById(R.id.header_server_name);
+ drawerUserName = (TextView) drawerHeader.findViewById(R.id.header_user_name);
+ drawerUserAvatar = (ImageView) drawerHeader.findViewById(R.id.header_user_avatar);
+ updateDrawerHeader();
if(!isTv()) {
drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
- drawerToggle = new ActionBarDrawerToggle(this, drawer, R.string.common_appname, R.string.common_appname) {
+ // Pass in toolbar if it exists
+ Toolbar toolbar = (Toolbar) findViewById(R.id.main_toolbar);
+ drawerToggle = new ActionBarDrawerToggle(this, drawer, toolbar, R.string.common_appname, R.string.common_appname) {
@Override
public void onDrawerClosed(View view) {
setTitle(currentFragment.getTitle());
@@ -246,23 +345,19 @@ public class SubsonicActivity extends ActionBarActivity implements OnItemSelecte
drawerOpen = false;
supportInvalidateOptionsMenu();
+ if(!showingTabs) {
+ populateTabs();
+ }
}
@Override
public void onDrawerOpened(View view) {
DownloadService downloadService = getDownloadService();
- if (downloadService == null || downloadService.getBackgroundDownloads().isEmpty()) {
- drawerAdapter.setDownloadVisible(false);
- } else {
- drawerAdapter.setDownloadVisible(true);
- }
-
- if (lastSelectedView == null && drawerList.getCount() > lastSelectedPosition) {
- lastSelectedView = (TextView) drawerList.getChildAt(lastSelectedPosition).findViewById(R.id.drawer_name);
- if (lastSelectedView != null) {
- lastSelectedView.setTextAppearance(SubsonicActivity.this, R.style.DSub_TextViewStyle_Bold);
- }
+ boolean downloadingVisible = downloadService != null && !downloadService.getBackgroundDownloads().isEmpty();
+ if(lastSelectedPosition == R.id.drawer_downloading) {
+ downloadingVisible = true;
}
+ setDrawerItemVisible(R.id.drawer_downloading, downloadingVisible);
getSupportActionBar().setTitle(R.string.common_appname);
getSupportActionBar().setDisplayShowCustomEnabled(false);
@@ -299,7 +394,7 @@ public class SubsonicActivity extends ActionBarActivity implements OnItemSelecte
primaryContainer = findViewById(R.id.fragment_container);
}
}
-
+
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
@@ -363,6 +458,12 @@ public class SubsonicActivity extends ActionBarActivity implements OnItemSelecte
}
lastSelectedPosition = savedInstanceState.getInt(Constants.FRAGMENT_POSITION);
+ if(lastSelectedPosition != 0) {
+ MenuItem item = drawerList.getMenu().findItem(lastSelectedPosition);
+ if(item != null) {
+ item.setChecked(true);
+ }
+ }
recreateSpinner();
}
@@ -370,10 +471,11 @@ public class SubsonicActivity extends ActionBarActivity implements OnItemSelecte
public void onNewIntent(Intent intent) {
super.onNewIntent(intent);
}
-
+
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater menuInflater = getMenuInflater();
+ SubsonicFragment currentFragment = getCurrentFragment();
if(drawerOpen) {
menuInflater.inflate(R.menu.drawer_menu, menu);
} else if(currentFragment != null) {
@@ -399,7 +501,7 @@ public class SubsonicActivity extends ActionBarActivity implements OnItemSelecte
return true;
}
- return currentFragment.onOptionsItemSelected(item);
+ return getCurrentFragment().onOptionsItemSelected(item);
}
@Override
@@ -415,7 +517,7 @@ public class SubsonicActivity extends ActionBarActivity implements OnItemSelecte
}
return super.onKeyDown(keyCode, event);
}
-
+
@Override
public void setTitle(CharSequence title) {
if(title != null && !title.equals(getSupportActionBar().getTitle())) {
@@ -426,7 +528,7 @@ public class SubsonicActivity extends ActionBarActivity implements OnItemSelecte
public void setSubtitle(CharSequence title) {
getSupportActionBar().setSubtitle(title);
}
-
+
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
int top = spinnerAdapter.getCount() - 1;
@@ -439,99 +541,73 @@ public class SubsonicActivity extends ActionBarActivity implements OnItemSelecte
@Override
public void onNothingSelected(AdapterView<?> parent) {
-
+
}
-
- private void populateDrawer() {
+
+ private void populateTabs() {
+ drawerList.getMenu().clear();
+ drawerList.inflateMenu(R.menu.drawer_navigation);
+
SharedPreferences prefs = Util.getPreferences(this);
boolean podcastsEnabled = prefs.getBoolean(Constants.PREFERENCES_KEY_PODCASTS_ENABLED, true);
boolean bookmarksEnabled = prefs.getBoolean(Constants.PREFERENCES_KEY_BOOKMARKS_ENABLED, true) && !Util.isOffline(this) && ServerInfo.canBookmark(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 || !enabledItems[4] == adminEnabled) {
- drawerItems = getResources().getStringArray(R.array.drawerItems);
- drawerItemsDescriptions = getResources().getStringArray(R.array.drawerItemsDescriptions);
-
- List<String> drawerItemsList = new ArrayList<String>(Arrays.asList(drawerItems));
- List<Integer> drawerItemsIconsList = new ArrayList<Integer>();
- List<Boolean> drawerItemsVisibleList = new ArrayList<Boolean>();
-
- int[] arrayAttr = {R.attr.drawerItemsIcons};
- TypedArray arrayType = obtainStyledAttributes(arrayAttr);
- int arrayId = arrayType.getResourceId(0, 0);
- TypedArray iconType = getResources().obtainTypedArray(arrayId);
- for(int i = 0; i < drawerItemsList.size(); i++) {
- drawerItemsIconsList.add(iconType.getResourceId(i, 0));
- drawerItemsVisibleList.add(true);
- }
- iconType.recycle();
- arrayType.recycle();
- // Hide listings user doesn't want to see
- if(!podcastsEnabled) {
- drawerItemsVisibleList.set(3, false);
- }
- if(!bookmarksEnabled) {
- drawerItemsVisibleList.set(4, false);
- }
- if(!sharedEnabled) {
- drawerItemsVisibleList.set(5, false);
- }
- if(!chatEnabled) {
- drawerItemsVisibleList.set(6, false);
- }
- 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) {
- for(int i = 0; i < drawerItemsDescriptions.length; i++) {
- if(fragmentType.equals(drawerItemsDescriptions[i])) {
- lastSelectedPosition = drawerAdapter.getAdapterPosition(i);
- break;
- }
- }
- }
+ if(!podcastsEnabled) {
+ setDrawerItemVisible(R.id.drawer_podcasts, false);
+ }
+ if(!bookmarksEnabled) {
+ setDrawerItemVisible(R.id.drawer_bookmarks, false);
+ }
+ if(!sharedEnabled) {
+ setDrawerItemVisible(R.id.drawer_shares, false);
+ }
+ if(!chatEnabled) {
+ setDrawerItemVisible(R.id.drawer_chat, false);
+ }
+ if(!adminEnabled) {
+ setDrawerItemVisible(R.id.drawer_admin, false);
+ }
- if(drawerList.getChildAt(lastSelectedPosition) == null) {
- lastSelectedView = null;
- drawerAdapter.setSelectedPosition(lastSelectedPosition);
- } else {
- lastSelectedView = (TextView) drawerList.getChildAt(lastSelectedPosition).findViewById(R.id.drawer_name);
- if(lastSelectedView != null) {
- lastSelectedView.setTextAppearance(SubsonicActivity.this, R.style.DSub_TextViewStyle_Bold);
- }
+ if(lastSelectedPosition != 0) {
+ MenuItem item = drawerList.getMenu().findItem(lastSelectedPosition);
+ if(item != null) {
+ item.setChecked(true);
}
}
+ showingTabs = true;
}
-
- private void drawerItemSelected(int position, View view) {
- startFragmentActivity(drawerItemsDescriptions[position]);
-
- if(lastSelectedView != view) {
- if(lastSelectedView != null) {
- lastSelectedView.setTextAppearance(this, R.style.DSub_TextViewStyle);
+ private void populateServers() {
+ drawerList.getMenu().clear();
+
+ int serverCount = Util.getServerCount(this);
+ int activeServer = Util.getActiveServer(this);
+ for(int i = 1; i <= serverCount; i++) {
+ MenuItem item = drawerList.getMenu().add(MENU_GROUP_SERVER, MENU_ITEM_SERVER_BASE + i, MENU_ITEM_SERVER_BASE + i, Util.getServerName(this, i));
+ if(activeServer == i) {
+ item.setChecked(true);
}
-
- lastSelectedView = (TextView) view.findViewById(R.id.drawer_name);
- lastSelectedView.setTextAppearance(this, R.style.DSub_TextViewStyle_Bold);
- lastSelectedPosition = position;
+ }
+ drawerList.getMenu().setGroupCheckable(MENU_GROUP_SERVER, true, true);
+
+ showingTabs = false;
+ }
+ private void setDrawerItemVisible(int id, boolean visible) {
+ MenuItem item = drawerList.getMenu().findItem(id);
+ if(item != null) {
+ item.setVisible(visible);
}
}
+ protected void drawerItemSelected(String fragmentType) {
+ if(currentFragment != null) {
+ currentFragment.stopActionMode();
+ }
+ startFragmentActivity(fragmentType);
+ }
+
public void startFragmentActivity(String fragmentType) {
Intent intent = new Intent();
intent.setClass(SubsonicActivity.this, SubsonicFragmentActivity.class);
@@ -539,6 +615,9 @@ public class SubsonicActivity extends ActionBarActivity implements OnItemSelecte
if(!"".equals(fragmentType)) {
intent.putExtra(Constants.INTENT_EXTRA_FRAGMENT_TYPE, fragmentType);
}
+ if(lastSelectedPosition != 0) {
+ intent.putExtra(Constants.FRAGMENT_POSITION, lastSelectedPosition);
+ }
startActivity(intent);
finish();
}
@@ -575,6 +654,10 @@ public class SubsonicActivity extends ActionBarActivity implements OnItemSelecte
}
}
+ protected SubsonicFragment getCurrentFragment() {
+ return this.currentFragment;
+ }
+
public void replaceFragment(SubsonicFragment fragment, int tag) {
replaceFragment(fragment, tag, false);
}
@@ -628,11 +711,11 @@ public class SubsonicActivity extends ActionBarActivity implements OnItemSelecte
backStack.remove(backStack.size() - 1);
}
}
-
+
// Add fragment to the right container
trans.setCustomAnimations(R.anim.enter_from_right, R.anim.exit_to_left, R.anim.enter_from_left, R.anim.exit_to_right);
trans.add(R.id.fragment_second_container, fragment, tag + "");
-
+
// Commit it all
trans.commit();
}
@@ -656,7 +739,7 @@ public class SubsonicActivity extends ActionBarActivity implements OnItemSelecte
trans.commit();
} else {
FragmentTransaction trans = getSupportFragmentManager().beginTransaction();
-
+
// Remove old right fragment
trans.setCustomAnimations(R.anim.enter_from_left, R.anim.exit_to_right, R.anim.enter_from_right, R.anim.exit_to_left);
trans.remove(oldFrag);
@@ -682,7 +765,7 @@ public class SubsonicActivity extends ActionBarActivity implements OnItemSelecte
secondaryContainer.startAnimation(AnimationUtils.loadAnimation(this, R.anim.exit_to_right));
secondaryContainer.setVisibility(View.GONE);
}
-
+
trans.commit();
}
recreateSpinner();
@@ -695,16 +778,19 @@ public class SubsonicActivity extends ActionBarActivity implements OnItemSelecte
}
currentFragment.invalidate();
- populateDrawer();
+ populateTabs();
}
-
+
supportInvalidateOptionsMenu();
}
-
+
protected void recreateSpinner() {
if(currentFragment == null || currentFragment.getTitle() == null) {
return;
}
+ if(spinnerAdapter == null) {
+ createCustomActionBarView();
+ }
if(backStack.size() > 0) {
spinnerAdapter.clear();
@@ -727,6 +813,7 @@ public class SubsonicActivity extends ActionBarActivity implements OnItemSelecte
getSupportActionBar().setDisplayShowCustomEnabled(true);
}
} else if(!isTv()) {
+ getSupportActionBar().setTitle(currentFragment.getTitle());
getSupportActionBar().setDisplayShowCustomEnabled(false);
}
}
@@ -735,6 +822,7 @@ public class SubsonicActivity extends ActionBarActivity implements OnItemSelecte
Intent intent = new Intent(this, ((Object) this).getClass());
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.putExtras(getIntent());
+ intent.putExtra(Constants.FRAGMENT_POSITION, lastSelectedPosition);
Util.startActivityWithoutTransition(this, intent);
}
@@ -745,7 +833,7 @@ public class SubsonicActivity extends ActionBarActivity implements OnItemSelecte
theme = theme.substring(0, theme.indexOf("_fullscreen"));
Util.setTheme(this, theme);
}
-
+
Util.applyTheme(this, theme);
}
private void applyFullscreen() {
@@ -754,8 +842,8 @@ public class SubsonicActivity extends ActionBarActivity implements OnItemSelecte
// Hide additional elements on higher Android versions
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
int flags = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
- View.SYSTEM_UI_FLAG_FULLSCREEN |
- View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
+ View.SYSTEM_UI_FLAG_FULLSCREEN |
+ View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
getWindow().getDecorView().setSystemUiVisibility(flags);
} else if(Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
@@ -786,20 +874,35 @@ public class SubsonicActivity extends ActionBarActivity implements OnItemSelecte
if(finished) {
return null;
}
-
+
// If service is not available, request it to start and wait for it.
for (int i = 0; i < 5; i++) {
DownloadService downloadService = DownloadService.getInstance();
if (downloadService != null) {
- return downloadService;
+ break;
}
Log.w(TAG, "DownloadService not running. Attempting to start it.");
startService(new Intent(this, DownloadService.class));
Util.sleepQuietly(50L);
}
- return DownloadService.getInstance();
+
+ final DownloadService downloadService = DownloadService.getInstance();
+ if(downloadService != null && afterServiceAvailable.size() > 0) {
+ for(Runnable runnable: afterServiceAvailable) {
+ handler.post(runnable);
+ }
+ afterServiceAvailable.clear();
+ }
+ return downloadService;
+ }
+ public void runWhenServiceAvailable(Runnable runnable) {
+ if(getDownloadService() != null) {
+ runnable.run();
+ } else {
+ afterServiceAvailable.add(runnable);
+ }
}
-
+
public static String getThemeName() {
return theme;
}
@@ -811,6 +914,49 @@ public class SubsonicActivity extends ActionBarActivity implements OnItemSelecte
return touchscreen;
}
+ public void openNowPlaying() {
+
+ }
+ public void closeNowPlaying() {
+
+ }
+
+ public void setActiveServer(int instance) {
+ if (Util.getActiveServer(this) != instance) {
+ final DownloadService service = getDownloadService();
+ if (service != null) {
+ new SilentBackgroundTask<Void>(this) {
+ @Override
+ protected Void doInBackground() throws Throwable {
+ service.clearIncomplete();
+ return null;
+ }
+ }.execute();
+
+ }
+ Util.setActiveServer(this, instance);
+ invalidate();
+ UserUtil.refreshCurrentUser(this, false, true);
+ updateDrawerHeader();
+ }
+ }
+ public void updateDrawerHeader() {
+ if(Util.isOffline(this)) {
+ drawerServerName.setText(R.string.select_album_offline);
+ drawerUserName.setText("");
+ drawerUserAvatar.setVisibility(View.GONE);
+ drawerHeader.setClickable(false);
+ drawerHeaderToggle.setVisibility(View.GONE);
+ } else {
+ drawerServerName.setText(Util.getServerName(this));
+ drawerUserName.setText(UserUtil.getCurrentUsername(this));
+ drawerUserAvatar.setVisibility(View.VISIBLE);
+ getImageLoader().loadAvatar(this, drawerUserAvatar, UserUtil.getCurrentUsername(this));
+ drawerHeader.setClickable(true);
+ drawerHeaderToggle.setVisibility(View.VISIBLE);
+ }
+ }
+
private void setUncaughtExceptionHandler() {
Thread.UncaughtExceptionHandler handler = Thread.getDefaultUncaughtExceptionHandler();
if (!(handler instanceof SubsonicActivity.SubsonicUncaughtExceptionHandler)) {
diff --git a/app/src/main/java/github/daneren2005/dsub/activity/SubsonicFragmentActivity.java b/app/src/main/java/github/daneren2005/dsub/activity/SubsonicFragmentActivity.java
index 6614e09d..778c4982 100644
--- a/app/src/main/java/github/daneren2005/dsub/activity/SubsonicFragmentActivity.java
+++ b/app/src/main/java/github/daneren2005/dsub/activity/SubsonicFragmentActivity.java
@@ -30,16 +30,22 @@ import android.content.res.TypedArray;
import android.os.Bundle;
import android.os.Handler;
import android.preference.PreferenceManager;
+import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
+import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.MenuItem;
import android.view.View;
+import android.view.ViewGroup;
import android.widget.ImageButton;
+import android.widget.ImageView;
import android.widget.TextView;
+import com.sothree.slidinguppanel.SlidingUpPanelLayout;
+
import java.io.File;
-import java.text.SimpleDateFormat;
import java.util.Date;
+import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
@@ -53,6 +59,7 @@ import github.daneren2005.dsub.fragments.AdminFragment;
import github.daneren2005.dsub.fragments.ChatFragment;
import github.daneren2005.dsub.fragments.DownloadFragment;
import github.daneren2005.dsub.fragments.MainFragment;
+import github.daneren2005.dsub.fragments.NowPlayingFragment;
import github.daneren2005.dsub.fragments.SearchFragment;
import github.daneren2005.dsub.fragments.SelectArtistFragment;
import github.daneren2005.dsub.fragments.SelectBookmarkFragment;
@@ -66,7 +73,6 @@ import github.daneren2005.dsub.service.DownloadService;
import github.daneren2005.dsub.service.MusicService;
import github.daneren2005.dsub.service.MusicServiceFactory;
import github.daneren2005.dsub.updates.Updater;
-import github.daneren2005.dsub.util.BackgroundTask;
import github.daneren2005.dsub.util.Constants;
import github.daneren2005.dsub.util.FileUtil;
import github.daneren2005.dsub.util.SilentBackgroundTask;
@@ -77,15 +83,21 @@ import github.daneren2005.dsub.view.ChangeLog;
/**
* Created by Scott on 10/14/13.
*/
-public class SubsonicFragmentActivity extends SubsonicActivity {
+public class SubsonicFragmentActivity extends SubsonicActivity implements DownloadService.OnSongChangedListener {
private static String TAG = SubsonicFragmentActivity.class.getSimpleName();
private static boolean infoDialogDisplayed;
private static boolean sessionInitialized = false;
private static long ALLOWED_SKEW = 30000L;
- private ScheduledExecutorService executorService;
+ private SlidingUpPanelLayout slideUpPanel;
+ private SlidingUpPanelLayout.PanelSlideListener panelSlideListener;
+ private NowPlayingFragment nowPlayingFragment;
+ private SubsonicFragment secondaryFragment;
+ private Toolbar mainToolbar;
+ private Toolbar nowPlayingToolbar;
+
private View bottomBar;
- private View coverArtView;
+ private ImageView coverArtView;
private TextView trackView;
private TextView artistView;
private ImageButton startButton;
@@ -95,6 +107,24 @@ public class SubsonicFragmentActivity extends SubsonicActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
+ if(savedInstanceState == null) {
+ String fragmentType = getIntent().getStringExtra(Constants.INTENT_EXTRA_FRAGMENT_TYPE);
+ boolean firstRun = false;
+ if (fragmentType == null) {
+ fragmentType = Util.openToTab(this);
+ if (fragmentType != null) {
+ firstRun = true;
+ }
+ }
+
+ if ("".equals(fragmentType) || fragmentType == null || firstRun) {
+ // Initial startup stuff
+ if (!sessionInitialized) {
+ loadSession();
+ }
+ }
+ }
+
super.onCreate(savedInstanceState);
if (getIntent().hasExtra(Constants.INTENT_EXTRA_NAME_EXIT)) {
stopService(new Intent(this, DownloadService.class));
@@ -102,75 +132,153 @@ public class SubsonicFragmentActivity extends SubsonicActivity {
getImageLoader().clearCache();
} else if(getIntent().hasExtra(Constants.INTENT_EXTRA_NAME_DOWNLOAD_VIEW)) {
getIntent().putExtra(Constants.INTENT_EXTRA_FRAGMENT_TYPE, "Download");
- if(drawerAdapter != null) {
- drawerAdapter.setDownloadVisible(true);
- }
- } else if(getIntent().hasExtra(Constants.INTENT_EXTRA_NAME_DOWNLOAD)) {
- DownloadService service = getDownloadService();
- if((service != null && service.getCurrentPlaying() != null)) {
- getIntent().removeExtra(Constants.INTENT_EXTRA_NAME_DOWNLOAD);
- Intent intent = new Intent();
- intent.setClass(this, DownloadActivity.class);
- startActivity(intent);
- }
+ lastSelectedPosition = R.id.drawer_downloading;
}
setContentView(R.layout.abstract_fragment_activity);
UserUtil.seedCurrentUser(this);
if (findViewById(R.id.fragment_container) != null && savedInstanceState == null) {
String fragmentType = getIntent().getStringExtra(Constants.INTENT_EXTRA_FRAGMENT_TYPE);
- boolean firstRun = false;
if(fragmentType == null) {
fragmentType = Util.openToTab(this);
if(fragmentType != null) {
getIntent().putExtra(Constants.INTENT_EXTRA_FRAGMENT_TYPE, fragmentType);
- firstRun = true;
+
+ switch(fragmentType) {
+ case "Home":
+ lastSelectedPosition = R.id.drawer_home;
+ break;
+ case "Artist":
+ lastSelectedPosition = R.id.drawer_library;
+ break;
+ case "Playlist":
+ lastSelectedPosition = R.id.drawer_playlists;
+ break;
+ case "Podcast":
+ lastSelectedPosition = R.id.drawer_podcasts;
+ break;
+ case "Bookmark":
+ lastSelectedPosition = R.id.drawer_bookmarks;
+ break;
+ case "Share":
+ lastSelectedPosition = R.id.drawer_shares;
+ break;
+ case "Chat":
+ lastSelectedPosition = R.id.drawer_chat;
+ break;
+ }
+ } else {
+ lastSelectedPosition = R.id.drawer_home;
}
- }
- currentFragment = getNewFragment(fragmentType);
-
- if("".equals(fragmentType) || fragmentType == null || firstRun) {
- // Initial startup stuff
- if(!sessionInitialized) {
- loadSession();
+
+ MenuItem item = drawerList.getMenu().findItem(lastSelectedPosition);
+ if(item != null) {
+ item.setChecked(true);
}
}
-
+ currentFragment = getNewFragment(fragmentType);
currentFragment.setPrimaryFragment(true);
getSupportFragmentManager().beginTransaction().add(R.id.fragment_container, currentFragment, currentFragment.getSupportTag() + "").commit();
-
+
if(getIntent().getStringExtra(Constants.INTENT_EXTRA_NAME_QUERY) != null) {
SearchFragment fragment = new SearchFragment();
replaceFragment(fragment, fragment.getSupportTag());
}
-
+
// If a album type is set, switch to that album type view
String albumType = getIntent().getStringExtra(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TYPE);
if(albumType != null) {
SubsonicFragment fragment = new SelectDirectoryFragment();
-
+
Bundle args = new Bundle();
args.putString(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TYPE, albumType);
args.putInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_SIZE, 20);
args.putInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_OFFSET, 0);
-
+
fragment.setArguments(args);
replaceFragment(fragment, fragment.getSupportTag());
}
}
- bottomBar = findViewById(R.id.bottom_bar);
- bottomBar.setOnClickListener(new View.OnClickListener() {
- public void onClick(View v) {
- Intent intent = new Intent();
- intent.setClass(v.getContext(), DownloadActivity.class);
- startActivity(intent);
+ slideUpPanel = (SlidingUpPanelLayout) findViewById(R.id.slide_up_panel);
+ panelSlideListener = new SlidingUpPanelLayout.PanelSlideListener() {
+ @Override
+ public void onPanelSlide(View panel, float slideOffset) {
+
}
- });
- coverArtView = bottomBar.findViewById(R.id.album_art);
+
+ @Override
+ public void onPanelCollapsed(View panel) {
+ bottomBar.setVisibility(View.VISIBLE);
+ nowPlayingToolbar.setVisibility(View.GONE);
+ nowPlayingFragment.setPrimaryFragment(false);
+ setSupportActionBar(mainToolbar);
+
+ if(getSupportActionBar().getCustomView() == null) {
+ createCustomActionBarView();
+ }
+ recreateSpinner();
+ if(drawerToggle != null && backStack.size() > 0) {
+ drawerToggle.setDrawerIndicatorEnabled(false);
+ } else {
+ drawerToggle.setDrawerIndicatorEnabled(true);
+ }
+ }
+
+ @Override
+ public void onPanelExpanded(View panel) {
+ currentFragment.stopActionMode();
+
+ // Disable custom view before switching
+ getSupportActionBar().setDisplayShowCustomEnabled(false);
+
+ bottomBar.setVisibility(View.GONE);
+ nowPlayingToolbar.setVisibility(View.VISIBLE);
+ setSupportActionBar(nowPlayingToolbar);
+ nowPlayingFragment.setPrimaryFragment(true);
+
+ drawerToggle.setDrawerIndicatorEnabled(false);
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ }
+
+ @Override
+ public void onPanelAnchored(View panel) {
+
+ }
+
+ @Override
+ public void onPanelHidden(View panel) {
+
+ }
+ };
+ slideUpPanel.setPanelSlideListener(panelSlideListener);
+
+ if(getIntent().hasExtra(Constants.INTENT_EXTRA_NAME_DOWNLOAD)) {
+ // Post this later so it actually runs
+ handler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ openNowPlaying();
+ }
+ }, 200);
+ }
+
+ bottomBar = findViewById(R.id.bottom_bar);
+ mainToolbar = (Toolbar) findViewById(R.id.main_toolbar);
+ nowPlayingToolbar = (Toolbar) findViewById(R.id.now_playing_toolbar);
+ coverArtView = (ImageView) bottomBar.findViewById(R.id.album_art);
trackView = (TextView) bottomBar.findViewById(R.id.track_name);
artistView = (TextView) bottomBar.findViewById(R.id.artist_name);
+ setSupportActionBar(mainToolbar);
+
+ if (findViewById(R.id.fragment_container) != null && savedInstanceState == null) {
+ nowPlayingFragment = new NowPlayingFragment();
+ FragmentTransaction trans = getSupportFragmentManager().beginTransaction();
+ trans.add(R.id.now_playing_fragment_container, nowPlayingFragment, nowPlayingFragment.getTag() + "");
+ trans.commit();
+ }
+
ImageButton previousButton = (ImageButton) findViewById(R.id.download_previous);
previousButton.setOnClickListener(new View.OnClickListener() {
@Override
@@ -185,11 +293,6 @@ public class SubsonicFragmentActivity extends SubsonicActivity {
getDownloadService().previous();
return null;
}
-
- @Override
- protected void done(Void result) {
- update();
- }
}.execute();
}
});
@@ -210,11 +313,6 @@ public class SubsonicFragmentActivity extends SubsonicActivity {
return null;
}
-
- @Override
- protected void done(Void result) {
- update();
- }
}.execute();
}
});
@@ -233,11 +331,6 @@ public class SubsonicFragmentActivity extends SubsonicActivity {
getDownloadService().next();
return null;
}
-
- @Override
- protected void done(Void result) {
- update();
- }
}.execute();
}
});
@@ -262,7 +355,7 @@ public class SubsonicFragmentActivity extends SubsonicActivity {
}
}
}
-
+
@Override
public void onNewIntent(Intent intent) {
super.onNewIntent(intent);
@@ -276,7 +369,6 @@ public class SubsonicFragmentActivity extends SubsonicActivity {
if (query != null) {
((SearchFragment)currentFragment).search(query, autoplay);
} else {
- ((SearchFragment)currentFragment).populateList();
if (requestsearch) {
onSearchRequested();
}
@@ -300,19 +392,6 @@ public class SubsonicFragmentActivity extends SubsonicActivity {
public void onResume() {
super.onResume();
- final Handler handler = new Handler();
- Runnable runnable = new Runnable() {
- @Override
- public void run() {
- handler.post(new Runnable() {
- @Override
- public void run() {
- update();
- }
- });
- }
- };
-
if(getIntent().hasExtra(Constants.INTENT_EXTRA_VIEW_ALBUM)) {
SubsonicFragment fragment = new SelectDirectoryFragment();
Bundle args = new Bundle();
@@ -329,29 +408,46 @@ public class SubsonicFragmentActivity extends SubsonicActivity {
replaceFragment(fragment, fragment.getSupportTag());
getIntent().removeExtra(Constants.INTENT_EXTRA_VIEW_ALBUM);
- if("Artist".equals(getIntent().getStringExtra(Constants.INTENT_EXTRA_FRAGMENT_TYPE))) {
- lastSelectedPosition = 1;
- }
}
createAccount();
-
- executorService = Executors.newSingleThreadScheduledExecutor();
- executorService.scheduleWithFixedDelay(runnable, 0L, 1000L, TimeUnit.MILLISECONDS);
+ runWhenServiceAvailable(new Runnable() {
+ @Override
+ public void run() {
+ getDownloadService().addOnSongChangedListener(SubsonicFragmentActivity.this, true);
+ }
+ });
}
@Override
public void onPause() {
super.onPause();
- executorService.shutdown();
+ DownloadService downloadService = getDownloadService();
+ if(downloadService != null) {
+ downloadService.removeOnSongChangeListener(this);
+ }
}
@Override
+ public void onSaveInstanceState(Bundle savedInstanceState) {
+ super.onSaveInstanceState(savedInstanceState);
+ savedInstanceState.putString(Constants.MAIN_NOW_PLAYING, nowPlayingFragment.getTag());
+ savedInstanceState.putInt(Constants.MAIN_SLIDE_PANEL_STATE, slideUpPanel.getPanelState().hashCode());
+ }
+ @Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
+
+ String id = savedInstanceState.getString(Constants.MAIN_NOW_PLAYING);
+ FragmentManager fm = getSupportFragmentManager();
+ nowPlayingFragment = (NowPlayingFragment) fm.findFragmentByTag(id);
if(drawerToggle != null && backStack.size() > 0) {
drawerToggle.setDrawerIndicatorEnabled(false);
}
+
+ if(savedInstanceState.getInt(Constants.MAIN_SLIDE_PANEL_STATE, -1) == SlidingUpPanelLayout.PanelState.EXPANDED.hashCode()) {
+ panelSlideListener.onPanelExpanded(null);
+ }
}
@Override
@@ -369,7 +465,9 @@ public class SubsonicFragmentActivity extends SubsonicActivity {
@Override
public void onBackPressed() {
- if(onBackPressedSupport()) {
+ if(slideUpPanel.getPanelState() == SlidingUpPanelLayout.PanelState.EXPANDED && secondaryFragment == null) {
+ slideUpPanel.setPanelState(SlidingUpPanelLayout.PanelState.COLLAPSED);
+ } else if(onBackPressedSupport()) {
if(!Util.disableExitPrompt(this) && lastBackPressTime < (System.currentTimeMillis() - 4000)) {
lastBackPressTime = System.currentTimeMillis();
Util.toast(this, R.string.main_back_confirm);
@@ -380,37 +478,99 @@ public class SubsonicFragmentActivity extends SubsonicActivity {
}
@Override
+ public boolean onBackPressedSupport() {
+ if(slideUpPanel.getPanelState() == SlidingUpPanelLayout.PanelState.EXPANDED) {
+ removeCurrent();
+ return false;
+ } else {
+ return super.onBackPressedSupport();
+ }
+ }
+
+ @Override
+ protected SubsonicFragment getCurrentFragment() {
+ if(slideUpPanel.getPanelState() == SlidingUpPanelLayout.PanelState.EXPANDED) {
+ return nowPlayingFragment;
+ } else {
+ return super.getCurrentFragment();
+ }
+ }
+
+ @Override
public void replaceFragment(SubsonicFragment fragment, int tag, boolean replaceCurrent) {
- super.replaceFragment(fragment, tag, replaceCurrent);
- if(drawerToggle != null) {
- drawerToggle.setDrawerIndicatorEnabled(false);
+ if(slideUpPanel.getPanelState() == SlidingUpPanelLayout.PanelState.EXPANDED) {
+ secondaryFragment = fragment;
+ nowPlayingFragment.setPrimaryFragment(false);
+ secondaryFragment.setPrimaryFragment(true);
+ supportInvalidateOptionsMenu();
+
+ FragmentTransaction trans = getSupportFragmentManager().beginTransaction();
+ trans.setCustomAnimations(R.anim.enter_from_right, R.anim.exit_to_left, R.anim.enter_from_left, R.anim.exit_to_right);
+ trans.hide(nowPlayingFragment);
+ trans.add(R.id.now_playing_fragment_container, secondaryFragment, tag + "");
+ trans.commit();
+ } else {
+ super.replaceFragment(fragment, tag, replaceCurrent);
+ if (drawerToggle != null) {
+ drawerToggle.setDrawerIndicatorEnabled(false);
+ }
}
}
@Override
public void removeCurrent() {
- super.removeCurrent();
- if(drawerToggle != null && backStack.isEmpty()) {
- drawerToggle.setDrawerIndicatorEnabled(true);
+ if(slideUpPanel.getPanelState() == SlidingUpPanelLayout.PanelState.EXPANDED && secondaryFragment != null) {
+ FragmentTransaction trans = getSupportFragmentManager().beginTransaction();
+ trans.setCustomAnimations(R.anim.enter_from_left, R.anim.exit_to_right, R.anim.enter_from_right, R.anim.exit_to_left);
+ trans.remove(secondaryFragment);
+ trans.show(nowPlayingFragment);
+ trans.commit();
+
+ secondaryFragment = null;
+ nowPlayingFragment.setPrimaryFragment(true);
+ supportInvalidateOptionsMenu();
+ } else {
+ super.removeCurrent();
+ if (drawerToggle != null && backStack.isEmpty()) {
+ drawerToggle.setDrawerIndicatorEnabled(true);
+ }
}
}
-
+
+ @Override
+ public void setTitle(CharSequence title) {
+ if(slideUpPanel.getPanelState() == SlidingUpPanelLayout.PanelState.EXPANDED) {
+ getSupportActionBar().setTitle(title);
+ } else {
+ super.setTitle(title);
+ }
+ }
+
+ @Override
+ protected void drawerItemSelected(String fragmentType) {
+ super.drawerItemSelected(fragmentType);
+
+ if(slideUpPanel.getPanelState() == SlidingUpPanelLayout.PanelState.EXPANDED) {
+ slideUpPanel.setPanelState(SlidingUpPanelLayout.PanelState.COLLAPSED);
+ }
+ }
+
@Override
public void startFragmentActivity(String fragmentType) {
// Create a transaction that does all of this
FragmentTransaction trans = getSupportFragmentManager().beginTransaction();
-
+
// Clear existing stack
for(int i = backStack.size() - 1; i >= 0; i--) {
trans.remove(backStack.get(i));
}
trans.remove(currentFragment);
backStack.clear();
-
+
// Create new stack
currentFragment = getNewFragment(fragmentType);
currentFragment.setPrimaryFragment(true);
trans.add(R.id.fragment_container, currentFragment, currentFragment.getSupportTag() + "");
-
+
// Done, cleanup
trans.commit();
supportInvalidateOptionsMenu();
@@ -426,7 +586,16 @@ public class SubsonicFragmentActivity extends SubsonicActivity {
drawerToggle.setDrawerIndicatorEnabled(true);
}
}
-
+
+ @Override
+ public void openNowPlaying() {
+ slideUpPanel.setPanelState(SlidingUpPanelLayout.PanelState.EXPANDED);
+ }
+ @Override
+ public void closeNowPlaying() {
+ slideUpPanel.setPanelState(SlidingUpPanelLayout.PanelState.COLLAPSED);
+ }
+
private SubsonicFragment getNewFragment(String fragmentType) {
if("Artist".equals(fragmentType)) {
return new SelectArtistFragment();
@@ -447,38 +616,6 @@ public class SubsonicFragmentActivity extends SubsonicActivity {
} else {
return new MainFragment();
}
- }
-
- private void update() {
- DownloadService downloadService = getDownloadService();
- if (downloadService == null) {
- return;
- }
-
- DownloadFile current = downloadService.getCurrentPlaying();
- PlayerState state = downloadService.getPlayerState();
- if(current == currentPlaying && state == currentState) {
- return;
- } else {
- currentPlaying = current;
- currentState = state;
- }
-
- MusicDirectory.Entry song = null;
- if(current != null) {
- song = current.getSong();
- trackView.setText(song.getTitle());
- artistView.setText(song.getArtist());
- } else {
- trackView.setText("Title");
- artistView.setText("Artist");
- }
-
- getImageLoader().loadImage(coverArtView, song, false, false);
- int[] attrs = new int[] {(state == PlayerState.STARTED) ? R.attr.media_button_pause : R.attr.media_button_start};
- TypedArray typedArray = this.obtainStyledAttributes(attrs);
- startButton.setImageResource(typedArray.getResourceId(0, 0));
- typedArray.recycle();
}
public void checkUpdates() {
@@ -502,7 +639,7 @@ public class SubsonicFragmentActivity extends SubsonicActivity {
if(ServerInfo.canSavePlayQueue(this) && !Util.isOffline(this)) {
loadRemotePlayQueue();
}
-
+
sessionInitialized = true;
}
private void loadSettings() {
@@ -551,7 +688,7 @@ public class SubsonicFragmentActivity extends SubsonicActivity {
return true;
}
}
-
+
private void loadBookmarks() {
final Context context = this;
new SilentBackgroundTask<Void>(context) {
@@ -562,7 +699,7 @@ public class SubsonicFragmentActivity extends SubsonicActivity {
return null;
}
-
+
@Override
public void error(Throwable error) {
Log.e(TAG, "Failed to get bookmarks", error);
@@ -683,4 +820,54 @@ public class SubsonicFragmentActivity extends SubsonicActivity {
}
}
}
+
+ public Toolbar getActiveToolbar() {
+ return slideUpPanel.getPanelState() == SlidingUpPanelLayout.PanelState.EXPANDED ? nowPlayingToolbar : mainToolbar;
+ }
+
+ @Override
+ public void onSongChanged(DownloadFile currentPlaying, int currentPlayingIndex) {
+ this.currentPlaying = currentPlaying;
+
+ MusicDirectory.Entry song = null;
+ if (currentPlaying != null) {
+ song = currentPlaying.getSong();
+ trackView.setText(song.getTitle());
+ artistView.setText(song.getArtist());
+ } else {
+ trackView.setText(R.string.main_title);
+ artistView.setText(R.string.main_artist);
+ }
+
+ if (coverArtView != null) {
+ int height = coverArtView.getHeight();
+ if (height <= 0) {
+ int[] attrs = new int[]{R.attr.actionBarSize};
+ TypedArray typedArray = this.obtainStyledAttributes(attrs);
+ height = typedArray.getDimensionPixelSize(0, 0);
+ typedArray.recycle();
+ }
+ getImageLoader().loadImage(coverArtView, song, false, height, false);
+ }
+ }
+
+ @Override
+ public void onSongsChanged(List<DownloadFile> songs, DownloadFile currentPlaying, int currentPlayingIndex) {
+ if(this.currentPlaying != currentPlaying || currentPlaying == null) {
+ onSongChanged(currentPlaying, currentPlayingIndex);
+ }
+ }
+
+ @Override
+ public void onSongProgress(DownloadFile currentPlaying, int millisPlayed, Integer duration, boolean isSeekable) {
+
+ }
+
+ @Override
+ public void onStateUpdate(DownloadFile downloadFile, PlayerState playerState) {
+ int[] attrs = new int[]{(playerState == PlayerState.STARTED) ? R.attr.actionbar_pause : R.attr.actionbar_start};
+ TypedArray typedArray = this.obtainStyledAttributes(attrs);
+ startButton.setImageResource(typedArray.getResourceId(0, 0));
+ typedArray.recycle();
+ }
}
diff --git a/app/src/main/java/github/daneren2005/dsub/adapter/AlbumGridAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/AlbumGridAdapter.java
deleted file mode 100644
index eb187569..00000000
--- a/app/src/main/java/github/daneren2005/dsub/adapter/AlbumGridAdapter.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- 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 <http://www.gnu.org/licenses/>.
- Copyright 2014 (C) Scott Jackson
-*/
-
-package github.daneren2005.dsub.adapter;
-
-import android.content.Context;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
-
-import java.util.List;
-
-import github.daneren2005.dsub.domain.MusicDirectory;
-import github.daneren2005.dsub.util.ImageLoader;
-import github.daneren2005.dsub.view.AlbumCell;
-
-public class AlbumGridAdapter extends ArrayAdapter<MusicDirectory.Entry> {
- private final static String TAG = AlbumGridAdapter.class.getSimpleName();
- private final Context activity;
- private final ImageLoader imageLoader;
- private List<MusicDirectory.Entry> entries;
- private boolean showArtist;
-
- public AlbumGridAdapter(Context activity, ImageLoader imageLoader, List<MusicDirectory.Entry> entries, boolean showArtist) {
- super(activity, android.R.layout.simple_list_item_1, entries);
- this.entries = entries;
- this.activity = activity;
- this.imageLoader = imageLoader;
-
- // Always show artist if they aren't all the same
- if(!showArtist) {
- String artist = null;
- for(MusicDirectory.Entry entry: entries) {
- if(artist == null) {
- artist = entry.getArtist();
- }
-
- if(artist != null && !artist.equals(entry.getArtist())) {
- showArtist = true;
- }
- }
- }
- this.showArtist = showArtist;
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- MusicDirectory.Entry entry = getItem(position);
-
- AlbumCell view;
- if(convertView instanceof AlbumCell) {
- view = (AlbumCell) convertView;
- } else {
- view = new AlbumCell(activity);
- }
-
- view.setShowArtist(showArtist);
- view.setObject(entry, imageLoader);
- return view;
- }
-}
diff --git a/app/src/main/java/github/daneren2005/dsub/adapter/AlbumListAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/AlbumListAdapter.java
index 7151362c..47f82259 100644
--- a/app/src/main/java/github/daneren2005/dsub/adapter/AlbumListAdapter.java
+++ b/app/src/main/java/github/daneren2005/dsub/adapter/AlbumListAdapter.java
@@ -72,11 +72,11 @@ public class AlbumListAdapter extends EndlessAdapter implements SectionIndexer {
MusicService service = MusicServiceFactory.getMusicService(context);
MusicDirectory result;
if(("genres".equals(type) && ServerInfo.checkServerVersion(context, "1.10.0")) || "years".equals(type)) {
- result = service.getAlbumList(type, extra, size, offset, context, null);
+ result = service.getAlbumList(type, extra, size, offset, false, context, null);
} else if("genres".equals(type) || "genres-songs".equals(type)) {
result = service.getSongsByGenre(extra, size, offset, context, null);
} else {
- result = service.getAlbumList(type, size, offset, context, null);
+ result = service.getAlbumList(type, size, offset, shouldIndex, context, null);
}
entries = result.getChildren();
return entries.size() > 0;
diff --git a/app/src/main/java/github/daneren2005/dsub/adapter/ArtistAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/ArtistAdapter.java
index 4d469faf..7461af69 100644
--- a/app/src/main/java/github/daneren2005/dsub/adapter/ArtistAdapter.java
+++ b/app/src/main/java/github/daneren2005/dsub/adapter/ArtistAdapter.java
@@ -1,97 +1,128 @@
/*
- This file is part of Subsonic.
+ 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 <http://www.gnu.org/licenses/>.
+ Copyright 2015 (C) Scott Jackson
+*/
- 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 <http://www.gnu.org/licenses/>.
-
- Copyright 2010 (C) Sindre Mehus
- */
package github.daneren2005.dsub.adapter;
import android.content.Context;
-import github.daneren2005.dsub.R;
-import java.util.List;
+import android.support.v7.widget.PopupMenu;
+import android.view.LayoutInflater;
+import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
-import android.widget.SectionIndexer;
+import android.widget.TextView;
+
+import java.util.List;
+
+import github.daneren2005.dsub.R;
import github.daneren2005.dsub.domain.Artist;
+import github.daneren2005.dsub.domain.MusicFolder;
+import github.daneren2005.dsub.util.Util;
import github.daneren2005.dsub.view.ArtistView;
+import github.daneren2005.dsub.view.UpdateView;
+
+public class ArtistAdapter extends SectionAdapter<Artist> {
+ public static int VIEW_TYPE_ARTIST = 4;
+
+ private List<MusicFolder> musicFolders;
+ private OnMusicFolderChanged onMusicFolderChanged;
+
+ public ArtistAdapter(Context context, List<Artist> artists, OnItemClickedListener listener) {
+ this(context, artists, null, listener, null);
+ }
+
+ public ArtistAdapter(Context context, List<Artist> artists, List<MusicFolder> musicFolders, OnItemClickedListener onItemClickedListener, OnMusicFolderChanged onMusicFolderChanged) {
+ super(context, artists);
+ this.musicFolders = musicFolders;
+ this.onItemClickedListener = onItemClickedListener;
+ this.onMusicFolderChanged = onMusicFolderChanged;
+
+ if(musicFolders != null) {
+ this.singleSectionHeader = true;
+ }
+ }
+
+ @Override
+ public UpdateView.UpdateViewHolder onCreateHeaderHolder(ViewGroup parent) {
+ final View header = LayoutInflater.from(context).inflate(R.layout.select_artist_header, parent, false);
+ header.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ PopupMenu popup = new PopupMenu(context, header.findViewById(R.id.select_artist_folder_2));
+
+ popup.getMenu().add(R.string.select_artist_all_folders);
+ for (MusicFolder musicFolder : musicFolders) {
+ popup.getMenu().add(musicFolder.getName());
+ }
+
+ popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
+ @Override
+ public boolean onMenuItemClick(MenuItem item) {
+ for (MusicFolder musicFolder : musicFolders) {
+ if(item.getTitle().equals(musicFolder.getName())) {
+ if(onMusicFolderChanged != null) {
+ onMusicFolderChanged.onMusicFolderChanged(musicFolder);
+ }
+ return true;
+ }
+ }
+
+ if(onMusicFolderChanged != null) {
+ onMusicFolderChanged.onMusicFolderChanged(null);
+ }
+ return true;
+ }
+ });
+ popup.show();
+ }
+ });
+
+ return new UpdateView.UpdateViewHolder(header, false);
+ }
+ @Override
+ public void onBindHeaderHolder(UpdateView.UpdateViewHolder holder, String header) {
+ TextView folderName = (TextView) holder.getView().findViewById(R.id.select_artist_folder_2);
-import java.util.ArrayList;
-import java.util.LinkedHashSet;
-import java.util.Set;
-
-/**
- * @author Sindre Mehus
- */
-public class ArtistAdapter extends ArrayAdapter<Artist> implements SectionIndexer {
-
- private final Context activity;
-
- // Both arrays are indexed by section ID.
- private final Object[] sections;
- private final Integer[] positions;
-
- public ArtistAdapter(Context activity, List<Artist> artists) {
- super(activity, R.layout.basic_list_item, artists);
- this.activity = activity;
-
- Set<String> sectionSet = new LinkedHashSet<String>(30);
- List<Integer> positionList = new ArrayList<Integer>(30);
- for (int i = 0; i < artists.size(); i++) {
- Artist artist = artists.get(i);
- String index = artist.getIndex();
- if (!sectionSet.contains(index)) {
- sectionSet.add(index);
- positionList.add(i);
- }
- }
- sections = sectionSet.toArray(new Object[sectionSet.size()]);
- positions = positionList.toArray(new Integer[positionList.size()]);
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- Artist entry = getItem(position);
- ArtistView view;
- if (convertView != null && convertView instanceof ArtistView) {
- view = (ArtistView) convertView;
+ String musicFolderId = Util.getSelectedMusicFolderId(context);
+ if(musicFolderId != null) {
+ for (MusicFolder musicFolder : musicFolders) {
+ if (musicFolder.getId().equals(musicFolderId)) {
+ folderName.setText(musicFolder.getName());
+ break;
+ }
+ }
} else {
- view = new ArtistView(activity);
+ folderName.setText(R.string.select_artist_all_folders);
}
- view.setObject(entry);
- return view;
- }
-
+ }
+
+ @Override
+ public UpdateView.UpdateViewHolder onCreateSectionViewHolder(ViewGroup parent, int viewType) {
+ return new UpdateView.UpdateViewHolder(new ArtistView(context));
+ }
+
@Override
- public Object[] getSections() {
- return sections;
- }
-
- @Override
- public int getPositionForSection(int section) {
- section = Math.min(section, positions.length - 1);
- return positions[section];
- }
-
- @Override
- public int getSectionForPosition(int pos) {
- for (int i = 0; i < sections.length - 1; i++) {
- if (pos < positions[i + 1]) {
- return i;
- }
- }
- return sections.length - 1;
- }
+ public void onBindViewHolder(UpdateView.UpdateViewHolder holder, Artist item, int viewType) {
+ holder.getUpdateView().setObject(item);
+ }
+
+ @Override
+ public int getItemViewType(Artist item) {
+ return VIEW_TYPE_ARTIST;
+ }
+
+ public interface OnMusicFolderChanged {
+ void onMusicFolderChanged(MusicFolder musicFolder);
+ }
}
diff --git a/app/src/main/java/github/daneren2005/dsub/adapter/BasicListAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/BasicListAdapter.java
new file mode 100644
index 00000000..dfea91bf
--- /dev/null
+++ b/app/src/main/java/github/daneren2005/dsub/adapter/BasicListAdapter.java
@@ -0,0 +1,48 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+ Copyright 2015 (C) Scott Jackson
+*/
+
+package github.daneren2005.dsub.adapter;
+
+import android.content.Context;
+import android.view.ViewGroup;
+
+import java.util.List;
+
+import github.daneren2005.dsub.view.BasicListView;
+import github.daneren2005.dsub.view.UpdateView;
+
+public class BasicListAdapter extends SectionAdapter<String> {
+ public static int VIEW_TYPE_LINE = 1;
+
+ public BasicListAdapter(Context context, List<String> strings, OnItemClickedListener listener) {
+ super(context, strings);
+ this.onItemClickedListener = listener;
+ }
+
+ @Override
+ public UpdateView.UpdateViewHolder onCreateSectionViewHolder(ViewGroup parent, int viewType) {
+ return new UpdateView.UpdateViewHolder(new BasicListView(context));
+ }
+
+ @Override
+ public void onBindViewHolder(UpdateView.UpdateViewHolder holder, String item, int viewType) {
+ holder.getUpdateView().setObject(item);
+ }
+
+ @Override
+ public int getItemViewType(String item) {
+ return VIEW_TYPE_LINE;
+ }
+}
diff --git a/app/src/main/java/github/daneren2005/dsub/adapter/BookmarkAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/BookmarkAdapter.java
index 26d3e16a..8e960de4 100644
--- a/app/src/main/java/github/daneren2005/dsub/adapter/BookmarkAdapter.java
+++ b/app/src/main/java/github/daneren2005/dsub/adapter/BookmarkAdapter.java
@@ -1,20 +1,16 @@
/*
- This file is part of Subsonic.
-
+ 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 <http://www.gnu.org/licenses/>.
-
- Copyright 2010 (C) Sindre Mehus
+ Copyright 2015 (C) Scott Jackson
*/
package github.daneren2005.dsub.adapter;
@@ -22,9 +18,10 @@ package github.daneren2005.dsub.adapter;
import android.content.Context;
import java.util.List;
-import android.view.View;
+
+import android.view.Menu;
+import android.view.MenuInflater;
import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
import android.widget.TextView;
import github.daneren2005.dsub.R;
@@ -32,33 +29,48 @@ import github.daneren2005.dsub.domain.Bookmark;
import github.daneren2005.dsub.domain.MusicDirectory;
import github.daneren2005.dsub.util.Util;
import github.daneren2005.dsub.view.SongView;
+import github.daneren2005.dsub.view.UpdateView;
-public class BookmarkAdapter extends ArrayAdapter<MusicDirectory.Entry> {
+public class BookmarkAdapter extends SectionAdapter<MusicDirectory.Entry> {
private final static String TAG = BookmarkAdapter.class.getSimpleName();
- private Context activity;
- public BookmarkAdapter(Context activity, List<MusicDirectory.Entry> bookmarks) {
- super(activity, android.R.layout.simple_list_item_1, bookmarks);
- this.activity = activity;
+ public BookmarkAdapter(Context activity, List<MusicDirectory.Entry> bookmarks, OnItemClickedListener listener) {
+ super(activity, bookmarks);
+ this.onItemClickedListener = listener;
+ checkable = true;
}
-
- public View getView(int position, View convertView, ViewGroup parent) {
- MusicDirectory.Entry entry = getItem(position);
- Bookmark bookmark = entry.getBookmark();
- SongView view;
- if (convertView != null) {
- view = (SongView) convertView;
- } else {
- view = new SongView(activity);
- }
- view.setObject(entry, false);
-
+ @Override
+ public UpdateView.UpdateViewHolder onCreateSectionViewHolder(ViewGroup parent, int viewType) {
+ return new UpdateView.UpdateViewHolder(new SongView(context));
+ }
+
+ @Override
+ public void onBindViewHolder(UpdateView.UpdateViewHolder holder, MusicDirectory.Entry item, int viewType) {
+ SongView songView = (SongView) holder.getUpdateView();
+ Bookmark bookmark = item.getBookmark();
+
+ songView.setObject(item, true);
+
// Add current position to duration
- TextView durationTextView = (TextView) view.findViewById(R.id.song_duration);
+ TextView durationTextView = (TextView) songView.findViewById(R.id.song_duration);
String duration = durationTextView.getText().toString();
durationTextView.setText(Util.formatDuration(bookmark.getPosition() / 1000) + " / " + duration);
-
- return view;
+ }
+
+ @Override
+ public int getItemViewType(MusicDirectory.Entry item) {
+ return EntryGridAdapter.VIEW_TYPE_SONG;
+ }
+
+ @Override
+ public void onCreateActionModeMenu(Menu menu, MenuInflater menuInflater) {
+ if(Util.isOffline(context)) {
+ menuInflater.inflate(R.menu.multiselect_media_offline, menu);
+ } else {
+ menuInflater.inflate(R.menu.multiselect_media, menu);
+ }
+
+ menu.removeItem(R.id.menu_remove_playlist);
}
}
diff --git a/app/src/main/java/github/daneren2005/dsub/adapter/DetailsAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/DetailsAdapter.java
new file mode 100644
index 00000000..efafe27a
--- /dev/null
+++ b/app/src/main/java/github/daneren2005/dsub/adapter/DetailsAdapter.java
@@ -0,0 +1,62 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+ Copyright 2015 (C) Scott Jackson
+*/
+
+package github.daneren2005.dsub.adapter;
+
+import android.content.Context;
+import android.text.SpannableString;
+import android.text.method.LinkMovementMethod;
+import android.text.util.Linkify;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.TextView;
+
+import java.util.List;
+
+import github.daneren2005.dsub.R;
+
+public class DetailsAdapter extends ArrayAdapter<String> {
+ private List<String> headers;
+ private List<String> details;
+
+ public DetailsAdapter(Context context, int layout, List<String> headers, List<String> details) {
+ super(context, layout, headers);
+
+ this.headers = headers;
+ this.details = details;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent){
+ View view;
+ if(convertView == null) {
+ view = LayoutInflater.from(getContext()).inflate(R.layout.details_item, null);
+ } else {
+ view = convertView;
+ }
+
+ TextView nameView = (TextView) view.findViewById(R.id.detail_name);
+ TextView detailsView = (TextView) view.findViewById(R.id.detail_value);
+
+ nameView.setText(headers.get(position));
+
+ detailsView.setText(details.get(position));
+ Linkify.addLinks(detailsView, Linkify.ALL);
+
+ return view;
+ }
+}
diff --git a/app/src/main/java/github/daneren2005/dsub/adapter/DownloadFileAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/DownloadFileAdapter.java
index be9b4cb9..7e926d51 100644
--- a/app/src/main/java/github/daneren2005/dsub/adapter/DownloadFileAdapter.java
+++ b/app/src/main/java/github/daneren2005/dsub/adapter/DownloadFileAdapter.java
@@ -24,26 +24,33 @@ import java.util.List;
import github.daneren2005.dsub.service.DownloadFile;
import github.daneren2005.dsub.view.SongView;
+import github.daneren2005.dsub.view.UpdateView;
-public class DownloadFileAdapter extends ArrayAdapter<DownloadFile> {
- Context context;
+public class DownloadFileAdapter extends SectionAdapter<DownloadFile> {
+ public static int VIEW_TYPE_DOWNLOAD_FILE = 1;
public DownloadFileAdapter(Context context, List<DownloadFile> entries) {
- super(context, android.R.layout.simple_list_item_1, entries);
- this.context = context;
+ super(context, entries);
+ }
+ public DownloadFileAdapter(Context context, List<DownloadFile> entries, OnItemClickedListener onItemClickedListener) {
+ super(context, entries);
+ this.onItemClickedListener = onItemClickedListener;
+ }
+
+ @Override
+ public UpdateView.UpdateViewHolder onCreateSectionViewHolder(ViewGroup parent, int viewType) {
+ return new UpdateView.UpdateViewHolder(new SongView(context));
+ }
+
+ @Override
+ public void onBindViewHolder(UpdateView.UpdateViewHolder holder, DownloadFile item, int viewType) {
+ SongView songView = (SongView) holder.getUpdateView();
+ songView.setObject(item.getSong(), false);
+ songView.setDownloadFile(item);
}
@Override
- public View getView(int position, View convertView, ViewGroup parent) {
- SongView view;
- if (convertView != null && convertView instanceof SongView) {
- view = (SongView) convertView;
- } else {
- view = new SongView(context);
- }
- DownloadFile downloadFile = getItem(position);
- view.setObject(downloadFile.getSong(), false);
- view.setDownloadFile(downloadFile);
- return view;
+ public int getItemViewType(DownloadFile item) {
+ return VIEW_TYPE_DOWNLOAD_FILE;
}
}
diff --git a/app/src/main/java/github/daneren2005/dsub/adapter/DrawerAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/DrawerAdapter.java
deleted file mode 100644
index b0a4a33d..00000000
--- a/app/src/main/java/github/daneren2005/dsub/adapter/DrawerAdapter.java
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- 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 <http://www.gnu.org/licenses/>.
-
- Copyright 2009 (C) Sindre Mehus
-*/
-package github.daneren2005.dsub.adapter;
-
-import android.content.Context;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import java.util.List;
-
-import github.daneren2005.dsub.R;
-
-/**
- * Created by Scott on 11/8/13.
- */
-public class DrawerAdapter extends ArrayAdapter<String> {
- private static String TAG = DrawerAdapter.class.getSimpleName();
- private Context context;
- private List<String> items;
- private List<Integer> icons;
- private List<Boolean> visible;
- private int selectedPosition = -1;
-
- public DrawerAdapter(Context context, List<String> items, List<Integer> icons, List<Boolean> visible) {
- super(context, R.layout.drawer_list_item, items);
-
- this.context = context;
- this.items = items;
- this.icons = icons;
- this.visible = visible;
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- position = getActualPosition(position);
- String item = items.get(position);
- Integer icon = icons.get(position);
-
- if(convertView == null) {
- convertView = LayoutInflater.from(context).inflate(R.layout.drawer_list_item, null);
- }
-
- TextView textView = (TextView) convertView.findViewById(R.id.drawer_name);
- textView.setText(item);
-
- if(selectedPosition == position) {
- textView.setTextAppearance(context, R.style.DSub_TextViewStyle_Bold);
- selectedPosition = -1;
- }
-
- ImageView iconView = (ImageView) convertView.findViewById(R.id.drawer_icon);
- iconView.setImageResource(icon);
-
- return convertView;
- }
-
- @Override
- public int getCount() {
- int count = 0;
- for(int i = 0; i < visible.size(); i++) {
- if(visible.get(i)) {
- count++;
- }
- }
-
- return count;
- }
-
- public int getActualPosition(int position) {
- for(int i = 0; i <= position; i++) {
- if(!visible.get(i)) {
- position++;
- }
- }
-
- return position;
- }
- public int getAdapterPosition(int position) {
- if(!visible.get(position)) {
- visible.set(position, true);
- notifyDataSetChanged();
- }
-
- for(int i = position; i >= 0; i--) {
- if(!visible.get(i)) {
- position--;
- }
- }
-
- return position;
- }
-
- public void setItemVisible(int position, boolean visible) {
- if(this.visible.get(position) != visible) {
- this.visible.set(position, visible);
- notifyDataSetInvalidated();
- }
- }
- public void setDownloadVisible(boolean visible) {
- setItemVisible(items.size() - 2, visible);
- }
-
- public void setSelectedPosition(int position) {
- selectedPosition = position;
- }
-}
diff --git a/app/src/main/java/github/daneren2005/dsub/adapter/EntryAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/EntryAdapter.java
deleted file mode 100644
index 9e506e5a..00000000
--- a/app/src/main/java/github/daneren2005/dsub/adapter/EntryAdapter.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- 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 <http://www.gnu.org/licenses/>.
-
- Copyright 2010 (C) Sindre Mehus
- */
-package github.daneren2005.dsub.adapter;
-
-import android.content.Context;
-
-import java.util.List;
-
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
-import github.daneren2005.dsub.domain.MusicDirectory;
-import github.daneren2005.dsub.util.ImageLoader;
-import github.daneren2005.dsub.view.AlbumView;
-import github.daneren2005.dsub.view.ArtistEntryView;
-import github.daneren2005.dsub.view.SongView;
-
-/**
- * @author Sindre Mehus
- */
-public class EntryAdapter extends ArrayAdapter<MusicDirectory.Entry> {
- private final static String TAG = EntryAdapter.class.getSimpleName();
- private final Context activity;
- private final ImageLoader imageLoader;
- private final boolean checkable;
- private List<MusicDirectory.Entry> entries;
-
- public EntryAdapter(Context activity, ImageLoader imageLoader, List<MusicDirectory.Entry> entries, boolean checkable) {
- super(activity, android.R.layout.simple_list_item_1, entries);
- this.entries = entries;
- this.activity = activity;
- this.imageLoader = imageLoader;
- this.checkable = checkable;
- }
-
- public void removeAt(int position) {
- entries.remove(position);
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- MusicDirectory.Entry entry = getItem(position);
-
- if (entry.isDirectory()) {
- if(entry.isAlbum()) {
- AlbumView view;
- view = new AlbumView(activity);
- view.setObject(entry, imageLoader);
- return view;
- } else {
- ArtistEntryView view = new ArtistEntryView(activity);
- view.setObject(entry);
- return view;
- }
- } else {
- SongView view;
- if (convertView != null && convertView instanceof SongView) {
- view = (SongView) convertView;
- } else {
- view = new SongView(activity);
- }
- view.setObject(entry, checkable);
- return view;
- }
- }
-}
diff --git a/app/src/main/java/github/daneren2005/dsub/adapter/EntryGridAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/EntryGridAdapter.java
new file mode 100644
index 00000000..78ef13ed
--- /dev/null
+++ b/app/src/main/java/github/daneren2005/dsub/adapter/EntryGridAdapter.java
@@ -0,0 +1,145 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+ Copyright 2015 (C) Scott Jackson
+*/
+
+package github.daneren2005.dsub.adapter;
+
+import android.content.Context;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.domain.MusicDirectory;
+import github.daneren2005.dsub.domain.MusicDirectory.Entry;
+import github.daneren2005.dsub.util.ImageLoader;
+import github.daneren2005.dsub.util.Util;
+import github.daneren2005.dsub.view.AlbumView;
+import github.daneren2005.dsub.view.SongView;
+import github.daneren2005.dsub.view.UpdateView;
+import github.daneren2005.dsub.view.UpdateView.UpdateViewHolder;
+
+public class EntryGridAdapter extends SectionAdapter<Entry> {
+ private static String TAG = EntryGridAdapter.class.getSimpleName();
+
+ public static int VIEW_TYPE_ALBUM_CELL = 1;
+ public static int VIEW_TYPE_ALBUM_LINE = 2;
+ public static int VIEW_TYPE_SONG = 3;
+
+ private ImageLoader imageLoader;
+ private boolean largeAlbums;
+ private boolean showArtist = false;
+ private boolean removeFromPlaylist = false;
+ private View header;
+
+ public EntryGridAdapter(Context context, List<Entry> entries, ImageLoader imageLoader, boolean largeCell) {
+ super(context, entries);
+ this.imageLoader = imageLoader;
+ this.largeAlbums = largeCell;
+
+ // Always show artist if they aren't all the same
+ String artist = null;
+ for(MusicDirectory.Entry entry: entries) {
+ if(artist == null) {
+ artist = entry.getArtist();
+ }
+
+ if(artist != null && !artist.equals(entry.getArtist())) {
+ showArtist = true;
+ }
+ }
+ checkable = true;
+ }
+
+ @Override
+ public UpdateViewHolder onCreateSectionViewHolder(ViewGroup parent, int viewType) {
+ UpdateView updateView = null;
+ if(viewType == VIEW_TYPE_ALBUM_LINE || viewType == VIEW_TYPE_ALBUM_CELL) {
+ updateView = new AlbumView(context, viewType == VIEW_TYPE_ALBUM_CELL);
+ } else if(viewType == VIEW_TYPE_SONG) {
+ updateView = new SongView(context);
+ }
+
+ return new UpdateViewHolder(updateView);
+ }
+
+ @Override
+ public void onBindViewHolder(UpdateViewHolder holder, Entry entry, int viewType) {
+ UpdateView view = holder.getUpdateView();
+ if(viewType == VIEW_TYPE_ALBUM_CELL || viewType == VIEW_TYPE_ALBUM_LINE) {
+ AlbumView albumView = (AlbumView) view;
+ albumView.setShowArtist(showArtist);
+ albumView.setObject(entry, imageLoader);
+ } else if(viewType == VIEW_TYPE_SONG) {
+ SongView songView = (SongView) view;
+ songView.setObject(entry, checkable && !entry.isVideo());
+ }
+ }
+
+ public UpdateViewHolder onCreateHeaderHolder(ViewGroup parent) {
+ return new UpdateViewHolder(header, false);
+ }
+ public void onBindHeaderHolder(UpdateViewHolder holder, String header) {
+
+ }
+
+ @Override
+ public int getItemViewType(Entry entry) {
+ if(entry.isDirectory()) {
+ if (largeAlbums) {
+ return VIEW_TYPE_ALBUM_CELL;
+ } else {
+ return VIEW_TYPE_ALBUM_LINE;
+ }
+ } else {
+ return VIEW_TYPE_SONG;
+ }
+ }
+
+ public void setHeader(View header) {
+ this.header = header;
+ this.singleSectionHeader = true;
+ }
+
+ public void setShowArtist(boolean showArtist) {
+ this.showArtist = showArtist;
+ }
+
+ public void removeAt(int index) {
+ sections.get(0).remove(index);
+ notifyItemRemoved(index);
+ }
+
+ public void setRemoveFromPlaylist(boolean removeFromPlaylist) {
+ this.removeFromPlaylist = removeFromPlaylist;
+ }
+
+ @Override
+ public void onCreateActionModeMenu(Menu menu, MenuInflater menuInflater) {
+ if(Util.isOffline(context)) {
+ menuInflater.inflate(R.menu.multiselect_media_offline, menu);
+ } else {
+ menuInflater.inflate(R.menu.multiselect_media, menu);
+ }
+
+ if(!removeFromPlaylist) {
+ menu.removeItem(R.id.menu_remove_playlist);
+ }
+ }
+}
diff --git a/app/src/main/java/github/daneren2005/dsub/adapter/EntryInfiniteGridAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/EntryInfiniteGridAdapter.java
new file mode 100644
index 00000000..2c4f75dc
--- /dev/null
+++ b/app/src/main/java/github/daneren2005/dsub/adapter/EntryInfiniteGridAdapter.java
@@ -0,0 +1,146 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+ Copyright 2015 (C) Scott Jackson
+*/
+
+package github.daneren2005.dsub.adapter;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.util.List;
+
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.domain.MusicDirectory;
+import github.daneren2005.dsub.domain.MusicDirectory.Entry;
+import github.daneren2005.dsub.domain.ServerInfo;
+import github.daneren2005.dsub.service.MusicService;
+import github.daneren2005.dsub.service.MusicServiceFactory;
+import github.daneren2005.dsub.util.ImageLoader;
+import github.daneren2005.dsub.util.SilentBackgroundTask;
+import github.daneren2005.dsub.view.UpdateView;
+
+public class EntryInfiniteGridAdapter extends EntryGridAdapter {
+ public static int VIEW_TYPE_LOADING = 4;
+
+ private String type;
+ private String extra;
+ private int size;
+
+ private boolean loading = false;
+ private boolean allLoaded = false;
+
+ public EntryInfiniteGridAdapter(Context context, List<Entry> entries, ImageLoader imageLoader, boolean largeCell) {
+ super(context, entries, imageLoader, largeCell);
+ }
+
+ @Override
+ public UpdateView.UpdateViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ if(viewType == VIEW_TYPE_LOADING) {
+ View progress = LayoutInflater.from(context).inflate(R.layout.tab_progress, null);
+ progress.setVisibility(View.VISIBLE);
+ return new UpdateView.UpdateViewHolder(progress, false);
+ }
+
+ return super.onCreateViewHolder(parent, viewType);
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ if(isLoadingView(position)) {
+ return VIEW_TYPE_LOADING;
+ }
+
+ return super.getItemViewType(position);
+ }
+
+ @Override
+ public void onBindViewHolder(UpdateView.UpdateViewHolder holder, int position) {
+ if(!isLoadingView(position)) {
+ super.onBindViewHolder(holder, position);
+ }
+ }
+
+ @Override
+ public int getItemCount() {
+ int size = super.getItemCount();
+
+ if(!allLoaded) {
+ size++;
+ }
+
+ return size;
+ }
+
+ public void setData(String type, String extra, int size) {
+ this.type = type;
+ this.extra = extra;
+ this.size = size;
+ }
+
+ public void loadMore() {
+ if(loading || allLoaded) {
+ return;
+ }
+ loading = true;
+
+ new SilentBackgroundTask<Void>(context) {
+ private List<Entry> newData;
+
+ @Override
+ protected Void doInBackground() throws Throwable {
+ newData = cacheInBackground();
+ return null;
+ }
+
+ @Override
+ protected void done(Void result) {
+ appendCachedData(newData);
+ loading = false;
+
+ if(newData.isEmpty()) {
+ allLoaded = true;
+ notifyDataSetChanged();
+ }
+ }
+ }.execute();
+ }
+
+ protected List<Entry> cacheInBackground() throws Exception {
+ MusicService service = MusicServiceFactory.getMusicService(context);
+ MusicDirectory result;
+ int offset = sections.get(0).size();
+ if(("genres".equals(type) && ServerInfo.checkServerVersion(context, "1.10.0")) || "years".equals(type)) {
+ result = service.getAlbumList(type, extra, size, offset, false, context, null);
+ } else if("genres".equals(type) || "genres-songs".equals(type)) {
+ result = service.getSongsByGenre(extra, size, offset, context, null);
+ } else {
+ result = service.getAlbumList(type, size, offset, false, context, null);
+ }
+ return result.getChildren();
+ }
+
+ protected void appendCachedData(List<Entry> newData) {
+ if(newData.size() > 0) {
+ int start = sections.get(0).size();
+ sections.get(0).addAll(newData);
+ this.notifyItemRangeInserted(start, newData.size());
+ }
+ }
+
+ protected boolean isLoadingView(int position) {
+ return !allLoaded && position >= sections.get(0).size();
+ }
+}
diff --git a/app/src/main/java/github/daneren2005/dsub/adapter/GenreAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/GenreAdapter.java
index abb208c9..7e6954f9 100644
--- a/app/src/main/java/github/daneren2005/dsub/adapter/GenreAdapter.java
+++ b/app/src/main/java/github/daneren2005/dsub/adapter/GenreAdapter.java
@@ -1,60 +1,48 @@
/*
- 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 <http://www.gnu.org/licenses/>.
+ 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 <http://www.gnu.org/licenses/>.
+ Copyright 2015 (C) Scott Jackson
+*/
- Copyright 2010 (C) Sindre Mehus
- */
package github.daneren2005.dsub.adapter;
-import android.widget.ArrayAdapter;
-import android.widget.SectionIndexer;
import android.content.Context;
-import android.view.View;
import android.view.ViewGroup;
-import github.daneren2005.dsub.R;
import github.daneren2005.dsub.domain.Genre;
import github.daneren2005.dsub.view.GenreView;
+import github.daneren2005.dsub.view.UpdateView;
import java.util.List;
-import java.util.Set;
-import java.util.LinkedHashSet;
-import java.util.ArrayList;
-/**
- * @author Sindre Mehus
-*/
-public class GenreAdapter extends ArrayAdapter<Genre>{
- private Context activity;
- private List<Genre> genres;
-
- public GenreAdapter(Context context, List<Genre> genres) {
- super(context, android.R.layout.simple_list_item_1, genres);
- this.activity = context;
- this.genres = genres;
+public class GenreAdapter extends SectionAdapter<Genre>{
+ public static int VIEW_TYPE_GENRE = 1;
+
+ public GenreAdapter(Context context, List<Genre> genres, OnItemClickedListener listener) {
+ super(context, genres);
+ this.onItemClickedListener = listener;
}
-
+
@Override
- public View getView(int position, View convertView, ViewGroup parent) {
- Genre genre = genres.get(position);
- GenreView view;
- if (convertView != null && convertView instanceof GenreView) {
- view = (GenreView) convertView;
- } else {
- view = new GenreView(activity);
- }
- view.setObject(genre);
- return view;
- }
+ public UpdateView.UpdateViewHolder onCreateSectionViewHolder(ViewGroup parent, int viewType) {
+ return new UpdateView.UpdateViewHolder(new GenreView(context));
+ }
+
+ @Override
+ public void onBindViewHolder(UpdateView.UpdateViewHolder holder, Genre item, int viewType) {
+ holder.getUpdateView().setObject(item);
+ }
+
+ @Override
+ public int getItemViewType(Genre item) {
+ return VIEW_TYPE_GENRE;
+ }
}
diff --git a/app/src/main/java/github/daneren2005/dsub/adapter/MainAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/MainAdapter.java
new file mode 100644
index 00000000..8f1f1c38
--- /dev/null
+++ b/app/src/main/java/github/daneren2005/dsub/adapter/MainAdapter.java
@@ -0,0 +1,112 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+ Copyright 2015 (C) Scott Jackson
+*/
+
+package github.daneren2005.dsub.adapter;
+
+import android.content.Context;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+
+import java.util.List;
+
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.domain.ServerInfo;
+import github.daneren2005.dsub.util.Util;
+import github.daneren2005.dsub.view.AlbumListCountView;
+import github.daneren2005.dsub.view.BasicHeaderView;
+import github.daneren2005.dsub.view.BasicListView;
+import github.daneren2005.dsub.view.UpdateView;
+
+public class MainAdapter extends SectionAdapter<Integer> {
+ public static final int VIEW_TYPE_ALBUM_LIST = 1;
+ public static final int VIEW_TYPE_ALBUM_COUNT_LIST = 2;
+
+ public MainAdapter(Context context, List<String> headers, List<List<Integer>> sections, OnItemClickedListener onItemClickedListener) {
+ super(context, headers, sections);
+ this.onItemClickedListener = onItemClickedListener;
+ }
+
+ @Override
+ public UpdateView.UpdateViewHolder onCreateSectionViewHolder(ViewGroup parent, int viewType) {
+ UpdateView updateView;
+ if(viewType == VIEW_TYPE_ALBUM_LIST) {
+ updateView = new BasicListView(context);
+ } else {
+ updateView = new AlbumListCountView(context);
+ }
+
+ return new UpdateView.UpdateViewHolder(updateView);
+ }
+
+ @Override
+ public void onBindViewHolder(UpdateView.UpdateViewHolder holder, Integer item, int viewType) {
+ UpdateView updateView = holder.getUpdateView();
+
+ if(viewType == VIEW_TYPE_ALBUM_LIST) {
+ updateView.setObject(context.getResources().getString(item));
+ } else {
+ updateView.setObject(item);
+ }
+ }
+
+ @Override
+ public int getItemViewType(Integer item) {
+ if(item == R.string.main_albums_newest) {
+ return VIEW_TYPE_ALBUM_COUNT_LIST;
+ } else {
+ return VIEW_TYPE_ALBUM_LIST;
+ }
+ }
+
+ @Override
+ public UpdateView.UpdateViewHolder onCreateHeaderHolder(ViewGroup parent) {
+ return new UpdateView.UpdateViewHolder(new BasicHeaderView(context, R.layout.album_list_header));
+ }
+ @Override
+ public void onBindHeaderHolder(UpdateView.UpdateViewHolder holder, String header) {
+ UpdateView view = holder.getUpdateView();
+ CheckBox checkBox = (CheckBox) view.findViewById(R.id.item_checkbox);
+
+ String display;
+ if ("albums".equals(header)) {
+ display = context.getResources().getString(R.string.main_albums_title);
+
+ if(!Util.isOffline(context) && ServerInfo.canAlbumListPerFolder(context)) {
+ checkBox.setVisibility(View.VISIBLE);
+ checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ Util.setAlbumListsPerFolder(context, isChecked);
+ }
+ });
+ checkBox.setChecked(Util.getAlbumListsPerFolder(context));
+ } else {
+ checkBox.setVisibility(View.GONE);
+ }
+ } else if("videos".equals(header)) {
+ display = context.getResources().getString(R.string.main_videos);
+ checkBox.setVisibility(View.GONE);
+ } else {
+ display = header;
+ checkBox.setVisibility(View.GONE);
+ }
+
+ if(view != null) {
+ view.setObject(display);
+ }
+ }
+}
diff --git a/app/src/main/java/github/daneren2005/dsub/adapter/MergeAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/MergeAdapter.java
deleted file mode 100644
index a2db4cf0..00000000
--- a/app/src/main/java/github/daneren2005/dsub/adapter/MergeAdapter.java
+++ /dev/null
@@ -1,290 +0,0 @@
-/***
- Copyright (c) 2008-2009 CommonsWare, LLC
- Portions (c) 2009 Google, Inc.
-
- Licensed under the Apache License, Version 2.0 (the "License"); you may
- not use this file except in compliance with the License. You may obtain
- a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- */
-
-package github.daneren2005.dsub.adapter;
-
-import android.database.DataSetObserver;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.BaseAdapter;
-import android.widget.ListAdapter;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Arrays;
-
-/**
- * Adapter that merges multiple child adapters and views
- * into a single contiguous whole.
- * <p/>
- * Adapters used as pieces within MergeAdapter must
- * have view type IDs monotonically increasing from 0. Ideally,
- * adapters also have distinct ranges for their row ids, as
- * returned by getItemId().
- */
-public class MergeAdapter extends BaseAdapter {
-
- private final CascadeDataSetObserver observer = new CascadeDataSetObserver();
- private final ArrayList<ListAdapter> pieces = new ArrayList<ListAdapter>();
-
- /**
- * Stock constructor, simply chaining to the superclass.
- */
- public MergeAdapter() {
- super();
- }
-
- /**
- * Adds a new adapter to the roster of things to appear
- * in the aggregate list.
- *
- * @param adapter Source for row views for this section
- */
- public void addAdapter(ListAdapter adapter) {
- pieces.add(adapter);
- adapter.registerDataSetObserver(observer);
- }
-
- public void removeAdapter(ListAdapter adapter) {
- adapter.unregisterDataSetObserver(observer);
- pieces.remove(adapter);
- }
-
- /**
- * Adds a new View to the roster of things to appear
- * in the aggregate list.
- *
- * @param view Single view to add
- */
- public ListAdapter addView(View view) {
- return addView(view, false);
- }
-
- /**
- * Adds a new View to the roster of things to appear
- * in the aggregate list.
- *
- * @param view Single view to add
- * @param enabled false if views are disabled, true if enabled
- */
- public ListAdapter addView(View view, boolean enabled) {
- return addViews(Arrays.asList(view), enabled);
- }
-
- /**
- * Adds a list of views to the roster of things to appear
- * in the aggregate list.
- *
- * @param views List of views to add
- */
- public ListAdapter addViews(List<View> views) {
- return addViews(views, false);
- }
-
- /**
- * Adds a list of views to the roster of things to appear
- * in the aggregate list.
- *
- * @param views List of views to add
- * @param enabled false if views are disabled, true if enabled
- */
- public ListAdapter addViews(List<View> views, boolean enabled) {
- ListAdapter adapter = enabled ? new EnabledSackAdapter(views) : new SackOfViewsAdapter(views);
- addAdapter(adapter);
- return adapter;
- }
-
- /**
- * Get the data item associated with the specified
- * position in the data set.
- *
- * @param position Position of the item whose data we want
- */
- @Override
- public Object getItem(int position) {
- for (ListAdapter piece : pieces) {
- int size = piece.getCount();
-
- if (position < size) {
- return (piece.getItem(position));
- }
-
- position -= size;
- }
-
- return (null);
- }
-
- /**
- * How many items are in the data set represented by this
- * Adapter.
- */
- @Override
- public int getCount() {
- int total = 0;
-
- for (ListAdapter piece : pieces) {
- total += piece.getCount();
- }
-
- return (total);
- }
-
- /**
- * Returns the number of types of Views that will be
- * created by getView().
- */
- @Override
- public int getViewTypeCount() {
- int total = 0;
-
- for (ListAdapter piece : pieces) {
- total += piece.getViewTypeCount();
- }
-
- return (Math.max(total, 1)); // needed for setListAdapter() before content add'
- }
-
- /**
- * Get the type of View that will be created by getView()
- * for the specified item.
- *
- * @param position Position of the item whose data we want
- */
- @Override
- public int getItemViewType(int position) {
- int typeOffset = 0;
- int result = -1;
-
- for (ListAdapter piece : pieces) {
- int size = piece.getCount();
-
- if (position < size) {
- result = typeOffset + piece.getItemViewType(position);
- break;
- }
-
- position -= size;
- typeOffset += piece.getViewTypeCount();
- }
-
- return (result);
- }
-
- /**
- * Are all items in this ListAdapter enabled? If yes it
- * means all items are selectable and clickable.
- */
- @Override
- public boolean areAllItemsEnabled() {
- return (false);
- }
-
- /**
- * Returns true if the item at the specified position is
- * not a separator.
- *
- * @param position Position of the item whose data we want
- */
- @Override
- public boolean isEnabled(int position) {
- for (ListAdapter piece : pieces) {
- int size = piece.getCount();
-
- if (position < size) {
- return (piece.isEnabled(position));
- }
-
- position -= size;
- }
-
- return (false);
- }
-
- /**
- * Get a View that displays the data at the specified
- * position in the data set.
- *
- * @param position Position of the item whose data we want
- * @param convertView View to recycle, if not null
- * @param parent ViewGroup containing the returned View
- */
- @Override
- public View getView(int position, View convertView,
- ViewGroup parent) {
- for (ListAdapter piece : pieces) {
- int size = piece.getCount();
-
- if (position < size) {
-
- return (piece.getView(position, convertView, parent));
- }
-
- position -= size;
- }
-
- return (null);
- }
-
- /**
- * Get the row id associated with the specified position
- * in the list.
- *
- * @param position Position of the item whose data we want
- */
- @Override
- public long getItemId(int position) {
- for (ListAdapter piece : pieces) {
- int size = piece.getCount();
-
- if (position < size) {
- return (piece.getItemId(position));
- }
-
- position -= size;
- }
-
- return (-1);
- }
-
- private static class EnabledSackAdapter extends SackOfViewsAdapter {
- public EnabledSackAdapter(List<View> views) {
- super(views);
- }
-
- @Override
- public boolean areAllItemsEnabled() {
- return (true);
- }
-
- @Override
- public boolean isEnabled(int position) {
- return (true);
- }
- }
-
- private class CascadeDataSetObserver extends DataSetObserver {
- @Override
- public void onChanged() {
- notifyDataSetChanged();
- }
-
- @Override
- public void onInvalidated() {
- notifyDataSetInvalidated();
- }
- }
-}
-
diff --git a/app/src/main/java/github/daneren2005/dsub/adapter/PlaylistAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/PlaylistAdapter.java
index d56a6b97..4221677e 100644
--- a/app/src/main/java/github/daneren2005/dsub/adapter/PlaylistAdapter.java
+++ b/app/src/main/java/github/daneren2005/dsub/adapter/PlaylistAdapter.java
@@ -1,70 +1,61 @@
/*
- 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 <http://www.gnu.org/licenses/>.
-
- Copyright 2010 (C) Sindre Mehus
- */
+ 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 <http://www.gnu.org/licenses/>.
+ Copyright 2015 (C) Scott Jackson
+*/
package github.daneren2005.dsub.adapter;
import android.content.Context;
-import github.daneren2005.dsub.R;
+
import java.util.List;
-import android.view.View;
+
import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
import github.daneren2005.dsub.domain.Playlist;
+import github.daneren2005.dsub.util.ImageLoader;
import github.daneren2005.dsub.view.PlaylistView;
+import github.daneren2005.dsub.view.UpdateView;
-import java.util.Collections;
-import java.util.Comparator;
+public class PlaylistAdapter extends SectionAdapter<Playlist> {
+ public static int VIEW_TYPE_PLAYLIST = 1;
-/**
- * @author Sindre Mehus
- */
-public class PlaylistAdapter extends ArrayAdapter<Playlist> {
+ private ImageLoader imageLoader;
+ private boolean largeCell;
- private final Context activity;
-
- public PlaylistAdapter(Context activity, List<Playlist> Playlists) {
- super(activity, R.layout.basic_list_item, Playlists);
- this.activity = activity;
+ public PlaylistAdapter(Context context, List<Playlist> playlists, ImageLoader imageLoader, boolean largeCell, OnItemClickedListener listener) {
+ super(context, playlists);
+ this.imageLoader = imageLoader;
+ this.largeCell = largeCell;
+ this.onItemClickedListener = listener;
+ }
+ public PlaylistAdapter(Context context, List<String> headers, List<List<Playlist>> sections, ImageLoader imageLoader, boolean largeCell, OnItemClickedListener listener) {
+ super(context, headers, sections);
+ this.imageLoader = imageLoader;
+ this.largeCell = largeCell;
+ this.onItemClickedListener = listener;
}
@Override
- public View getView(int position, View convertView, ViewGroup parent) {
- Playlist entry = getItem(position);
- PlaylistView view;
- if (convertView != null && convertView instanceof PlaylistView) {
- view = (PlaylistView) convertView;
- } else {
- view = new PlaylistView(activity);
- }
- view.setObject(entry);
- return view;
+ public UpdateView.UpdateViewHolder onCreateSectionViewHolder(ViewGroup parent, int viewType) {
+ return new UpdateView.UpdateViewHolder(new PlaylistView(context, imageLoader, largeCell));
}
- public static class PlaylistComparator implements Comparator<Playlist> {
- @Override
- public int compare(Playlist playlist1, Playlist playlist2) {
- return playlist1.getName().compareToIgnoreCase(playlist2.getName());
- }
-
- public static List<Playlist> sort(List<Playlist> playlists) {
- Collections.sort(playlists, new PlaylistComparator());
- return playlists;
- }
+ @Override
+ public void onBindViewHolder(UpdateView.UpdateViewHolder holder, Playlist playlist, int viewType) {
+ holder.getUpdateView().setObject(playlist);
+ holder.setItem(playlist);
+ }
+ @Override
+ public int getItemViewType(Playlist playlist) {
+ return VIEW_TYPE_PLAYLIST;
}
}
diff --git a/app/src/main/java/github/daneren2005/dsub/adapter/PodcastChannelAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/PodcastChannelAdapter.java
index 8ee39a10..dc94178d 100644
--- a/app/src/main/java/github/daneren2005/dsub/adapter/PodcastChannelAdapter.java
+++ b/app/src/main/java/github/daneren2005/dsub/adapter/PodcastChannelAdapter.java
@@ -1,60 +1,47 @@
/*
- 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 <http://www.gnu.org/licenses/>.
-
- Copyright 2010 (C) Sindre Mehus
- */
+ 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 <http://www.gnu.org/licenses/>.
+ Copyright 2015 (C) Scott Jackson
+*/
package github.daneren2005.dsub.adapter;
-import android.widget.ArrayAdapter;
-import android.widget.SectionIndexer;
import android.content.Context;
-import android.view.View;
import android.view.ViewGroup;
-import github.daneren2005.dsub.R;
import github.daneren2005.dsub.domain.PodcastChannel;
import github.daneren2005.dsub.view.PodcastChannelView;
+import github.daneren2005.dsub.view.UpdateView;
import java.util.List;
-import java.util.Set;
-import java.util.LinkedHashSet;
-import java.util.ArrayList;
-/**
- * @author Sindre Mehus
-*/
-public class PodcastChannelAdapter extends ArrayAdapter<PodcastChannel>{
- private Context activity;
- private List<PodcastChannel> podcasts;
+public class PodcastChannelAdapter extends SectionAdapter<PodcastChannel>{
+ public static int VIEW_TYPE_PODCAST = 1;
- public PodcastChannelAdapter(Context context, List<PodcastChannel> podcasts) {
- super(context, android.R.layout.simple_list_item_1, podcasts);
- this.activity = context;
- this.podcasts = podcasts;
+ public PodcastChannelAdapter(Context context, List<PodcastChannel> podcasts, OnItemClickedListener listener) {
+ super(context, podcasts);
+ this.onItemClickedListener = listener;
}
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- PodcastChannel podcast = podcasts.get(position);
- PodcastChannelView view;
- if (convertView != null && convertView instanceof PodcastChannelView) {
- view = (PodcastChannelView) convertView;
- } else {
- view = new PodcastChannelView(activity);
- }
- view.setObject(podcast);
- return view;
+
+ @Override
+ public UpdateView.UpdateViewHolder onCreateSectionViewHolder(ViewGroup parent, int viewType) {
+ return new UpdateView.UpdateViewHolder(new PodcastChannelView(context));
+ }
+
+ @Override
+ public void onBindViewHolder(UpdateView.UpdateViewHolder holder, PodcastChannel item, int viewType) {
+ holder.getUpdateView().setObject(item);
+ }
+
+ @Override
+ public int getItemViewType(PodcastChannel item) {
+ return VIEW_TYPE_PODCAST;
}
}
diff --git a/app/src/main/java/github/daneren2005/dsub/adapter/SackOfViewsAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/SackOfViewsAdapter.java
deleted file mode 100644
index e4744cc5..00000000
--- a/app/src/main/java/github/daneren2005/dsub/adapter/SackOfViewsAdapter.java
+++ /dev/null
@@ -1,181 +0,0 @@
-/***
- Copyright (c) 2008-2009 CommonsWare, LLC
- Portions (c) 2009 Google, Inc.
-
- Licensed under the Apache License, Version 2.0 (the "License"); you may
- not use this file except in compliance with the License. You may obtain
- a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- */
-package github.daneren2005.dsub.adapter;
-
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.BaseAdapter;
-import android.widget.ListView;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Adapter that simply returns row views from a list.
- * <p/>
- * If you supply a size, you must implement newView(), to
- * create a required view. The adapter will then cache these
- * views.
- * <p/>
- * If you supply a list of views in the constructor, that
- * list will be used directly. If any elements in the list
- * are null, then newView() will be called just for those
- * slots.
- * <p/>
- * Subclasses may also wish to override areAllItemsEnabled()
- * (default: false) and isEnabled() (default: false), if some
- * of their rows should be selectable.
- * <p/>
- * It is assumed each view is unique, and therefore will not
- * get recycled.
- * <p/>
- * Note that this adapter is not designed for long lists. It
- * is more for screens that should behave like a list. This
- * is particularly useful if you combine this with other
- * adapters (e.g., SectionedAdapter) that might have an
- * arbitrary number of rows, so it all appears seamless.
- */
-public class SackOfViewsAdapter extends BaseAdapter {
- private List<View> views = null;
-
- /**
- * Constructor creating an empty list of views, but with
- * a specified count. Subclasses must override newView().
- */
- public SackOfViewsAdapter(int count) {
- super();
-
- views = new ArrayList<View>(count);
-
- for (int i = 0; i < count; i++) {
- views.add(null);
- }
- }
-
- /**
- * Constructor wrapping a supplied list of views.
- * Subclasses must override newView() if any of the elements
- * in the list are null.
- */
- public SackOfViewsAdapter(List<View> views) {
- for (View view : views) {
- view.setLayoutParams(new ListView.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
- }
- this.views = views;
- }
-
- /**
- * Get the data item associated with the specified
- * position in the data set.
- *
- * @param position Position of the item whose data we want
- */
- @Override
- public Object getItem(int position) {
- return (views.get(position));
- }
-
- /**
- * How many items are in the data set represented by this
- * Adapter.
- */
- @Override
- public int getCount() {
- return (views.size());
- }
-
- /**
- * Returns the number of types of Views that will be
- * created by getView().
- */
- @Override
- public int getViewTypeCount() {
- return (getCount());
- }
-
- /**
- * Get the type of View that will be created by getView()
- * for the specified item.
- *
- * @param position Position of the item whose data we want
- */
- @Override
- public int getItemViewType(int position) {
- return (position);
- }
-
- /**
- * Are all items in this ListAdapter enabled? If yes it
- * means all items are selectable and clickable.
- */
- @Override
- public boolean areAllItemsEnabled() {
- return (false);
- }
-
- /**
- * Returns true if the item at the specified position is
- * not a separator.
- *
- * @param position Position of the item whose data we want
- */
- @Override
- public boolean isEnabled(int position) {
- return (false);
- }
-
- /**
- * Get a View that displays the data at the specified
- * position in the data set.
- *
- * @param position Position of the item whose data we want
- * @param convertView View to recycle, if not null
- * @param parent ViewGroup containing the returned View
- */
- @Override
- public View getView(int position, View convertView,
- ViewGroup parent) {
- View result = views.get(position);
-
- if (result == null) {
- result = newView(position, parent);
- views.set(position, result);
- }
-
- return (result);
- }
-
- /**
- * Get the row id associated with the specified position
- * in the list.
- *
- * @param position Position of the item whose data we want
- */
- @Override
- public long getItemId(int position) {
- return (position);
- }
-
- /**
- * Create a new View to go into the list at the specified
- * position.
- *
- * @param position Position of the item whose data we want
- * @param parent ViewGroup containing the returned View
- */
- protected View newView(int position, ViewGroup parent) {
- throw new RuntimeException("You must override newView()!");
- }
-}
diff --git a/app/src/main/java/github/daneren2005/dsub/adapter/SearchAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/SearchAdapter.java
new file mode 100644
index 00000000..66f2db21
--- /dev/null
+++ b/app/src/main/java/github/daneren2005/dsub/adapter/SearchAdapter.java
@@ -0,0 +1,129 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+ Copyright 2015 (C) Scott Jackson
+*/
+
+package github.daneren2005.dsub.adapter;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.ViewGroup;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.domain.MusicDirectory.Entry;
+import github.daneren2005.dsub.domain.SearchResult;
+import github.daneren2005.dsub.util.ImageLoader;
+import github.daneren2005.dsub.util.Util;
+import github.daneren2005.dsub.view.AlbumView;
+import github.daneren2005.dsub.view.ArtistView;
+import github.daneren2005.dsub.view.SongView;
+import github.daneren2005.dsub.view.UpdateView;
+
+import static github.daneren2005.dsub.adapter.ArtistAdapter.VIEW_TYPE_ARTIST;
+import static github.daneren2005.dsub.adapter.EntryGridAdapter.VIEW_TYPE_ALBUM_CELL;
+import static github.daneren2005.dsub.adapter.EntryGridAdapter.VIEW_TYPE_ALBUM_LINE;
+import static github.daneren2005.dsub.adapter.EntryGridAdapter.VIEW_TYPE_SONG;
+
+public class SearchAdapter extends SectionAdapter<Serializable> {
+ private SearchResult searchResult;
+ private ImageLoader imageLoader;
+ private boolean largeAlbums;
+
+ public SearchAdapter(Context context, SearchResult searchResult, ImageLoader imageLoader, boolean largeAlbums, OnItemClickedListener listener) {
+ this.context = context;
+ this.searchResult = searchResult;
+ this.imageLoader = imageLoader;
+ this.largeAlbums = largeAlbums;
+
+ this.sections = new ArrayList<>();
+ this.headers = new ArrayList<>();
+ Resources res = context.getResources();
+ if(!searchResult.getArtists().isEmpty()) {
+ this.sections.add((List<Serializable>) (List<?>) searchResult.getArtists());
+ this.headers.add(res.getString(R.string.search_artists));
+ }
+ if(!searchResult.getAlbums().isEmpty()) {
+ this.sections.add((List<Serializable>) (List<?>) searchResult.getAlbums());
+ this.headers.add(res.getString(R.string.search_albums));
+ }
+ if(!searchResult.getSongs().isEmpty()) {
+ this.sections.add((List<Serializable>) (List<?>) searchResult.getSongs());
+ this.headers.add(res.getString(R.string.search_songs));
+ }
+ this.onItemClickedListener = listener;
+ checkable = true;
+ }
+
+ @Override
+ public UpdateView.UpdateViewHolder onCreateSectionViewHolder(ViewGroup parent, int viewType) {
+ UpdateView updateView = null;
+ if(viewType == VIEW_TYPE_ALBUM_CELL || viewType == VIEW_TYPE_ALBUM_LINE) {
+ updateView = new AlbumView(context, viewType == VIEW_TYPE_ALBUM_CELL);
+ } else if(viewType == VIEW_TYPE_SONG) {
+ updateView = new SongView(context);
+ } else if(viewType == VIEW_TYPE_ARTIST) {
+ updateView = new ArtistView(context);
+ }
+
+ return new UpdateView.UpdateViewHolder(updateView);
+ }
+
+ @Override
+ public void onBindViewHolder(UpdateView.UpdateViewHolder holder, Serializable item, int viewType) {
+ UpdateView view = holder.getUpdateView();
+ if(viewType == VIEW_TYPE_ALBUM_CELL || viewType == VIEW_TYPE_ALBUM_LINE) {
+ AlbumView albumView = (AlbumView) view;
+ albumView.setObject((Entry) item, imageLoader);
+ } else if(viewType == VIEW_TYPE_SONG) {
+ SongView songView = (SongView) view;
+ songView.setObject((Entry) item, true);
+ } else if(viewType == VIEW_TYPE_ARTIST) {
+ view.setObject(item);
+ }
+ }
+
+ @Override
+ public int getItemViewType(Serializable item) {
+ if(item instanceof Entry) {
+ Entry entry = (Entry) item;
+ if (entry.isDirectory()) {
+ if (largeAlbums) {
+ return VIEW_TYPE_ALBUM_CELL;
+ } else {
+ return VIEW_TYPE_ALBUM_LINE;
+ }
+ } else {
+ return VIEW_TYPE_SONG;
+ }
+ } else {
+ return VIEW_TYPE_ARTIST;
+ }
+ }
+
+ @Override
+ public void onCreateActionModeMenu(Menu menu, MenuInflater menuInflater) {
+ if(Util.isOffline(context)) {
+ menuInflater.inflate(R.menu.multiselect_media_offline, menu);
+ } else {
+ menuInflater.inflate(R.menu.multiselect_media, menu);
+ }
+
+ menu.removeItem(R.id.menu_remove_playlist);
+ }
+}
diff --git a/app/src/main/java/github/daneren2005/dsub/adapter/SectionAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/SectionAdapter.java
new file mode 100644
index 00000000..2dbdf1f1
--- /dev/null
+++ b/app/src/main/java/github/daneren2005/dsub/adapter/SectionAdapter.java
@@ -0,0 +1,463 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+ Copyright 2015 (C) Scott Jackson
+*/
+
+package github.daneren2005.dsub.adapter;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.os.Build;
+import android.support.v7.widget.PopupMenu;
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.Toolbar;
+import android.util.Log;
+import android.util.TypedValue;
+import android.view.ActionMode;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.view.WindowManager;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.activity.SubsonicFragmentActivity;
+import github.daneren2005.dsub.util.MenuUtil;
+import github.daneren2005.dsub.view.BasicHeaderView;
+import github.daneren2005.dsub.view.UpdateView;
+import github.daneren2005.dsub.view.UpdateView.UpdateViewHolder;
+
+public abstract class SectionAdapter<T> extends RecyclerView.Adapter<UpdateViewHolder<T>> {
+ private static String TAG = SectionAdapter.class.getSimpleName();
+ public static int VIEW_TYPE_HEADER = 0;
+
+ protected Context context;
+ protected List<String> headers;
+ protected List<List<T>> sections;
+ protected boolean singleSectionHeader;
+ protected OnItemClickedListener<T> onItemClickedListener;
+ protected List<T> selected = new ArrayList<>();
+ protected List<UpdateView> selectedViews = new ArrayList<>();
+ protected ActionMode currentActionMode;
+ protected boolean checkable = false;
+
+ protected SectionAdapter() {}
+ public SectionAdapter(Context context, List<T> section) {
+ this(context, section, false);
+ }
+ public SectionAdapter(Context context, List<T> section, boolean singleSectionHeader) {
+ this.context = context;
+ this.headers = Arrays.asList("Section");
+ this.sections = new ArrayList<>();
+ this.sections.add(section);
+ this.singleSectionHeader = singleSectionHeader;
+ }
+ public SectionAdapter(Context context, List<String> headers, List<List<T>> sections) {
+ this(context, headers, sections, true);
+ }
+ public SectionAdapter(Context context, List<String> headers, List<List<T>> sections, boolean singleSectionHeader){
+ this.context = context;
+ this.headers = headers;
+ this.sections = sections;
+ this.singleSectionHeader = singleSectionHeader;
+ }
+
+ public void replaceExistingData(List<T> section) {
+ this.sections = new ArrayList<>();
+ this.sections.add(section);
+ notifyDataSetChanged();
+ }
+ public void replaceExistingData(List<String> headers, List<List<T>> sections) {
+ this.headers = headers;
+ this.sections = sections;
+ notifyDataSetChanged();
+ }
+
+ @Override
+ public UpdateViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ if(viewType == VIEW_TYPE_HEADER) {
+ return onCreateHeaderHolder(parent);
+ } else {
+ final UpdateViewHolder<T> holder = onCreateSectionViewHolder(parent, viewType);
+ final UpdateView updateView = holder.getUpdateView();
+
+ if(updateView != null) {
+ updateView.getChildAt(0).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ T item = holder.getItem();
+ updateView.onClick();
+ if (currentActionMode != null) {
+ if(updateView.isCheckable()) {
+ if (selected.contains(item)) {
+ selected.remove(item);
+ selectedViews.remove(updateView);
+ setChecked(updateView, false);
+ } else {
+ selected.add(item);
+ selectedViews.add(updateView);
+ setChecked(updateView, true);
+ }
+
+ if (selected.isEmpty()) {
+ currentActionMode.finish();
+ } else {
+ currentActionMode.setTitle(context.getResources().getString(R.string.select_album_n_selected, selected.size()));
+ }
+ }
+ } else if (onItemClickedListener != null) {
+ onItemClickedListener.onItemClicked(item);
+ }
+ }
+ });
+
+ View moreButton = updateView.findViewById(R.id.more_button);
+ if(moreButton == null) {
+ moreButton = updateView.findViewById(R.id.item_more);
+ }
+ if (moreButton != null) {
+ moreButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ final T item = holder.getItem();
+ if(onItemClickedListener != null) {
+ PopupMenu popup = new PopupMenu(context, v);
+ onItemClickedListener.onCreateContextMenu(popup.getMenu(), popup.getMenuInflater(), updateView, item);
+
+ popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
+ @Override
+ public boolean onMenuItemClick(MenuItem menuItem) {
+ return onItemClickedListener.onContextItemSelected(menuItem, updateView, item);
+ }
+ });
+ popup.show();
+ }
+ }
+ });
+
+ if(checkable) {
+ updateView.getChildAt(0).setOnLongClickListener(new View.OnLongClickListener() {
+ @Override
+ public boolean onLongClick(View v) {
+ if(currentActionMode == null) {
+ startActionMode(holder);
+ } else {
+ updateView.getChildAt(0).performClick();
+ }
+ return true;
+ }
+ });
+ }
+ }
+ }
+
+ return holder;
+ }
+ }
+
+ @Override
+ public void onBindViewHolder(UpdateViewHolder holder, int position) {
+ UpdateView updateView = holder.getUpdateView();
+
+ if(sections.size() == 1 && !singleSectionHeader) {
+ T item = sections.get(0).get(position);
+ onBindViewHolder(holder, item, getItemViewType(position));
+ postBindView(updateView, item);
+ holder.setItem(item);
+ return;
+ }
+
+ int subPosition = 0;
+ int subHeader = 0;
+ for(List<T> section: sections) {
+ boolean validHeader = headers.get(subHeader) != null;
+ if(position == subPosition && validHeader) {
+ onBindHeaderHolder(holder, headers.get(subHeader));
+ return;
+ }
+
+ int headerOffset = validHeader ? 1 : 0;
+ if(position < (subPosition + section.size() + headerOffset)) {
+ T item = section.get(position - subPosition - headerOffset);
+ onBindViewHolder(holder, item, getItemViewType(item));
+
+ postBindView(updateView, item);
+ holder.setItem(item);
+ return;
+ }
+
+ subPosition += section.size();
+ if(validHeader) {
+ subPosition += 1;
+ }
+ subHeader++;
+ }
+ }
+
+ private void postBindView(UpdateView updateView, T item) {
+ if(updateView.isCheckable()) {
+ setChecked(updateView, selected.contains(item));
+ }
+
+ View moreButton = updateView.findViewById(R.id.more_button);
+ if(moreButton == null) {
+ moreButton = updateView.findViewById(R.id.item_more);
+ }
+ if(moreButton != null) {
+ PopupMenu popup = new PopupMenu(context, moreButton);
+ Menu menu = popup.getMenu();
+ onItemClickedListener.onCreateContextMenu(popup.getMenu(), popup.getMenuInflater(), updateView, item);
+ if(menu.size() == 0) {
+ moreButton.setVisibility(View.GONE);
+ } else {
+ moreButton.setVisibility(View.VISIBLE);
+ }
+ }
+ }
+
+ @Override
+ public int getItemCount() {
+ if(sections.size() == 1 && !singleSectionHeader) {
+ return sections.get(0).size();
+ }
+
+ int count = 0;
+ for(String header: headers) {
+ if(header != null) {
+ count++;
+ }
+ }
+ for(List<T> section: sections) {
+ count += section.size();
+ }
+
+ return count;
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ if(sections.size() == 1 && !singleSectionHeader) {
+ return getItemViewType(sections.get(0).get(position));
+ }
+
+ int subPosition = 0;
+ int subHeader = 0;
+ for(List<T> section: sections) {
+ boolean validHeader = headers.get(subHeader) != null;
+ if(position == subPosition && validHeader) {
+ return VIEW_TYPE_HEADER;
+ }
+
+ int headerOffset = validHeader ? 1 : 0;
+ if(position < (subPosition + section.size() + headerOffset)) {
+ return getItemViewType(section.get(position - subPosition - headerOffset));
+ }
+
+ subPosition += section.size();
+ if(validHeader) {
+ subPosition += 1;
+ }
+ subHeader++;
+ }
+
+ return -1;
+ }
+
+ public UpdateViewHolder onCreateHeaderHolder(ViewGroup parent) {
+ return new UpdateViewHolder(new BasicHeaderView(context));
+ }
+ public void onBindHeaderHolder(UpdateViewHolder holder, String header) {
+ UpdateView view = holder.getUpdateView();
+ if(view != null) {
+ view.setObject(header);
+ }
+ }
+
+ public T getItemForPosition(int position) {
+ if(sections.size() == 1 && !singleSectionHeader) {
+ return sections.get(0).get(position);
+ }
+
+ int subPosition = 0;
+ for(List<T> section: sections) {
+ if(position == subPosition) {
+ return null;
+ }
+
+ if(position <= (subPosition + section.size())) {
+ return section.get(position - subPosition - 1);
+ }
+
+ subPosition += section.size() + 1;
+ }
+
+ return null;
+ }
+ public int getItemPosition(T item) {
+ if(sections.size() == 1 && !singleSectionHeader) {
+ return sections.get(0).indexOf(item);
+ }
+
+ int subPosition = 0;
+ for(List<T> section: sections) {
+ subPosition += section.size() + 1;
+
+ int position = section.indexOf(item);
+ if(position != -1) {
+ return position + subPosition;
+ }
+ }
+
+ return -1;
+ }
+
+ public void setOnItemClickedListener(OnItemClickedListener<T> onItemClickedListener) {
+ this.onItemClickedListener = onItemClickedListener;
+ }
+
+ public void addSelected(T item) {
+ selected.add(item);
+ }
+ public List<T> getSelected() {
+ List<T> selected = new ArrayList<>();
+ selected.addAll(this.selected);
+ return selected;
+ }
+
+ public void clearSelected() {
+ // TODO: This needs to work with multiple sections
+ for(T item: selected) {
+ int index = sections.get(0).indexOf(item);
+
+ if(singleSectionHeader) {
+ index++;
+ }
+ }
+ selected.clear();
+
+ for(UpdateView updateView: selectedViews) {
+ updateView.setChecked(false);
+ }
+ }
+
+ public void removeItem(T item) {
+ int subPosition = 0;
+ for(List<T> section: sections) {
+ if(sections.size() > 1 || singleSectionHeader) {
+ subPosition++;
+ }
+
+ int index = section.indexOf(item);
+ if (index != -1) {
+ section.remove(item);
+ notifyItemRemoved(subPosition + index);
+ break;
+ }
+
+ subPosition += section.size();
+ }
+ }
+
+ public abstract UpdateView.UpdateViewHolder onCreateSectionViewHolder(ViewGroup parent, int viewType);
+ public abstract void onBindViewHolder(UpdateViewHolder holder, T item, int viewType);
+ public abstract int getItemViewType(T item);
+ public void setCheckable(boolean checkable) {
+ this.checkable = checkable;
+ }
+ public void setChecked(UpdateView updateView, boolean checked) {
+ updateView.setChecked(checked);
+ }
+ public void onCreateActionModeMenu(Menu menu, MenuInflater menuInflater) {}
+
+ private void startActionMode(final UpdateView.UpdateViewHolder<T> holder) {
+ final UpdateView<T> updateView = holder.getUpdateView();
+ if (context instanceof SubsonicFragmentActivity && currentActionMode == null) {
+ final SubsonicFragmentActivity fragmentActivity = (SubsonicFragmentActivity) context;
+ Toolbar toolbar = fragmentActivity.getActiveToolbar();
+
+ toolbar.startActionMode(new ActionMode.Callback() {
+ @Override
+ public boolean onCreateActionMode(ActionMode mode, Menu menu) {
+ currentActionMode = mode;
+ onCreateActionModeMenu(menu, mode.getMenuInflater());
+ MenuUtil.hideMenuItems(context, menu);
+
+ T item = holder.getItem();
+ selected.add(item);
+ selectedViews.add(updateView);
+ setChecked(updateView, true);
+
+ mode.setTitle(context.getResources().getString(R.string.select_album_n_selected, selected.size()));
+ if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ TypedValue typedValue = new TypedValue();
+ Resources.Theme theme = context.getTheme();
+ theme.resolveAttribute(R.attr.colorPrimaryDark, typedValue, true);
+ int colorPrimaryDark = typedValue.data;
+
+ Window window = ((SubsonicFragmentActivity) context).getWindow();
+ window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
+ window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
+ window.setStatusBarColor(colorPrimaryDark);
+ }
+ return true;
+ }
+
+ @Override
+ public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
+ return false;
+ }
+
+ @Override
+ public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+ if (fragmentActivity.onOptionsItemSelected(item)) {
+ currentActionMode.finish();
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public void onDestroyActionMode(ActionMode mode) {
+ currentActionMode = null;
+ selected.clear();
+ for(UpdateView<T> updateView: selectedViews) {
+ updateView.setChecked(false);
+ }
+ selectedViews.clear();
+
+ Window window = ((SubsonicFragmentActivity) context).getWindow();
+ window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
+ }
+ });
+ }
+ }
+ public void stopActionMode() {
+ if(currentActionMode != null) {
+ currentActionMode.finish();
+ }
+ }
+
+ public interface OnItemClickedListener<T> {
+ void onItemClicked(T item);
+ void onCreateContextMenu(Menu menu, MenuInflater menuInflater, UpdateView<T> updateView, T item);
+ boolean onContextItemSelected(MenuItem menuItem, UpdateView<T> updateView, T item);
+ }
+}
diff --git a/app/src/main/java/github/daneren2005/dsub/adapter/SettingsAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/SettingsAdapter.java
index 45c3ead1..d99b294b 100644
--- a/app/src/main/java/github/daneren2005/dsub/adapter/SettingsAdapter.java
+++ b/app/src/main/java/github/daneren2005/dsub/adapter/SettingsAdapter.java
@@ -16,44 +16,83 @@
package github.daneren2005.dsub.adapter;
import android.content.Context;
+import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
+import android.widget.ImageView;
+import android.widget.TextView;
import java.util.List;
import github.daneren2005.dsub.R;
import github.daneren2005.dsub.domain.User;
+import github.daneren2005.dsub.util.ImageLoader;
import github.daneren2005.dsub.view.SettingView;
+import github.daneren2005.dsub.view.UpdateView;
import static github.daneren2005.dsub.domain.User.Setting;
-public class SettingsAdapter extends ArrayAdapter<Setting> {
- private final Context context;
+public class SettingsAdapter extends SectionAdapter<Setting> {
+ public final int VIEW_TYPE_SETTING = 1;
+
+ private final User user;
private final boolean editable;
+ private final ImageLoader imageLoader;
- public SettingsAdapter(Context context, User user, boolean editable) {
- super(context, R.layout.basic_list_item, user.getSettings());
- this.context = context;
+ public SettingsAdapter(Context context, User user, ImageLoader imageLoader, boolean editable) {
+ super(context, user.getSettings(), imageLoader != null);
+ this.user = user;
+ this.imageLoader = imageLoader;
this.editable = editable;
+
+ List<Setting> settings = sections.get(0);
+ for(Setting setting: settings) {
+ if(setting.getValue()) {
+ addSelected(setting);
+ }
+ }
}
- public SettingsAdapter(Context context, List<Setting> settings, boolean editable) {
- super(context, R.layout.basic_list_item, settings);
- this.context = context;
- this.editable = editable;
+ public UpdateView.UpdateViewHolder onCreateHeaderHolder(ViewGroup parent) {
+ View header = LayoutInflater.from(context).inflate(R.layout.user_header, parent, false);
+ return new UpdateView.UpdateViewHolder(header, false);
}
+ public void onBindHeaderHolder(UpdateView.UpdateViewHolder holder, String description) {
+ View header = holder.getView();
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- Setting entry = getItem(position);
- SettingView view;
- if (convertView != null && convertView instanceof SettingView) {
- view = (SettingView) convertView;
+ 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 {
- view = new SettingView(context);
+ emailView.setVisibility(View.GONE);
+ }
+ }
+
+ @Override
+ public UpdateView.UpdateViewHolder onCreateSectionViewHolder(ViewGroup parent, int viewType) {
+ return new UpdateView.UpdateViewHolder(new SettingView(context));
+ }
+
+ @Override
+ public void onBindViewHolder(UpdateView.UpdateViewHolder holder, Setting item, int viewType) {
+ holder.getUpdateView().setObject(item, editable);
+ }
+
+ @Override
+ public int getItemViewType(Setting item) {
+ return VIEW_TYPE_SETTING;
+ }
+
+ @Override
+ public void setChecked(UpdateView updateView, boolean checked) {
+ if(updateView instanceof SettingView) {
+ ((SettingView) updateView).setChecked(checked);
}
- view.setObject(entry, editable);
- return view;
}
}
diff --git a/app/src/main/java/github/daneren2005/dsub/adapter/ShareAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/ShareAdapter.java
index 4121a85a..6db3d927 100644
--- a/app/src/main/java/github/daneren2005/dsub/adapter/ShareAdapter.java
+++ b/app/src/main/java/github/daneren2005/dsub/adapter/ShareAdapter.java
@@ -1,56 +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 <http://www.gnu.org/licenses/>.
+ 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 <http://www.gnu.org/licenses/>.
+ Copyright 2015 (C) Scott Jackson
+*/
- Copyright 2010 (C) Sindre Mehus
- */
package github.daneren2005.dsub.adapter;
import android.content.Context;
-import android.view.View;
import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
import java.util.List;
import github.daneren2005.dsub.domain.Share;
import github.daneren2005.dsub.view.ShareView;
+import github.daneren2005.dsub.view.UpdateView;
-/**
- * @author Sindre Mehus
-*/
-public class ShareAdapter extends ArrayAdapter<Share>{
- private Context activity;
- private List<Share> shares;
-
- public ShareAdapter(Context context, List<Share> shares) {
- super(context, android.R.layout.simple_list_item_1, shares);
- this.activity = context;
- this.shares = shares;
+public class ShareAdapter extends SectionAdapter<Share>{
+ public static int VIEW_TYPE_SHARE = 1;
+
+ public ShareAdapter(Context context, List<Share> shares, OnItemClickedListener listener) {
+ super(context, shares);
+ this.onItemClickedListener = listener;
}
-
+
@Override
- public View getView(int position, View convertView, ViewGroup parent) {
- Share share = shares.get(position);
- ShareView view;
- if (convertView != null && convertView instanceof ShareView) {
- view = (ShareView) convertView;
- } else {
- view = new ShareView(activity);
- }
- view.setObject(share);
- return view;
- }
+ public UpdateView.UpdateViewHolder onCreateSectionViewHolder(ViewGroup parent, int viewType) {
+ return new UpdateView.UpdateViewHolder(new ShareView(context));
+ }
+
+ @Override
+ public void onBindViewHolder(UpdateView.UpdateViewHolder holder, Share item, int viewType) {
+ holder.getUpdateView().setObject(item);
+ }
+
+ @Override
+ public int getItemViewType(Share item) {
+ return VIEW_TYPE_SHARE;
+ }
}
diff --git a/app/src/main/java/github/daneren2005/dsub/adapter/UserAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/UserAdapter.java
index f0f78d97..95809e48 100644
--- a/app/src/main/java/github/daneren2005/dsub/adapter/UserAdapter.java
+++ b/app/src/main/java/github/daneren2005/dsub/adapter/UserAdapter.java
@@ -25,28 +25,32 @@ import java.util.List;
import github.daneren2005.dsub.R;
import github.daneren2005.dsub.domain.User;
import github.daneren2005.dsub.util.ImageLoader;
+import github.daneren2005.dsub.view.UpdateView;
import github.daneren2005.dsub.view.UserView;
-public class UserAdapter extends ArrayAdapter<User> {
- private final Context activity;
+public class UserAdapter extends SectionAdapter<User> {
+ public static int VIEW_TYPE_USER = 1;
+
private final ImageLoader imageLoader;
- public UserAdapter(Context activity, List<User> users, ImageLoader imageLoader) {
- super(activity, R.layout.basic_list_item, users);
- this.activity = activity;
+ public UserAdapter(Context context, List<User> users, ImageLoader imageLoader, OnItemClickedListener listener) {
+ super(context, users);
this.imageLoader = imageLoader;
+ this.onItemClickedListener = listener;
+ }
+
+ @Override
+ public UpdateView.UpdateViewHolder onCreateSectionViewHolder(ViewGroup parent, int viewType) {
+ return new UpdateView.UpdateViewHolder(new UserView(context));
+ }
+
+ @Override
+ public void onBindViewHolder(UpdateView.UpdateViewHolder holder, User item, int viewType) {
+ holder.getUpdateView().setObject(item, imageLoader);
}
@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, imageLoader);
- return view;
+ public int getItemViewType(User item) {
+ return VIEW_TYPE_USER;
}
} \ No newline at end of file
diff --git a/app/src/main/java/github/daneren2005/dsub/domain/Playlist.java b/app/src/main/java/github/daneren2005/dsub/domain/Playlist.java
index 7cd820c0..99b85ce9 100644
--- a/app/src/main/java/github/daneren2005/dsub/domain/Playlist.java
+++ b/app/src/main/java/github/daneren2005/dsub/domain/Playlist.java
@@ -19,6 +19,9 @@
package github.daneren2005.dsub.domain;
import java.io.Serializable;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
/**
* @author Sindre Mehus
@@ -125,4 +128,16 @@ public class Playlist implements Serializable {
Playlist playlist = (Playlist) o;
return playlist.id.equals(this.id);
}
+
+ public static class PlaylistComparator implements Comparator<Playlist> {
+ @Override
+ public int compare(Playlist playlist1, Playlist playlist2) {
+ return playlist1.getName().compareToIgnoreCase(playlist2.getName());
+ }
+
+ public static List<Playlist> sort(List<Playlist> playlists) {
+ Collections.sort(playlists, new PlaylistComparator());
+ return playlists;
+ }
+ }
}
diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/AdminFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/AdminFragment.java
index 66ce5f15..69164036 100644
--- a/app/src/main/java/github/daneren2005/dsub/fragments/AdminFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/AdminFragment.java
@@ -17,6 +17,7 @@ package github.daneren2005.dsub.fragments;
import android.os.Bundle;
import android.view.ContextMenu;
+import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
@@ -28,6 +29,7 @@ import java.util.ArrayList;
import java.util.List;
import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.adapter.SectionAdapter;
import github.daneren2005.dsub.domain.User;
import github.daneren2005.dsub.service.MusicService;
import github.daneren2005.dsub.service.parser.SubsonicRESTException;
@@ -36,8 +38,9 @@ import github.daneren2005.dsub.util.ProgressListener;
import github.daneren2005.dsub.util.UserUtil;
import github.daneren2005.dsub.util.Util;
import github.daneren2005.dsub.adapter.UserAdapter;
+import github.daneren2005.dsub.view.UpdateView;
-public class AdminFragment extends SelectListFragment<User> {
+public class AdminFragment extends SelectRecyclerFragment<User> {
private static String TAG = AdminFragment.class.getSimpleName();
@Override
@@ -56,22 +59,16 @@ public class AdminFragment extends SelectListFragment<User> {
}
@Override
- public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) {
- super.onCreateContextMenu(menu, view, menuInfo);
-
- MenuInflater inflater = context.getMenuInflater();
+ public void onCreateContextMenu(Menu menu, MenuInflater menuInflater, UpdateView<User> updateView, User item) {
if(UserUtil.isCurrentAdmin()) {
- inflater.inflate(R.menu.admin_context, menu);
+ menuInflater.inflate(R.menu.admin_context, menu);
} else if(UserUtil.isCurrentRole(User.SETTINGS)) {
- inflater.inflate(R.menu.admin_context_user, menu);
+ menuInflater.inflate(R.menu.admin_context_user, menu);
}
}
@Override
- public boolean onContextItemSelected(MenuItem menuItem) {
- AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo();
- User user = objects.get(info.position);
-
+ public boolean onContextItemSelected(MenuItem menuItem, UpdateView<User> updateView, User user) {
switch(menuItem.getItemId()) {
case R.id.admin_change_email:
UserUtil.changeEmail(context, user);
@@ -97,8 +94,8 @@ public class AdminFragment extends SelectListFragment<User> {
}
@Override
- public ArrayAdapter getAdapter(List<User> objs) {
- return new UserAdapter(context, objs, getImageLoader());
+ public SectionAdapter getAdapter(List<User> objs) {
+ return new UserAdapter(context, objs, getImageLoader(), this);
}
@Override
@@ -134,9 +131,7 @@ public class AdminFragment extends SelectListFragment<User> {
}
@Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- User user = (User) parent.getItemAtPosition(position);
-
+ public void onItemClicked(User user) {
SubsonicFragment fragment = new UserFragment();
Bundle args = new Bundle();
args.putSerializable(Constants.INTENT_EXTRA_NAME_ID, user);
diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/DownloadFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/DownloadFragment.java
index 59229c3f..36653ff5 100644
--- a/app/src/main/java/github/daneren2005/dsub/fragments/DownloadFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/DownloadFragment.java
@@ -16,10 +16,17 @@
package github.daneren2005.dsub.fragments;
import android.content.DialogInterface;
+import android.os.Bundle;
import android.os.Handler;
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.helper.ItemTouchHelper;
import android.view.ContextMenu;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
+import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
@@ -30,6 +37,7 @@ import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.adapter.SectionAdapter;
import github.daneren2005.dsub.domain.MusicDirectory;
import github.daneren2005.dsub.service.DownloadFile;
import github.daneren2005.dsub.service.DownloadService;
@@ -38,13 +46,60 @@ import github.daneren2005.dsub.util.ProgressListener;
import github.daneren2005.dsub.util.SilentBackgroundTask;
import github.daneren2005.dsub.util.Util;
import github.daneren2005.dsub.adapter.DownloadFileAdapter;
+import github.daneren2005.dsub.view.SongView;
+import github.daneren2005.dsub.view.UpdateView;
-public class DownloadFragment extends SelectListFragment<DownloadFile> {
+public class DownloadFragment extends SelectRecyclerFragment<DownloadFile> {
private long currentRevision;
private ScheduledExecutorService executorService;
public DownloadFragment() {
serialize = false;
+ pullToRefresh = false;
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
+ super.onCreateView(inflater, container, bundle);
+
+ ItemTouchHelper touchHelper = new ItemTouchHelper(new ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP | ItemTouchHelper.DOWN, ItemTouchHelper.LEFT) {
+ @Override
+ public boolean onMove(RecyclerView recyclerView, final RecyclerView.ViewHolder fromHolder, final RecyclerView.ViewHolder toHolder) {
+ new SilentBackgroundTask<Void>(context) {
+ private int from;
+ private int to;
+
+ @Override
+ protected Void doInBackground() throws Throwable {
+ from = fromHolder.getAdapterPosition();
+ to = toHolder.getAdapterPosition();
+ getDownloadService().swap(false, from, to);
+ return null;
+ }
+
+ @Override
+ protected void done(Void result) {
+ adapter.notifyItemMoved(from, to);
+ }
+ }.execute();
+
+ return true;
+ }
+
+ @Override
+ public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
+ SongView songView = (SongView) ((UpdateView.UpdateViewHolder) viewHolder).getUpdateView();
+ DownloadFile downloadFile = songView.getDownloadFile();
+
+ DownloadService downloadService = getDownloadService();
+ downloadService.removeBackground(downloadFile);
+ adapter.removeItem(downloadFile);
+ currentRevision = downloadService.getDownloadListUpdateRevision();
+ }
+ });
+ touchHelper.attachToRecyclerView(recyclerView);
+
+ return rootView;
}
@Override
@@ -80,7 +135,7 @@ public class DownloadFragment extends SelectListFragment<DownloadFile> {
}
@Override
- public ArrayAdapter getAdapter(List<DownloadFile> objs) {
+ public SectionAdapter getAdapter(List<DownloadFile> objs) {
return new DownloadFileAdapter(context, objs);
}
@@ -91,9 +146,6 @@ public class DownloadFragment extends SelectListFragment<DownloadFile> {
return new ArrayList<DownloadFile>();
}
- listView.setOnScrollListener(null);
- refreshLayout.setEnabled(false);
-
List<DownloadFile> songList = new ArrayList<DownloadFile>();
songList.addAll(downloadService.getBackgroundDownloads());
currentRevision = downloadService.getDownloadListUpdateRevision();
@@ -106,8 +158,25 @@ public class DownloadFragment extends SelectListFragment<DownloadFile> {
}
@Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ public void onItemClicked(DownloadFile item) {
+
+ }
+
+ @Override
+ public void onCreateContextMenu(Menu menu, MenuInflater menuInflater, UpdateView<DownloadFile> updateView, DownloadFile downloadFile) {
+ MusicDirectory.Entry selectedItem = downloadFile.getSong();
+ onCreateContextMenuSupport(menu, menuInflater, updateView, selectedItem);
+ if(!selectedItem.isVideo() && !Util.isOffline(context)) {
+ menu.removeItem(R.id.song_menu_remove_playlist);
+ }
+
+ recreateContextMenu(menu);
+ }
+ @Override
+ public boolean onContextItemSelected(MenuItem menuItem, UpdateView<DownloadFile> updateView, DownloadFile downloadFile) {
+ MusicDirectory.Entry selectedItem = downloadFile.getSong();
+ return onContextItemSelected(menuItem, selectedItem);
}
@Override
@@ -141,36 +210,6 @@ public class DownloadFragment extends SelectListFragment<DownloadFile> {
return false;
}
- @Override
- public void onCreateContextMenu(android.view.ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) {
- super.onCreateContextMenu(menu, view, menuInfo);
-
- AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
- Object selectedItem = ((DownloadFile) listView.getItemAtPosition(info.position)).getSong();
- onCreateContextMenu(menu, view, menuInfo, selectedItem);
- if(selectedItem instanceof MusicDirectory.Entry && !((MusicDirectory.Entry) selectedItem).isVideo() && !Util.isOffline(context)) {
- menu.removeItem(R.id.song_menu_remove_playlist);
- }
-
- recreateContextMenu(menu);
- }
-
- @Override
- public boolean onContextItemSelected(MenuItem menuItem) {
- if(menuItem.getGroupId() != getSupportTag()) {
- return false;
- }
-
- AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo();
- Object selectedItem = ((DownloadFile) listView.getItemAtPosition(info.position)).getSong();
-
- if(onContextItemSelected(menuItem, selectedItem)) {
- return true;
- }
-
- return true;
- }
-
private void update() {
DownloadService downloadService = getDownloadService();
if (downloadService == null || objects == null || adapter == null) {
diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/EqualizerFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/EqualizerFragment.java
index ea61f2c7..e2535e04 100644
--- a/app/src/main/java/github/daneren2005/dsub/fragments/EqualizerFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/EqualizerFragment.java
@@ -104,6 +104,7 @@ public class EqualizerFragment extends SubsonicFragment {
});
setTitle(R.string.equalizer_label);
+ setSubtitle(null);
return rootView;
}
diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/LyricsFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/LyricsFragment.java
index 826029f5..402bd257 100644
--- a/app/src/main/java/github/daneren2005/dsub/fragments/LyricsFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/LyricsFragment.java
@@ -62,6 +62,7 @@ public final class LyricsFragment extends SubsonicFragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
setTitle(R.string.download_menu_lyrics);
+ setSubtitle(null);
rootView = inflater.inflate(R.layout.lyrics, container, false);
artistView = (TextView) rootView.findViewById(R.id.lyrics_artist);
titleView = (TextView) rootView.findViewById(R.id.lyrics_title);
diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/MainFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/MainFragment.java
index ae38534a..d25315ac 100644
--- a/app/src/main/java/github/daneren2005/dsub/fragments/MainFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/MainFragment.java
@@ -1,6 +1,7 @@
package github.daneren2005.dsub.fragments;
-import android.app.AlertDialog;
+import android.content.res.Resources;
+import android.support.v7.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
@@ -9,69 +10,43 @@ import android.os.Build;
import android.os.Bundle;
import android.os.StatFs;
import android.util.Log;
-import android.view.ContextMenu;
-import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AdapterView;
import android.widget.CheckBox;
-import android.widget.CompoundButton;
-import android.widget.ListView;
-import android.widget.TextView;
+
import github.daneren2005.dsub.R;
-import github.daneren2005.dsub.domain.MusicDirectory;
+import github.daneren2005.dsub.adapter.MainAdapter;
+import github.daneren2005.dsub.adapter.SectionAdapter;
import github.daneren2005.dsub.domain.ServerInfo;
import github.daneren2005.dsub.service.DownloadService;
import github.daneren2005.dsub.util.Constants;
import github.daneren2005.dsub.util.FileUtil;
import github.daneren2005.dsub.util.LoadingTask;
import github.daneren2005.dsub.util.Pair;
+import github.daneren2005.dsub.util.ProgressListener;
import github.daneren2005.dsub.util.UserUtil;
-import github.daneren2005.dsub.adapter.MergeAdapter;
import github.daneren2005.dsub.util.Util;
import github.daneren2005.dsub.service.MusicService;
import github.daneren2005.dsub.service.MusicServiceFactory;
import github.daneren2005.dsub.util.SilentBackgroundTask;
import github.daneren2005.dsub.view.ChangeLog;
+import github.daneren2005.dsub.view.UpdateView;
+
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
-public class MainFragment extends SubsonicFragment {
+public class MainFragment extends SelectRecyclerFragment<Integer> {
private static final String TAG = MainFragment.class.getSimpleName();
- private LayoutInflater inflater;
- private TextView countView;
-
- private static final int MENU_GROUP_SERVER = 10;
- private static final int MENU_ITEM_SERVER_BASE = 100;
-
- @Override
- public void onCreate(Bundle bundle) {
- super.onCreate(bundle);
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
- this.inflater = inflater;
- rootView = inflater.inflate(R.layout.home, container, false);
-
- createLayout();
-
- return rootView;
- }
-
- @Override
- public void onResume() {
- super.onResume();
- }
- @Override
- public void onDestroy() {
- super.onDestroy();
+ public MainFragment() {
+ super();
+ pullToRefresh = false;
+ serialize = false;
+ backgroundUpdate = false;
}
@Override
@@ -116,159 +91,60 @@ public class MainFragment extends SubsonicFragment {
}
@Override
- public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) {
- super.onCreateContextMenu(menu, view, menuInfo);
-
- int serverCount = Util.getServerCount(context);
- int activeServer = Util.getActiveServer(context);
- for(int i = 1; i <= serverCount; i++) {
- android.view.MenuItem menuItem = menu.add(MENU_GROUP_SERVER, MENU_ITEM_SERVER_BASE + i, MENU_ITEM_SERVER_BASE + i, Util.getServerName(context, i));
- if(i == activeServer) {
- menuItem.setChecked(true);
- }
- }
- menu.setGroupCheckable(MENU_GROUP_SERVER, true, true);
- menu.setHeaderTitle(R.string.main_select_server);
-
- recreateContextMenu(menu);
+ public int getOptionsMenu() {
+ return 0;
}
@Override
- public boolean onContextItemSelected(android.view.MenuItem menuItem) {
- if(menuItem.getGroupId() != getSupportTag()) {
- return false;
- }
-
- int activeServer = menuItem.getItemId() - MENU_ITEM_SERVER_BASE;
- setActiveServer(activeServer);
- return true;
- }
+ public SectionAdapter getAdapter(List objs) {
+ List<List<Integer>> sections = new ArrayList<>();
+ List<String> headers = new ArrayList<>();
- @Override
- protected void refresh(boolean refresh) {
- createLayout();
- }
-
- private void createLayout() {
- View buttons = inflater.inflate(R.layout.main_buttons, null);
-
- final View serverButton = buttons.findViewById(R.id.main_select_server);
- final TextView serverTextView = (TextView) serverButton.findViewById(R.id.main_select_server_2);
- final TextView offlineButton = (TextView) buttons.findViewById(R.id.main_offline);
- offlineButton.setText(Util.isOffline(context) ? R.string.main_online : R.string.main_offline);
-
- final View albumsTitle = buttons.findViewById(R.id.main_albums);
- final View videoTitle = buttons.findViewById(R.id.main_video_section);
- final View albumsNewestButton = buttons.findViewById(R.id.main_albums_newest);
- countView = (TextView) buttons.findViewById(R.id.main_albums_recent_count);
- final View albumsRandomButton = buttons.findViewById(R.id.main_albums_random);
- final View albumsHighestButton = buttons.findViewById(R.id.main_albums_highest);
- final View albumsRecentButton = buttons.findViewById(R.id.main_albums_recent);
- final View albumsFrequentButton = buttons.findViewById(R.id.main_albums_frequent);
- final View albumsStarredButton = buttons.findViewById(R.id.main_albums_starred);
- final View albumsGenresButton = buttons.findViewById(R.id.main_albums_genres);
- final View albumsYearButton = buttons.findViewById(R.id.main_albums_year);
- final View albumsAlphabeticalButton = buttons.findViewById(R.id.main_albums_alphabetical);
- final View videosButton = buttons.findViewById(R.id.main_videos);
-
- final View dummyView = rootView.findViewById(R.id.main_dummy);
-
- final CheckBox albumsPerFolderCheckbox = (CheckBox) buttons.findViewById(R.id.main_albums_per_folder);
- if(!Util.isOffline(context) && ServerInfo.canAlbumListPerFolder(context)) {
- albumsPerFolderCheckbox.setChecked(Util.getAlbumListsPerFolder(context));
- albumsPerFolderCheckbox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
- @Override
- public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
- Util.setAlbumListsPerFolder(context, isChecked);
- }
- });
- } else {
- albumsPerFolderCheckbox.setVisibility(View.GONE);
- }
-
- int instance = Util.getActiveServer(context);
- String name = Util.getServerName(context, instance);
- serverTextView.setText(name);
-
- ListView list = (ListView) rootView.findViewById(R.id.main_list);
-
- MergeAdapter adapter = new MergeAdapter();
if (!Util.isOffline(context)) {
- adapter.addViews(Arrays.asList(serverButton), true);
- }
- adapter.addView(offlineButton, true);
- if (!Util.isOffline(context)) {
- adapter.addView(albumsTitle, false);
- adapter.addViews(Arrays.asList(albumsNewestButton, albumsRandomButton), true);
+ List<Integer> offline = Arrays.asList(R.string.main_offline);
+ sections.add(offline);
+ headers.add(null);
+
+ List<Integer> albums = new ArrayList<>();
+ albums.add(R.string.main_albums_newest);
+ albums.add(R.string.main_albums_random);
if(ServerInfo.checkServerVersion(context, "1.8")) {
- adapter.addView(albumsAlphabeticalButton, true);
+ albums.add(R.string.main_albums_alphabetical);
}
if(!Util.isTagBrowsing(context)) {
- adapter.addView(albumsHighestButton, true);
+ albums.add(R.string.main_albums_highest);
}
- adapter.addViews(Arrays.asList(albumsStarredButton, albumsGenresButton, albumsYearButton, albumsRecentButton, albumsFrequentButton), true);
+ albums.add(R.string.main_albums_starred);
+ albums.add(R.string.main_albums_genres);
+ albums.add(R.string.main_albums_year);
+ albums.add(R.string.main_albums_recent);
+ albums.add(R.string.main_albums_highest);
+
+ sections.add(albums);
+ headers.add("albums");
+
if(ServerInfo.checkServerVersion(context, "1.8") && !Util.isTagBrowsing(context)) {
- adapter.addView(videoTitle, false);
- adapter.addView(videosButton, true);
+ List<Integer> videos = Arrays.asList(R.string.main_videos);
+ sections.add(videos);
+ headers.add("videos");
}
+ } else {
+ List<Integer> online = Arrays.asList(R.string.main_online);
+ sections.add(online);
+ headers.add(null);
}
- list.setAdapter(adapter);
- registerForContextMenu(dummyView);
- list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- if (view == serverButton) {
- dummyView.showContextMenu();
- } else if (view == offlineButton) {
- toggleOffline();
- } else if (view == albumsNewestButton) {
- showAlbumList("newest");
- } else if (view == albumsRandomButton) {
- showAlbumList("random");
- } else if (view == albumsHighestButton) {
- showAlbumList("highest");
- } else if (view == albumsRecentButton) {
- showAlbumList("recent");
- } else if (view == albumsFrequentButton) {
- showAlbumList("frequent");
- } else if (view == albumsStarredButton) {
- showAlbumList("starred");
- } else if(view == albumsGenresButton) {
- showAlbumList("genres");
- } else if(view == albumsYearButton) {
- showAlbumList("years");
- } else if(view == albumsAlphabeticalButton) {
- showAlbumList("alphabeticalByName");
- } else if(view == videosButton) {
- showVideos();
- }
- }
- });
- setTitle(R.string.common_appname);
-
- if(!Util.isOffline(context)) {
- getMostRecentCount();
- }
+ return new MainAdapter(context, headers, sections, this);
}
- private void setActiveServer(int instance) {
- if (Util.getActiveServer(context) != instance) {
- final DownloadService service = getDownloadService();
- if (service != null) {
- new SilentBackgroundTask<Void>(context) {
- @Override
- protected Void doInBackground() throws Throwable {
- service.clearIncomplete();
- return null;
- }
- }.execute();
+ @Override
+ public List<Integer> getObjects(MusicService musicService, boolean refresh, ProgressListener listener) throws Exception {
+ return Arrays.asList(0);
+ }
- }
- Util.setActiveServer(context, instance);
- context.invalidate();
- UserUtil.refreshCurrentUser(context, false, true);
- }
+ @Override
+ public int getTitleResource() {
+ return R.string.common_appname;
}
private void toggleOffline() {
@@ -290,6 +166,7 @@ public class MainFragment extends SubsonicFragment {
}
UserUtil.seedCurrentUser(context);
+ context.updateDrawerHeader();
}
private void showAlbumList(String type) {
@@ -305,9 +182,6 @@ public class MainFragment extends SubsonicFragment {
SharedPreferences.Editor editor = Util.getPreferences(context).edit();
editor.putInt(Constants.PREFERENCES_KEY_RECENT_COUNT + Util.getActiveServer(context), 0);
editor.commit();
-
- // Clear immediately so doesn't still show when pressing back
- setMostRecentCount(0);
}
SubsonicFragment fragment = new SelectDirectoryFragment();
@@ -404,32 +278,54 @@ public class MainFragment extends SubsonicFragment {
}
private void showAboutDialog() {
- new LoadingTask<String>(context) {
+ new LoadingTask<Void>(context) {
+ Long[] used;
+ long bytesTotalFs;
+ long bytesAvailableFs;
+
@Override
- protected String doInBackground() throws Throwable {
+ protected Void doInBackground() throws Throwable {
File rootFolder = FileUtil.getMusicDirectory(context);
StatFs stat = new StatFs(rootFolder.getPath());
- long bytesTotalFs = (long) stat.getBlockCount() * (long) stat.getBlockSize();
- long bytesAvailableFs = (long) stat.getAvailableBlocks() * (long) stat.getBlockSize();
-
- Pair<Long, Long> used = FileUtil.getUsedSize(context, rootFolder);
-
- return getResources().getString(R.string.main_about_text,
- context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionName,
- used.getFirst(),
- Util.formatLocalizedBytes(used.getSecond(), context),
- Util.formatLocalizedBytes(Util.getCacheSizeMB(context) * 1024L * 1024L, context),
- Util.formatLocalizedBytes(bytesAvailableFs, context),
- Util.formatLocalizedBytes(bytesTotalFs, context));
+ bytesTotalFs = (long) stat.getBlockCount() * (long) stat.getBlockSize();
+ bytesAvailableFs = (long) stat.getAvailableBlocks() * (long) stat.getBlockSize();
+
+ used = FileUtil.getUsedSize(context, rootFolder);
+ return null;
}
@Override
- protected void done(String msg) {
+ protected void done(Void result) {
+ List<Integer> headers = new ArrayList<>();
+ List<String> details = new ArrayList<>();
+
+ headers.add(R.string.details_author);
+ details.add("Scott Jackson");
+
+ headers.add(R.string.details_email);
+ details.add("dsub.android@gmail.com");
+
try {
- Util.info(context, R.string.main_about_title, msg);
+ headers.add(R.string.details_version);
+ details.add(context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionName);
} catch(Exception e) {
- Util.toast(context, "Failed to open dialog");
+ details.add("");
}
+
+ Resources res = context.getResources();
+ headers.add(R.string.details_files_cached);
+ details.add(Long.toString(used[0]));
+
+ headers.add(R.string.details_files_permanent);
+ details.add(Long.toString(used[1]));
+
+ headers.add(R.string.details_used_space);
+ details.add(res.getString(R.string.details_of, Util.formatLocalizedBytes(used[2], context), Util.formatLocalizedBytes(Util.getCacheSizeMB(context) * 1024L * 1024L, context)));
+
+ headers.add(R.string.details_available_space);
+ details.add(res.getString(R.string.details_of, Util.formatLocalizedBytes(bytesAvailableFs, context), Util.formatLocalizedBytes(bytesTotalFs, context)));
+
+ Util.showDetailsDialog(context, R.string.main_about_title, headers, details);
}
}.execute();
}
@@ -506,81 +402,39 @@ public class MainFragment extends SubsonicFragment {
}.execute();
} catch(Exception e) {}
}
-
- private void getMostRecentCount() {
- // Use stashed value until after refresh occurs
- SharedPreferences prefs = Util.getPreferences(context);
- final int startCount = prefs.getInt(Constants.PREFERENCES_KEY_RECENT_COUNT + Util.getActiveServer(context), 0);
- setMostRecentCount(startCount);
-
- new SilentBackgroundTask<Integer>(context) {
- @Override
- public Integer doInBackground() throws Exception {
- String recentAddedFile = Util.getCacheName(context, "recent_count");
- ArrayList<String> recents = FileUtil.deserialize(context, recentAddedFile, ArrayList.class);
- if(recents == null) {
- recents = new ArrayList<String>();
- }
-
- MusicService musicService = MusicServiceFactory.getMusicService(context);
- MusicDirectory recentlyAdded = musicService.getAlbumList("newest", 20, 0, context, null);
-
- // If first run, just put everything in it and return 0
- boolean firstRun = recents.isEmpty();
-
- // Count how many new albums are in the list
- int count = 0;
- for(MusicDirectory.Entry album: recentlyAdded.getChildren()) {
- if(!recents.contains(album.getId())) {
- recents.add(album.getId());
- count++;
- }
- }
-
- // Keep recents list from growing infinitely
- while(recents.size() > 40) {
- recents.remove(0);
- }
- FileUtil.serialize(context, recents, recentAddedFile);
-
- if(firstRun) {
- return 0;
- } else {
- // Add the old count which will get cleared out after viewing recents
- count += startCount;
- SharedPreferences.Editor editor = Util.getPreferences(context).edit();
- editor.putInt(Constants.PREFERENCES_KEY_RECENT_COUNT + Util.getActiveServer(context), count);
- editor.commit();
-
- return count;
- }
- }
-
- @Override
- public void done(Integer result) {
- setMostRecentCount(result);
- }
-
- @Override
- public void error(Throwable x) {
- Log.w(TAG, "Failed to refresh most recent count", x);
- }
- }.execute();
- }
-
- private void setMostRecentCount(int count) {
- if(count <= 0) {
- countView.setVisibility(View.GONE);
- } else {
- String displayValue;
- if(count < 10) {
- displayValue = "0" + count;
- } else {
- displayValue = "" + count;
- }
-
- countView.setText(displayValue);
- countView.setVisibility(View.VISIBLE);
+
+ @Override
+ public void onItemClicked(Integer item) {
+ if(item == R.string.main_offline || item == R.string.main_online) {
+ toggleOffline();
+ } else if (item == R.string.main_albums_newest) {
+ showAlbumList("newest");
+ } else if (item == R.string.main_albums_random) {
+ showAlbumList("random");
+ } else if (item == R.string.main_albums_highest) {
+ showAlbumList("highest");
+ } else if (item == R.string.main_albums_recent) {
+ showAlbumList("recent");
+ } else if (item == R.string.main_albums_frequent) {
+ showAlbumList("frequent");
+ } else if (item == R.string.main_albums_starred) {
+ showAlbumList("starred");
+ } else if(item == R.string.main_albums_genres) {
+ showAlbumList("genres");
+ } else if(item == R.string.main_albums_year) {
+ showAlbumList("years");
+ } else if(item == R.string.main_albums_alphabetical) {
+ showAlbumList("alphabeticalByName");
+ } else if(item == R.string.main_videos) {
+ showVideos();
}
}
+
+ @Override
+ public void onCreateContextMenu(Menu menu, MenuInflater menuInflater, UpdateView<Integer> updateView, Integer item) {}
+
+ @Override
+ public boolean onContextItemSelected(MenuItem menuItem, UpdateView<Integer> updateView, Integer item) {
+ return false;
+ }
}
diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/NowPlayingFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/NowPlayingFragment.java
index 3e30af50..00b780a5 100644
--- a/app/src/main/java/github/daneren2005/dsub/fragments/NowPlayingFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/NowPlayingFragment.java
@@ -20,7 +20,8 @@ import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
-import android.app.AlertDialog;
+import android.annotation.TargetApi;
+import android.support.v7.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
@@ -30,6 +31,9 @@ import android.os.Bundle;
import android.os.Handler;
import android.support.v4.view.MenuItemCompat;
import android.support.v7.app.MediaRouteButton;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.helper.ItemTouchHelper;
import android.util.Log;
import android.view.ContextMenu;
import android.view.Display;
@@ -54,6 +58,7 @@ import android.widget.TextView;
import android.widget.ViewFlipper;
import github.daneren2005.dsub.R;
import github.daneren2005.dsub.activity.SubsonicFragmentActivity;
+import github.daneren2005.dsub.adapter.SectionAdapter;
import github.daneren2005.dsub.audiofx.EqualizerController;
import github.daneren2005.dsub.domain.Bookmark;
import github.daneren2005.dsub.domain.PlayerState;
@@ -61,6 +66,7 @@ import github.daneren2005.dsub.domain.RepeatMode;
import github.daneren2005.dsub.domain.ServerInfo;
import github.daneren2005.dsub.service.DownloadFile;
import github.daneren2005.dsub.service.DownloadService;
+import github.daneren2005.dsub.service.DownloadService.OnSongChangedListener;
import github.daneren2005.dsub.service.MusicService;
import github.daneren2005.dsub.service.MusicServiceFactory;
import github.daneren2005.dsub.service.OfflineException;
@@ -69,6 +75,7 @@ import github.daneren2005.dsub.util.Constants;
import github.daneren2005.dsub.util.SilentBackgroundTask;
import github.daneren2005.dsub.adapter.DownloadFileAdapter;
import github.daneren2005.dsub.view.FadeOutAnimation;
+import github.daneren2005.dsub.view.SongView;
import github.daneren2005.dsub.view.UpdateView;
import github.daneren2005.dsub.util.Util;
@@ -78,14 +85,12 @@ import github.daneren2005.dsub.util.*;
import github.daneren2005.dsub.view.AutoRepeatButton;
import java.util.ArrayList;
import java.util.concurrent.ScheduledFuture;
-import com.mobeta.android.dslv.*;
import github.daneren2005.dsub.activity.SubsonicActivity;
-public class NowPlayingFragment extends SubsonicFragment implements OnGestureListener {
+public class NowPlayingFragment extends SubsonicFragment implements OnGestureListener, SectionAdapter.OnItemClickedListener<DownloadFile>, OnSongChangedListener {
private static final String TAG = NowPlayingFragment.class.getSimpleName();
private static final int PERCENTAGE_OF_SCREEN_FOR_SWIPE = 10;
private static final int INCREMENT_TIME = 5000;
- private static final int SERVICE_BACKOFF = 200;
private static final int ACTION_PREVIOUS = 1;
private static final int ACTION_NEXT = 2;
@@ -96,7 +101,7 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
private TextView emptyTextView;
private TextView songTitleTextView;
private ImageView albumArtImageView;
- private DragSortListView playlistView;
+ private RecyclerView playlistView;
private TextView positionTextView;
private TextView durationTextView;
private TextView statusTextView;
@@ -121,13 +126,12 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
private ScheduledFuture<?> hideControlsFuture;
private List<DownloadFile> songList;
private DownloadFileAdapter songListAdapter;
- private SilentBackgroundTask<Void> onProgressChangedTask;
- private SilentBackgroundTask<Void> onCurrentChangedTask;
- private SilentBackgroundTask<Void> onDownloadListChangedTask;
private boolean seekInProgress = false;
private boolean startFlipped = false;
private boolean scrollWhenLoaded = false;
private int lastY = 0;
+ private int currentPlayingSize = 0;
+ private MenuItem timerMenu;
/**
* Called when the activity is first created.
@@ -141,6 +145,7 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
startFlipped = true;
}
}
+ primaryFragment = false;
}
@Override
@@ -153,11 +158,8 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
rootView = inflater.inflate(R.layout.download, container, false);
setTitle(R.string.button_bar_now_playing);
-
+
mainLayout = rootView.findViewById(R.id.download_layout);
- if(!primaryFragment) {
- mainLayout.setVisibility(View.GONE);
- }
WindowManager w = context.getWindowManager();
Display d = w.getDefaultDisplay();
@@ -173,7 +175,6 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
durationTextView = (TextView)rootView.findViewById(R.id.download_duration);
statusTextView = (TextView)rootView.findViewById(R.id.download_status);
progressBar = (SeekBar)rootView.findViewById(R.id.download_progress_bar);
- playlistView = (DragSortListView)rootView.findViewById(R.id.download_list);
previousButton = (AutoRepeatButton)rootView.findViewById(R.id.download_previous);
nextButton = (AutoRepeatButton)rootView.findViewById(R.id.download_next);
pauseButton =rootView.findViewById(R.id.download_pause);
@@ -185,6 +186,45 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
rateGoodButton = (ImageButton) rootView.findViewById(R.id.download_rating_good);
toggleListButton =rootView.findViewById(R.id.download_toggle_list);
+ playlistView = (RecyclerView)rootView.findViewById(R.id.download_list);
+ setupLayoutManager(playlistView, false);
+ ItemTouchHelper touchHelper = new ItemTouchHelper(new ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP | ItemTouchHelper.DOWN, ItemTouchHelper.LEFT) {
+ @Override
+ public boolean onMove(RecyclerView recyclerView, final RecyclerView.ViewHolder fromHolder, final RecyclerView.ViewHolder toHolder) {
+ new SilentBackgroundTask<Void>(context) {
+ private int from;
+ private int to;
+
+ @Override
+ protected Void doInBackground() throws Throwable {
+ from = fromHolder.getAdapterPosition();
+ to = toHolder.getAdapterPosition();
+ getDownloadService().swap(true, from, to);
+ return null;
+ }
+
+ @Override
+ protected void done(Void result) {
+ songListAdapter.notifyItemMoved(from, to);
+ }
+ }.execute();
+
+ return true;
+ }
+
+ @Override
+ public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
+ SongView songView = (SongView) ((UpdateView.UpdateViewHolder) viewHolder).getUpdateView();
+ DownloadFile downloadFile = songView.getDownloadFile();
+
+ DownloadService downloadService = getDownloadService();
+ downloadService.remove(downloadFile);
+ songListAdapter.removeItem(downloadFile);
+ currentRevision = downloadService.getDownloadListUpdateRevision();
+ }
+ });
+ touchHelper.attachToRecyclerView(playlistView);
+
starButton = (ImageButton)rootView.findViewById(R.id.download_star);
if(Util.getPreferences(context).getBoolean(Constants.PREFERENCES_KEY_MENU_STAR, true)) {
starButton.setOnClickListener(new OnClickListener() {
@@ -196,7 +236,11 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
toggleStarred(currentSong, new OnStarChange() {
@Override
void starChange(boolean starred) {
- starButton.setImageResource(currentSong.isStarred() ? android.R.drawable.btn_star_big_on : android.R.drawable.btn_star_big_off);
+ if(currentSong.isStarred()) {
+ starButton.setImageDrawable(DrawableTint.getTintedDrawable(context, R.drawable.ic_toggle_star));
+ } else {
+ starButton.setImageResource(R.drawable.ic_toggle_star_outline_dark);
+ }
}
});
}
@@ -239,12 +283,6 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
getDownloadService().previous();
return null;
}
-
- @Override
- protected void done(Void result) {
- onCurrentChanged();
- onProgressChanged();
- }
}.execute();
setControlsVisible(true);
}
@@ -265,14 +303,6 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
getDownloadService().next();
return true;
}
-
- @Override
- protected void done(Boolean result) {
- if (result) {
- onCurrentChanged();
- onProgressChanged();
- }
- }
}.execute();
setControlsVisible(true);
}
@@ -292,12 +322,6 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
getDownloadService().pause();
return null;
}
-
- @Override
- protected void done(Void result) {
- onCurrentChanged();
- onProgressChanged();
- }
}.execute();
}
});
@@ -311,12 +335,6 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
getDownloadService().reset();
return null;
}
-
- @Override
- protected void done(Void result) {
- onCurrentChanged();
- onProgressChanged();
- }
}.execute();
}
});
@@ -331,12 +349,6 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
start();
return null;
}
-
- @Override
- protected void done(Void result) {
- onCurrentChanged();
- onProgressChanged();
- }
}.execute();
}
});
@@ -346,7 +358,6 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
public void onClick(View view) {
RepeatMode repeatMode = getDownloadService().getRepeatMode().next();
getDownloadService().setRepeatMode(repeatMode);
- onDownloadListChanged();
switch (repeatMode) {
case OFF:
Util.toast(context, R.string.download_repeat_off);
@@ -392,21 +403,21 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
if(context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
rateBadButton.setImageResource(R.drawable.ic_action_rating_bad_dark);
} else {
- rateBadButton.setImageResource(Util.getAttribute(context, R.attr.rating_bad));
+ rateBadButton.setImageResource(DrawableTint.getDrawableRes(context, R.attr.rating_bad));
}
} else {
// Immediately skip to the next song
downloadService.next(true);
-
+
// Otherwise set rating to 1
setRating(entry, 1);
- rateBadButton.setImageResource(R.drawable.ic_action_rating_bad_selected);
+ rateBadButton.setImageDrawable(DrawableTint.getTintedDrawable(context, R.drawable.ic_action_rating_bad_selected));
// Make sure good rating is blank
if (context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
rateGoodButton.setImageResource(R.drawable.ic_action_rating_good_dark);
} else {
- rateGoodButton.setImageResource(Util.getAttribute(context, R.attr.rating_good));
+ rateGoodButton.setImageResource(DrawableTint.getDrawableRes(context, R.attr.rating_good));
}
}
}
@@ -432,18 +443,18 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
if (context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
rateGoodButton.setImageResource(R.drawable.ic_action_rating_good_dark);
} else {
- rateGoodButton.setImageResource(Util.getAttribute(context, R.attr.rating_good));
+ rateGoodButton.setImageResource(DrawableTint.getDrawableRes(context, R.attr.rating_good));
}
} else {
// Otherwise set rating to maximum
setRating(entry, 5);
- rateGoodButton.setImageResource(R.drawable.ic_action_rating_good_selected);
+ rateGoodButton.setImageDrawable(DrawableTint.getTintedDrawable(context, R.drawable.ic_action_rating_good_selected));
// Make sure bad rating is blank
if (context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
rateBadButton.setImageResource(R.drawable.ic_action_rating_bad_dark);
} else {
- rateBadButton.setImageResource(Util.getAttribute(context, R.attr.rating_bad));
+ rateBadButton.setImageResource(DrawableTint.getDrawableRes(context, R.attr.rating_bad));
}
}
}
@@ -482,7 +493,6 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
@Override
protected void done(Void result) {
seekInProgress = false;
- NowPlayingFragment.this.onProgressChanged();
}
}.execute();
}
@@ -500,55 +510,6 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
}
}
});
- playlistView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
- @Override
- public void onItemClick(AdapterView<?> parent, View view, final int position, long id) {
- warnIfStorageUnavailable();
- new SilentBackgroundTask<Void>(context) {
- @Override
- protected Void doInBackground() throws Throwable {
- getDownloadService().play(position);
- return null;
- }
-
- @Override
- protected void done(Void result) {
- onCurrentChanged();
- onProgressChanged();
- }
- }.execute();
- }
- });
- playlistView.setDropListener(new DragSortListView.DropListener() {
- @Override
- public void drop(final int from, final int to) {
- new SilentBackgroundTask<Void>(context) {
- @Override
- protected Void doInBackground() throws Throwable {
- getDownloadService().swap(true, from, to);
- onDownloadListChanged();
-
- return null;
- }
- }.execute();
- }
- });
- playlistView.setRemoveListener(new DragSortListView.RemoveListener() {
- @Override
- public void remove(int which) {
- getDownloadService().remove(which);
- onDownloadListChanged();
- }
- });
-
- registerForContextMenu(playlistView);
-
- DownloadService downloadService = getDownloadService();
- if (downloadService != null && context.getIntent().getBooleanExtra(Constants.INTENT_EXTRA_NAME_SHUFFLE, false)) {
- context.getIntent().removeExtra(Constants.INTENT_EXTRA_NAME_SHUFFLE);
- warnIfStorageUnavailable();
- downloadService.setShufflePlayEnabled(true);
- }
if(Build.MODEL.equals("Nexus 4") || Build.MODEL.equals("GT-I9100")) {
View slider = rootView.findViewById(R.id.download_slider);
@@ -565,10 +526,11 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
menuInflater.inflate(R.menu.nowplaying_offline, menu);
} else {
menuInflater.inflate(R.menu.nowplaying, menu);
-
- if(downloadService != null && downloadService.getSleepTimer()) {
- menu.findItem(R.id.menu_toggle_timer).setTitle(R.string.download_stop_timer);
- }
+ }
+ if(downloadService != null && downloadService.getSleepTimer()) {
+ int timeRemaining = downloadService.getSleepTimeRemaining();
+ timerMenu = menu.findItem(R.id.menu_toggle_timer);
+ timerMenu.setTitle(context.getResources().getString(R.string.download_stop_time_remaining, Util.formatDuration(timeRemaining)));
}
if(downloadService != null && downloadService.getKeepScreenOn()) {
menu.findItem(R.id.menu_screen_on_off).setChecked(true);
@@ -604,47 +566,30 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
if(menuItemSelected(menuItem.getItemId(), null)) {
return true;
}
-
+
return super.onOptionsItemSelected(menuItem);
}
@Override
- public void onCreateContextMenu(android.view.ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) {
- super.onCreateContextMenu(menu, view, menuInfo);
- if(!primaryFragment) {
- return;
+ public void onCreateContextMenu(Menu menu, MenuInflater menuInflater, UpdateView<DownloadFile> updateView, DownloadFile downloadFile) {
+ if(Util.isOffline(context)) {
+ menuInflater.inflate(R.menu.nowplaying_context_offline, menu);
+ } else {
+ menuInflater.inflate(R.menu.nowplaying_context, menu);
+ menu.findItem(R.id.menu_star).setTitle(downloadFile.getSong().isStarred() ? R.string.common_unstar : R.string.common_star);
}
- if (view == playlistView) {
- AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
- DownloadFile downloadFile = (DownloadFile) playlistView.getItemAtPosition(info.position);
-
- android.view.MenuInflater inflater = context.getMenuInflater();
- if(Util.isOffline(context)) {
- inflater.inflate(R.menu.nowplaying_context_offline, menu);
- } else {
- inflater.inflate(R.menu.nowplaying_context, menu);
- menu.findItem(R.id.menu_star).setTitle(downloadFile.getSong().isStarred() ? R.string.common_unstar : R.string.common_star);
- }
-
- if (downloadFile.getSong().getParent() == null) {
- menu.findItem(R.id.menu_show_album).setVisible(false);
- menu.findItem(R.id.menu_show_artist).setVisible(false);
- }
-
- hideMenuItems(menu, (AdapterView.AdapterContextMenuInfo) menuInfo);
+ if (downloadFile.getSong().getParent() == null) {
+ menu.findItem(R.id.menu_show_album).setVisible(false);
+ menu.findItem(R.id.menu_show_artist).setVisible(false);
}
+
+ MenuUtil.hideMenuItems(context, menu);
}
@Override
- public boolean onContextItemSelected(android.view.MenuItem menuItem) {
- if(!primaryFragment) {
- return false;
- }
-
- AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo();
- DownloadFile downloadFile = (DownloadFile) playlistView.getItemAtPosition(info.position);
- return menuItemSelected(menuItem.getItemId(), downloadFile) || super.onContextItemSelected(menuItem);
+ public boolean onContextItemSelected(MenuItem menuItem, UpdateView<DownloadFile> updateView, DownloadFile downloadFile) {
+ return menuItemSelected(menuItem.getItemId(), downloadFile);
}
private boolean menuItemSelected(int menuItemId, final DownloadFile song) {
@@ -712,20 +657,7 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
replaceFragment(fragment);
return true;
- } case R.id.menu_remove:
- new SilentBackgroundTask<Void>(context) {
- @Override
- protected Void doInBackground() throws Throwable {
- getDownloadService().remove(song);
- return null;
- }
-
- @Override
- protected void done(Void result) {
- onDownloadListChanged();
- }
- }.execute();
- return true;
+ }
case R.id.menu_delete:
List<Entry> songs = new ArrayList<Entry>(1);
songs.add(song.getSong());
@@ -745,7 +677,7 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
@Override
protected void done(Void result) {
- onDownloadListChanged();
+ context.closeNowPlaying();
}
}.execute();
}
@@ -841,26 +773,18 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
@Override
public void onResume() {
super.onResume();
-
+ if(this.primaryFragment) {
+ onResumeHandlers();
+ } else {
+ update();
+ }
+ }
+ private void onResumeHandlers() {
final Handler handler = new Handler();
- Runnable runnable = new Runnable() {
- @Override
- public void run() {
- handler.post(new Runnable() {
- @Override
- public void run() {
- update();
- }
- });
- }
- };
-
executorService = Executors.newSingleThreadScheduledExecutor();
- executorService.scheduleWithFixedDelay(runnable, 0L, 1000L, TimeUnit.MILLISECONDS);
-
setControlsVisible(true);
- DownloadService downloadService = getDownloadService();
+ final DownloadService downloadService = getDownloadService();
if (downloadService == null || downloadService.getCurrentPlaying() == null || startFlipped) {
playlistFlipper.setDisplayedChild(1);
}
@@ -875,48 +799,62 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
if(currentPlaying == null && downloadService != null && currentPlaying == downloadService.getCurrentPlaying()) {
getImageLoader().loadImage(albumArtImageView, (Entry) null, true, false);
}
- if(downloadService != null) {
- downloadService.startRemoteScan();
- } else {
- // Make sure to call remote scan once the service is ready
- final Runnable waitForService = new Runnable() {
- @Override
- public void run() {
- DownloadService service = getDownloadService();
- if(service != null) {
- service.startRemoteScan();
- } else {
- handler.postDelayed(this, SERVICE_BACKOFF);
- }
- }
- };
- handler.postDelayed(waitForService, SERVICE_BACKOFF);
- }
+ context.runWhenServiceAvailable(new Runnable() {
+ @Override
+ public void run() {
+ if(primaryFragment) {
+ DownloadService downloadService = getDownloadService();
+ downloadService.startRemoteScan();
+ downloadService.addOnSongChangedListener(NowPlayingFragment.this, true);
+ }
+ }
+ });
}
@Override
public void onPause() {
super.onPause();
- executorService.shutdown();
- if(getDownloadService() != null) {
- getDownloadService().stopRemoteScan();
+ onPauseHandlers();
+ }
+ private void onPauseHandlers() {
+ if(executorService != null) {
+ DownloadService downloadService = getDownloadService();
+ if (downloadService != null) {
+ downloadService.stopRemoteScan();
+ downloadService.removeOnSongChangeListener(this);
+ }
+ playlistFlipper.setDisplayedChild(0);
}
}
-
+
@Override
public void setPrimaryFragment(boolean primary) {
super.setPrimaryFragment(primary);
if(rootView != null) {
if(primary) {
- mainLayout.setVisibility(View.VISIBLE);
- updateButtons();
+ onResumeHandlers();
} else {
- mainLayout.setVisibility(View.GONE);
+ onPauseHandlers();
}
}
}
+ @Override
+ public void setTitle(int title) {
+ this.title = context.getResources().getString(title);
+ if(this.primaryFragment) {
+ context.setTitle(this.title);
+ }
+ }
+ @Override
+ public void setSubtitle(CharSequence title) {
+ this.subtitle = title;
+ if(this.primaryFragment) {
+ context.setSubtitle(title);
+ }
+ }
+
private void scheduleHideControls() {
if (hideControlsFuture != null) {
hideControlsFuture.cancel(false);
@@ -954,7 +892,7 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
if(context == null) {
return;
}
-
+
if(Util.isOffline(context)) {
bookmarkButton.setVisibility(View.GONE);
rateBadButton.setVisibility(View.GONE);
@@ -971,46 +909,33 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
}
// Scroll to current playing/downloading.
+ @TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void scrollToCurrent() {
if (getDownloadService() == null || songListAdapter == null) {
scrollWhenLoaded = true;
return;
}
- for (int i = 0; i < songListAdapter.getCount(); i++) {
- if (currentPlaying == playlistView.getItemAtPosition(i)) {
- playlistView.setSelectionFromTop(i, 40);
- return;
- }
+ // Try to get position of current playing/downloading
+ int position = songListAdapter.getItemPosition(currentPlaying);
+ if(position == -1) {
+ DownloadFile currentDownloading = getDownloadService().getCurrentDownloading();
+ position = songListAdapter.getItemPosition(currentDownloading);
}
- DownloadFile currentDownloading = getDownloadService().getCurrentDownloading();
- for (int i = 0; i < songListAdapter.getCount(); i++) {
- if (currentDownloading == playlistView.getItemAtPosition(i)) {
- playlistView.setSelectionFromTop(i, 40);
- return;
- }
+
+ // If found, scroll to it
+ if(position != -1) {
+ // RecyclerView.scrollToPosition just puts it on the screen (ie: bottom if scrolled below it)
+ LinearLayoutManager layoutManager = (LinearLayoutManager) playlistView.getLayoutManager();
+ layoutManager.scrollToPositionWithOffset(position, 0);
}
}
private void update() {
- if (getDownloadService() == null) {
- return;
- }
-
- if (currentRevision != getDownloadService().getDownloadListUpdateRevision()) {
- onDownloadListChanged();
- }
-
- if (currentPlaying != getDownloadService().getCurrentPlaying()) {
- onCurrentChanged();
- }
-
if(startFlipped) {
startFlipped = false;
scrollToCurrent();
}
-
- onProgressChanged();
}
protected void startTimer() {
@@ -1043,26 +968,26 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
- lengthBar.setProgress(length - 1);
+ lengthBar.setProgress(length - 1);
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(R.string.menu_set_timer)
- .setView(dialogView)
- .setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int id) {
- int length = getMinutes(lengthBar.getProgress());
+ .setView(dialogView)
+ .setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int id) {
+ int length = getMinutes(lengthBar.getProgress());
- SharedPreferences.Editor editor = prefs.edit();
- editor.putString(Constants.PREFERENCES_KEY_SLEEP_TIMER_DURATION, Integer.toString(length));
- editor.commit();
+ SharedPreferences.Editor editor = prefs.edit();
+ editor.putString(Constants.PREFERENCES_KEY_SLEEP_TIMER_DURATION, Integer.toString(length));
+ editor.commit();
- getDownloadService().setSleepTimerDuration(length);
- getDownloadService().startSleepTimer();
- context.supportInvalidateOptionsMenu();
- }
- })
- .setNegativeButton(R.string.common_cancel, null);
+ getDownloadService().setSleepTimerDuration(length);
+ getDownloadService().startSleepTimer();
+ context.supportInvalidateOptionsMenu();
+ }
+ })
+ .setNegativeButton(R.string.common_cancel, null);
AlertDialog dialog = builder.create();
dialog.show();
}
@@ -1087,7 +1012,7 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
playlistFlipper.setInAnimation(AnimationUtils.loadAnimation(context, R.anim.push_up_in));
playlistFlipper.setOutAnimation(AnimationUtils.loadAnimation(context, R.anim.push_up_out));
playlistFlipper.setDisplayedChild(1);
-
+
UpdateView.triggerUpdate();
}
}
@@ -1108,258 +1033,6 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
}
}
}
- private void onDownloadListChanged() {
- onDownloadListChanged(false);
- }
- private void onDownloadListChanged(final boolean refresh) {
- final DownloadService downloadService = getDownloadService();
- if (downloadService == null || onDownloadListChangedTask != null) {
- return;
- }
-
- onDownloadListChangedTask = new SilentBackgroundTask<Void>(context) {
- int currentPlayingIndex;
- int size;
-
- @Override
- protected Void doInBackground() throws Throwable {
- currentPlayingIndex = downloadService.getCurrentPlayingIndex() + 1;
- size = downloadService.size();
-
- return null;
- }
-
- @Override
- protected void done(Void result) {
- List<DownloadFile> list;
- list = downloadService.getSongs();
-
- if(downloadService.isShufflePlayEnabled()) {
- emptyTextView.setText(R.string.download_shuffle_loading);
- }
- else {
- emptyTextView.setText(R.string.download_empty);
- }
-
- if(songListAdapter == null || refresh) {
- songList = new ArrayList<DownloadFile>();
- songList.addAll(list);
- playlistView.setAdapter(songListAdapter = new DownloadFileAdapter(context, songList));
- } else {
- songList.clear();
- songList.addAll(list);
- songListAdapter.notifyDataSetChanged();
- }
-
- emptyTextView.setVisibility(list.isEmpty() ? View.VISIBLE : View.GONE);
- currentRevision = downloadService.getDownloadListUpdateRevision();
-
- switch (downloadService.getRepeatMode()) {
- case OFF:
- if("light".equals(SubsonicActivity.getThemeName()) | "light_fullscreen".equals(SubsonicActivity.getThemeName())) {
- repeatButton.setImageResource(R.drawable.media_repeat_off_light);
- } else {
- repeatButton.setImageResource(R.drawable.media_repeat_off);
- }
- break;
- case ALL:
- repeatButton.setImageResource(R.drawable.media_repeat_all);
- break;
- case SINGLE:
- repeatButton.setImageResource(R.drawable.media_repeat_single);
- break;
- default:
- break;
- }
-
- if(scrollWhenLoaded) {
- scrollToCurrent();
- scrollWhenLoaded = false;
- }
-
- setSubtitle(context.getResources().getString(R.string.download_playing_out_of, currentPlayingIndex, size));
- onDownloadListChangedTask = null;
- if(onCurrentChangedTask != null) {
- onCurrentChangedTask.execute();
- } else if(onProgressChangedTask != null) {
- onProgressChangedTask.execute();
- }
- }
- };
- onDownloadListChangedTask.execute();
- }
-
- private void onCurrentChanged() {
- final DownloadService downloadService = getDownloadService();
- if (downloadService == null || onCurrentChangedTask != null) {
- return;
- }
-
- onCurrentChangedTask = new SilentBackgroundTask<Void>(context) {
- int currentPlayingIndex;
- int currentPlayingSize;
-
- @Override
- protected Void doInBackground() throws Throwable {
- currentPlaying = downloadService.getCurrentPlaying();
- currentPlayingIndex = downloadService.getCurrentPlayingIndex() + 1;
- currentPlayingSize = downloadService.size();
- return null;
- }
-
- @Override
- protected void done(Void result) {
- if (currentPlaying != null) {
- Entry song = currentPlaying.getSong();
- songTitleTextView.setText(song.getTitle());
- getImageLoader().loadImage(albumArtImageView, song, true, true);
- starButton.setImageResource(song.isStarred() ? android.R.drawable.btn_star_big_on : android.R.drawable.btn_star_big_off);
- setSubtitle(context.getResources().getString(R.string.download_playing_out_of, currentPlayingIndex, currentPlayingSize));
-
- int badRating, goodRating, bookmark;
- if(song.getRating() == 1) {
- badRating = R.drawable.ic_action_rating_bad_selected;
- } else if(context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
- badRating = R.drawable.ic_action_rating_bad_dark;
- } else {
- badRating = Util.getAttribute(context, R.attr.rating_bad);
- }
- rateBadButton.setImageResource(badRating);
-
- if(song.getRating() == 5) {
- goodRating = R.drawable.ic_action_rating_good_selected;
- } else if(context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
- goodRating = R.drawable.ic_action_rating_good_dark;
- } else {
- goodRating = Util.getAttribute(context, R.attr.rating_good);
- }
- rateGoodButton.setImageResource(goodRating);
-
- if(song.getBookmark() != null) {
- bookmark = R.drawable.ic_menu_bookmark_selected;
- } else if(context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
- bookmark = R.drawable.ic_menu_bookmark_dark;
- } else {
- bookmark = Util.getAttribute(context, R.attr.bookmark);
- }
- bookmarkButton.setImageResource(bookmark);
- } else {
- songTitleTextView.setText(null);
- getImageLoader().loadImage(albumArtImageView, (Entry) null, true, false);
- starButton.setImageResource(android.R.drawable.btn_star_big_off);
- setSubtitle(null);
- }
- onCurrentChangedTask = null;
- if(onProgressChangedTask != null) {
- onProgressChangedTask.execute();
- }
- }
- };
-
- if(onDownloadListChangedTask == null) {
- onCurrentChangedTask.execute();
- }
- }
-
- private void onProgressChanged() {
- // Make sure to only be trying to run one of these at a time
- if (getDownloadService() == null || onProgressChangedTask != null) {
- return;
- }
-
- onProgressChangedTask = new SilentBackgroundTask<Void>(context) {
- DownloadService downloadService;
- int millisPlayed;
- Integer duration;
- PlayerState playerState;
- boolean isSeekable;
-
- @Override
- protected Void doInBackground() throws Throwable {
- downloadService = getDownloadService();
- millisPlayed = Math.max(0, downloadService.getPlayerPosition());
- duration = downloadService.getPlayerDuration();
- playerState = getDownloadService().getPlayerState();
- isSeekable = downloadService.isSeekable();
- return null;
- }
-
- @Override
- protected void done(Void result) {
- if (currentPlaying != null) {
- int millisTotal = duration == null ? 0 : duration;
-
- positionTextView.setText(Util.formatDuration(millisPlayed / 1000));
- if(millisTotal > 0) {
- durationTextView.setText(Util.formatDuration(millisTotal / 1000));
- } else {
- durationTextView.setText("-:--");
- }
- progressBar.setMax(millisTotal == 0 ? 100 : millisTotal); // Work-around for apparent bug.
- if(!seekInProgress) {
- progressBar.setProgress(millisPlayed);
- }
- progressBar.setEnabled(isSeekable);
- } else {
- positionTextView.setText("0:00");
- durationTextView.setText("-:--");
- progressBar.setProgress(0);
- progressBar.setEnabled(false);
- }
-
- switch (playerState) {
- case DOWNLOADING:
- if(currentPlaying != null) {
- if(Util.isWifiRequiredForDownload(context)) {
- statusTextView.setText(context.getResources().getString(R.string.download_playerstate_mobile_disabled));
- } else {
- long bytes = currentPlaying.getPartialFile().length();
- statusTextView.setText(context.getResources().getString(R.string.download_playerstate_downloading, Util.formatLocalizedBytes(bytes, context)));
- }
- }
- break;
- case PREPARING:
- statusTextView.setText(R.string.download_playerstate_buffering);
- break;
- default:
- if(currentPlaying != null) {
- String artist = "";
- if(currentPlaying.getSong().getArtist() != null) {
- artist = currentPlaying.getSong().getArtist() + " - ";
- }
- statusTextView.setText(artist + currentPlaying.getSong().getAlbum());
- } else {
- statusTextView.setText(null);
- }
- break;
- }
-
- switch (playerState) {
- case STARTED:
- pauseButton.setVisibility(View.VISIBLE);
- stopButton.setVisibility(View.INVISIBLE);
- startButton.setVisibility(View.INVISIBLE);
- break;
- case DOWNLOADING:
- case PREPARING:
- pauseButton.setVisibility(View.INVISIBLE);
- stopButton.setVisibility(View.VISIBLE);
- startButton.setVisibility(View.INVISIBLE);
- break;
- default:
- pauseButton.setVisibility(View.INVISIBLE);
- stopButton.setVisibility(View.INVISIBLE);
- startButton.setVisibility(View.VISIBLE);
- break;
- }
-
- onProgressChangedTask = null;
- }
- };
- if(onDownloadListChangedTask == null && onCurrentChangedTask == null) {
- onProgressChangedTask.execute();
- }
- }
private void changeProgress(final int ms) {
final DownloadService downloadService = getDownloadService();
@@ -1395,33 +1068,33 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
}
}.execute();
}
-
+
private void createBookmark() {
DownloadService downloadService = getDownloadService();
if(downloadService == null) {
return;
}
-
+
final DownloadFile currentDownload = downloadService.getCurrentPlaying();
if(currentDownload == null) {
return;
}
-
+
View dialogView = context.getLayoutInflater().inflate(R.layout.create_bookmark, null);
final EditText commentBox = (EditText)dialogView.findViewById(R.id.comment_text);
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(R.string.download_save_bookmark_title)
- .setView(dialogView)
- .setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int id) {
- String comment = commentBox.getText().toString();
+ .setView(dialogView)
+ .setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int id) {
+ String comment = commentBox.getText().toString();
- createBookmark(currentDownload, comment);
- }
- })
- .setNegativeButton(R.string.common_cancel, null);
+ createBookmark(currentDownload, comment);
+ }
+ })
+ .setNegativeButton(R.string.common_cancel, null);
AlertDialog dialog = builder.create();
dialog.show();
}
@@ -1430,13 +1103,13 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
if(downloadService == null) {
return;
}
-
+
final Entry currentSong = currentDownload.getSong();
final int position = downloadService.getPlayerPosition();
final Bookmark oldBookmark = currentSong.getBookmark();
currentSong.setBookmark(new Bookmark(position));
- bookmarkButton.setImageResource(R.drawable.ic_menu_bookmark_selected);
-
+ bookmarkButton.setImageDrawable(DrawableTint.getTintedDrawable(context, R.drawable.ic_menu_bookmark_selected));
+
new SilentBackgroundTask<Void>(context) {
@Override
protected Void doInBackground() throws Throwable {
@@ -1458,30 +1131,30 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
Util.toast(context, R.string.download_save_bookmark);
setControlsVisible(true);
}
-
+
@Override
protected void error(Throwable error) {
Log.w(TAG, "Failed to create bookmark", error);
currentSong.setBookmark(oldBookmark);
-
+
// If no bookmark at start, then return to no bookmark
if(oldBookmark == null) {
int bookmark;
if(context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
bookmark = R.drawable.ic_menu_bookmark_dark;
} else {
- bookmark = Util.getAttribute(context, R.attr.bookmark);
+ bookmark = DrawableTint.getDrawableRes(context, R.attr.bookmark);
}
bookmarkButton.setImageResource(bookmark);
}
-
+
String msg;
if(error instanceof OfflineException || error instanceof ServerTooOldException) {
msg = getErrorMessage(error);
} else {
msg = context.getResources().getString(R.string.download_save_bookmark_failed) + getErrorMessage(error);
}
-
+
Util.toast(context, msg, false);
}
}.execute();
@@ -1538,11 +1211,6 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
downloadService.seekTo(downloadService.getPlayerPosition() - DownloadService.REWIND);
break;
}
-
- onProgressChanged();
- if(performAction == ACTION_NEXT || performAction == ACTION_PREVIOUS) {
- onCurrentChanged();
- }
return null;
}
}.execute();
@@ -1570,4 +1238,204 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
public boolean onSingleTapUp(MotionEvent e) {
return false;
}
+
+ @Override
+ public void onItemClicked(final DownloadFile item) {
+ warnIfStorageUnavailable();
+ new SilentBackgroundTask<Void>(context) {
+ @Override
+ protected Void doInBackground() throws Throwable {
+ getDownloadService().play(item);
+ return null;
+ }
+ }.execute();
+ }
+
+ @Override
+ public void onSongChanged(DownloadFile currentPlaying, int currentPlayingIndex) {
+ this.currentPlaying = currentPlaying;
+ if (currentPlaying != null) {
+ Entry song = currentPlaying.getSong();
+ songTitleTextView.setText(song.getTitle());
+ getImageLoader().loadImage(albumArtImageView, song, true, true);
+ if(song.isStarred()) {
+ starButton.setImageDrawable(DrawableTint.getTintedDrawable(context, R.drawable.ic_toggle_star));
+ } else {
+ starButton.setImageResource(R.drawable.ic_toggle_star_outline_dark);
+ }
+ setSubtitle(context.getResources().getString(R.string.download_playing_out_of, currentPlayingIndex + 1, currentPlayingSize));
+
+ int badRating, goodRating, bookmark;
+ if(song.getRating() == 1) {
+ rateBadButton.setImageDrawable(DrawableTint.getTintedDrawable(context, R.drawable.ic_action_rating_bad_selected));
+ } else {
+ if(context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
+ badRating = R.drawable.ic_action_rating_bad_dark;
+ } else {
+ badRating = DrawableTint.getDrawableRes(context, R.attr.rating_bad);
+ }
+ rateBadButton.setImageResource(badRating);
+ }
+
+ if(song.getRating() == 5) {
+ rateGoodButton.setImageDrawable(DrawableTint.getTintedDrawable(context, R.drawable.ic_action_rating_good_selected));
+ } else {
+ if(context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
+ goodRating = R.drawable.ic_action_rating_good_dark;
+ } else {
+ goodRating = DrawableTint.getDrawableRes(context, R.attr.rating_good);
+ }
+ rateGoodButton.setImageResource(goodRating);
+ }
+
+ if(song.getBookmark() != null) {
+ bookmarkButton.setImageDrawable(DrawableTint.getTintedDrawable(context, R.drawable.ic_menu_bookmark_selected));
+ } else {
+ if(context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
+ bookmark = R.drawable.ic_menu_bookmark_dark;
+ } else {
+ bookmark = DrawableTint.getDrawableRes(context, R.attr.bookmark);
+ }
+ bookmarkButton.setImageResource(bookmark);
+ }
+ } else {
+ songTitleTextView.setText(null);
+ getImageLoader().loadImage(albumArtImageView, (Entry) null, true, false);
+ starButton.setImageResource(R.drawable.ic_toggle_star_outline_dark);
+ setSubtitle(null);
+ }
+ }
+
+ @Override
+ public void onSongsChanged(List<DownloadFile> songs, DownloadFile currentPlaying, int currentPlayingIndex) {
+ currentPlayingSize = songs.size();
+
+ DownloadService downloadService = getDownloadService();
+ if(downloadService.isShufflePlayEnabled()) {
+ emptyTextView.setText(R.string.download_shuffle_loading);
+ }
+ else {
+ emptyTextView.setText(R.string.download_empty);
+ }
+
+ if(songListAdapter == null) {
+ songList = new ArrayList<>();
+ songList.addAll(songs);
+ playlistView.setAdapter(songListAdapter = new DownloadFileAdapter(context, songList, NowPlayingFragment.this));
+ } else {
+ songList.clear();
+ songList.addAll(songs);
+ songListAdapter.notifyDataSetChanged();
+ }
+
+ emptyTextView.setVisibility(songs.isEmpty() ? View.VISIBLE : View.GONE);
+ currentRevision = downloadService.getDownloadListUpdateRevision();
+
+ switch (downloadService.getRepeatMode()) {
+ case OFF:
+ if("light".equals(SubsonicActivity.getThemeName()) | "light_fullscreen".equals(SubsonicActivity.getThemeName())) {
+ repeatButton.setImageResource(R.drawable.media_repeat_off_light);
+ } else {
+ repeatButton.setImageResource(R.drawable.media_repeat_off);
+ }
+ break;
+ case ALL:
+ repeatButton.setImageResource(R.drawable.media_repeat_all);
+ break;
+ case SINGLE:
+ repeatButton.setImageResource(R.drawable.media_repeat_single);
+ break;
+ default:
+ break;
+ }
+
+ if(scrollWhenLoaded) {
+ scrollToCurrent();
+ scrollWhenLoaded = false;
+ }
+
+ setSubtitle(context.getResources().getString(R.string.download_playing_out_of, currentPlayingIndex + 1, currentPlayingSize));
+ if(this.currentPlaying != currentPlaying) {
+ onSongChanged(currentPlaying, currentPlayingIndex);
+ }
+ }
+
+ @Override
+ public void onSongProgress(DownloadFile currentPlaying, int millisPlayed, Integer duration, boolean isSeekable) {
+ if (currentPlaying != null) {
+ int millisTotal = duration == null ? 0 : duration;
+
+ positionTextView.setText(Util.formatDuration(millisPlayed / 1000));
+ if(millisTotal > 0) {
+ durationTextView.setText(Util.formatDuration(millisTotal / 1000));
+ } else {
+ durationTextView.setText("-:--");
+ }
+ progressBar.setMax(millisTotal == 0 ? 100 : millisTotal); // Work-around for apparent bug.
+ if(!seekInProgress) {
+ progressBar.setProgress(millisPlayed);
+ }
+ progressBar.setEnabled(isSeekable);
+ } else {
+ positionTextView.setText("0:00");
+ durationTextView.setText("-:--");
+ progressBar.setProgress(0);
+ progressBar.setEnabled(false);
+ }
+
+ DownloadService downloadService = getDownloadService();
+ if(downloadService.getSleepTimer() && timerMenu != null) {
+ int timeRemaining = downloadService.getSleepTimeRemaining();
+ timerMenu.setTitle(context.getResources().getString(R.string.download_stop_time_remaining, Util.formatDuration(timeRemaining)));
+ }
+ }
+
+ @Override
+ public void onStateUpdate(DownloadFile downloadFile, PlayerState playerState) {
+ switch (playerState) {
+ case DOWNLOADING:
+ if(currentPlaying != null) {
+ if(Util.isWifiRequiredForDownload(context)) {
+ statusTextView.setText(context.getResources().getString(R.string.download_playerstate_mobile_disabled));
+ } else {
+ long bytes = currentPlaying.getPartialFile().length();
+ statusTextView.setText(context.getResources().getString(R.string.download_playerstate_downloading, Util.formatLocalizedBytes(bytes, context)));
+ }
+ }
+ break;
+ case PREPARING:
+ statusTextView.setText(R.string.download_playerstate_buffering);
+ break;
+ default:
+ if(currentPlaying != null) {
+ String artist = "";
+ if(currentPlaying.getSong().getArtist() != null) {
+ artist = currentPlaying.getSong().getArtist() + " - ";
+ }
+ statusTextView.setText(artist + currentPlaying.getSong().getAlbum());
+ } else {
+ statusTextView.setText(null);
+ }
+ break;
+ }
+
+ switch (playerState) {
+ case STARTED:
+ pauseButton.setVisibility(View.VISIBLE);
+ stopButton.setVisibility(View.INVISIBLE);
+ startButton.setVisibility(View.INVISIBLE);
+ break;
+ case DOWNLOADING:
+ case PREPARING:
+ pauseButton.setVisibility(View.INVISIBLE);
+ stopButton.setVisibility(View.VISIBLE);
+ startButton.setVisibility(View.INVISIBLE);
+ break;
+ default:
+ pauseButton.setVisibility(View.INVISIBLE);
+ stopButton.setVisibility(View.INVISIBLE);
+ startButton.setVisibility(View.VISIBLE);
+ break;
+ }
+ }
}
diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/SearchFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SearchFragment.java
index 0f1598dd..f442dabb 100644
--- a/app/src/main/java/github/daneren2005/dsub/fragments/SearchFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/SearchFragment.java
@@ -1,12 +1,16 @@
package github.daneren2005.dsub.fragments;
+import java.io.Serializable;
import java.util.ArrayList;
-import java.util.List;
import java.util.Arrays;
+import java.util.List;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.widget.SwipeRefreshLayout;
+import android.support.v7.widget.GridLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.util.Log;
import android.view.ContextMenu;
import android.view.LayoutInflater;
import android.view.Menu;
@@ -14,11 +18,13 @@ import android.view.MenuInflater;
import android.view.View;
import android.view.MenuItem;
import android.widget.AdapterView;
-import android.widget.ListAdapter;
-import android.widget.ListView;
import android.net.Uri;
import android.view.ViewGroup;
import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.adapter.ArtistAdapter;
+import github.daneren2005.dsub.adapter.EntryGridAdapter;
+import github.daneren2005.dsub.adapter.SearchAdapter;
+import github.daneren2005.dsub.adapter.SectionAdapter;
import github.daneren2005.dsub.domain.Artist;
import github.daneren2005.dsub.domain.MusicDirectory;
import github.daneren2005.dsub.domain.SearchCritera;
@@ -26,40 +32,24 @@ import github.daneren2005.dsub.domain.SearchResult;
import github.daneren2005.dsub.service.MusicService;
import github.daneren2005.dsub.service.MusicServiceFactory;
import github.daneren2005.dsub.service.DownloadService;
-import github.daneren2005.dsub.adapter.ArtistAdapter;
import github.daneren2005.dsub.util.BackgroundTask;
import github.daneren2005.dsub.util.Constants;
-import github.daneren2005.dsub.adapter.EntryAdapter;
-import github.daneren2005.dsub.adapter.MergeAdapter;
import github.daneren2005.dsub.util.TabBackgroundTask;
import github.daneren2005.dsub.util.Util;
+import github.daneren2005.dsub.view.UpdateView;
-public class SearchFragment extends SubsonicFragment {
+public class SearchFragment extends SubsonicFragment implements SectionAdapter.OnItemClickedListener<Serializable> {
private static final String TAG = SearchFragment.class.getSimpleName();
- private static final int DEFAULT_ARTISTS = 3;
- private static final int DEFAULT_ALBUMS = 5;
- private static final int DEFAULT_SONGS = 10;
-
private static final int MAX_ARTISTS = 10;
- private static final int MAX_ALBUMS = 20;
+ private static final int MAX_ALBUMS = 10;
private static final int MAX_SONGS = 25;
- private ListView list;
-
- private View artistsHeading;
- private View albumsHeading;
- private View songsHeading;
- private View moreArtistsButton;
- private View moreAlbumsButton;
- private View moreSongsButton;
+
+ protected RecyclerView recyclerView;
+ protected SearchAdapter adapter;
+ protected boolean largeAlbums = false;
+
private SearchResult searchResult;
- private MergeAdapter mergeAdapter;
- private ArtistAdapter artistAdapter;
- private ListAdapter moreArtistsAdapter;
- private EntryAdapter albumAdapter;
- private ListAdapter moreAlbumsAdapter;
- private ListAdapter moreSongsAdapter;
- private EntryAdapter songAdapter;
private boolean skipSearch = false;
private String currentQuery;
@@ -70,6 +60,7 @@ public class SearchFragment extends SubsonicFragment {
if(savedInstanceState != null) {
searchResult = (SearchResult) savedInstanceState.getSerializable(Constants.FRAGMENT_LIST);
}
+ largeAlbums = Util.getPreferences(context).getBoolean(Constants.PREFERENCES_KEY_LARGE_ALBUM_ART, true);
}
@Override
@@ -80,63 +71,43 @@ public class SearchFragment extends SubsonicFragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
- rootView = inflater.inflate(R.layout.abstract_list_fragment, container, false);
+ rootView = inflater.inflate(R.layout.abstract_recycler_fragment, container, false);
setTitle(R.string.search_title);
- View buttons = inflater.inflate(R.layout.search_buttons, null);
-
- artistsHeading = buttons.findViewById(R.id.search_artists);
- albumsHeading = buttons.findViewById(R.id.search_albums);
- songsHeading = buttons.findViewById(R.id.search_songs);
-
- moreArtistsButton = buttons.findViewById(R.id.search_more_artists);
- moreAlbumsButton = buttons.findViewById(R.id.search_more_albums);
- moreSongsButton = buttons.findViewById(R.id.search_more_songs);
-
refreshLayout = (SwipeRefreshLayout) rootView.findViewById(R.id.refresh_layout);
refreshLayout.setEnabled(false);
- list = (ListView) rootView.findViewById(R.id.fragment_list);
+ recyclerView = (RecyclerView) rootView.findViewById(R.id.fragment_recycler);
+ setupLayoutManager(recyclerView, largeAlbums);
- list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- if (view == moreArtistsButton) {
- expandArtists();
- } else if (view == moreAlbumsButton) {
- expandAlbums();
- } else if (view == moreSongsButton) {
- expandSongs();
- } else {
- Object item = parent.getItemAtPosition(position);
- if (item instanceof Artist) {
- onArtistSelected((Artist) item, false);
- } else if (item instanceof MusicDirectory.Entry) {
- MusicDirectory.Entry entry = (MusicDirectory.Entry) item;
- if (entry.isDirectory()) {
- onAlbumSelected(entry, false);
- } else if (entry.isVideo()) {
- onVideoSelected(entry);
- } else {
- onSongSelected(entry, false, true, true, false);
- }
-
- }
- }
- }
- });
- registerForContextMenu(list);
+ registerForContextMenu(recyclerView);
context.onNewIntent(context.getIntent());
if(searchResult != null) {
skipSearch = true;
- populateList();
+ recyclerView.setAdapter(adapter = new SearchAdapter(context, searchResult, getImageLoader(), largeAlbums, this));
}
return rootView;
}
@Override
+ public GridLayoutManager.SpanSizeLookup getSpanSizeLookup() {
+ final int columns = getRecyclerColumnCount();
+ return new GridLayoutManager.SpanSizeLookup() {
+ @Override
+ public int getSpanSize(int position) {
+ int viewType = adapter.getItemViewType(position);
+ if(viewType == EntryGridAdapter.VIEW_TYPE_SONG || viewType == EntryGridAdapter.VIEW_TYPE_HEADER || viewType == ArtistAdapter.VIEW_TYPE_ARTIST) {
+ return columns;
+ } else {
+ return 1;
+ }
+ }
+ };
+ }
+
+ @Override
public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) {
menuInflater.inflate(R.menu.search, menu);
}
@@ -154,43 +125,52 @@ public class SearchFragment extends SubsonicFragment {
}
@Override
- public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) {
- super.onCreateContextMenu(menu, view, menuInfo);
-
- AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
- Object selectedItem = list.getItemAtPosition(info.position);
- onCreateContextMenu(menu, view, menuInfo, selectedItem);
- if(selectedItem instanceof MusicDirectory.Entry && !((MusicDirectory.Entry) selectedItem).isVideo() && !Util.isOffline(context)) {
+ public void onCreateContextMenu(Menu menu, MenuInflater menuInflater, UpdateView<Serializable> updateView, Serializable item) {
+ onCreateContextMenuSupport(menu, menuInflater, updateView, item);
+ if(item instanceof MusicDirectory.Entry && !((MusicDirectory.Entry) item).isVideo() && !Util.isOffline(context)) {
menu.removeItem(R.id.song_menu_remove_playlist);
}
-
recreateContextMenu(menu);
}
@Override
- public boolean onContextItemSelected(MenuItem menuItem) {
- if(menuItem.getGroupId() != getSupportTag()) {
- return false;
- }
-
- AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo();
- Object selectedItem = list.getItemAtPosition(info.position);
-
- if(onContextItemSelected(menuItem, selectedItem)) {
- return true;
- }
+ public boolean onContextItemSelected(MenuItem menuItem, UpdateView<Serializable> updateView, Serializable item) {
+ return onContextItemSelected(menuItem, item);
+ }
- return true;
+ @Override
+ public void refresh(boolean refresh) {
+ context.onNewIntent(context.getIntent());
}
-
+
@Override
- public void setPrimaryFragment(boolean primary) {
- super.setPrimaryFragment(primary);
+ public void onItemClicked(Serializable item) {
+ Log.d(TAG, item.getClass().getSimpleName());
+ if (item instanceof Artist) {
+ onArtistSelected((Artist) item, false);
+ } else if (item instanceof MusicDirectory.Entry) {
+ MusicDirectory.Entry entry = (MusicDirectory.Entry) item;
+ if (entry.isDirectory()) {
+ onAlbumSelected(entry, false);
+ } else if (entry.isVideo()) {
+ onVideoSelected(entry);
+ } else {
+ onSongSelected(entry, false, true, true, false);
+ }
+ }
}
@Override
- public void refresh(boolean refresh) {
- context.onNewIntent(context.getIntent());
+ protected List<MusicDirectory.Entry> getSelectedEntries() {
+ List<Serializable> selected = adapter.getSelected();
+ List<MusicDirectory.Entry> selectedMedia = new ArrayList<>();
+ for(Serializable ser: selected) {
+ if(ser instanceof MusicDirectory.Entry) {
+ selectedMedia.add((MusicDirectory.Entry) ser);
+ }
+ }
+
+ return selectedMedia;
}
public void search(final String query, final boolean autoplay) {
@@ -200,9 +180,6 @@ public class SearchFragment extends SubsonicFragment {
}
currentQuery = query;
- mergeAdapter = new MergeAdapter();
- list.setAdapter(mergeAdapter);
-
BackgroundTask<SearchResult> task = new TabBackgroundTask<SearchResult>(this) {
@Override
protected SearchResult doInBackground() throws Throwable {
@@ -214,7 +191,7 @@ public class SearchFragment extends SubsonicFragment {
@Override
protected void done(SearchResult result) {
searchResult = result;
- populateList();
+ recyclerView.setAdapter(adapter = new SearchAdapter(context, searchResult, getImageLoader(), largeAlbums, SearchFragment.this));
if (autoplay) {
autoplay(query);
}
@@ -224,82 +201,6 @@ public class SearchFragment extends SubsonicFragment {
task.execute();
}
- public void populateList() {
- mergeAdapter = new MergeAdapter();
-
- if (searchResult != null) {
- List<Artist> artists = searchResult.getArtists();
- if (!artists.isEmpty()) {
- mergeAdapter.addView(artistsHeading);
- List<Artist> displayedArtists = new ArrayList<Artist>(artists.subList(0, Math.min(DEFAULT_ARTISTS, artists.size())));
- artistAdapter = new ArtistAdapter(context, displayedArtists);
- mergeAdapter.addAdapter(artistAdapter);
- if (artists.size() > DEFAULT_ARTISTS) {
- moreArtistsAdapter = mergeAdapter.addView(moreArtistsButton, true);
- }
- }
-
- List<MusicDirectory.Entry> albums = searchResult.getAlbums();
- if (!albums.isEmpty()) {
- mergeAdapter.addView(albumsHeading);
- List<MusicDirectory.Entry> displayedAlbums = new ArrayList<MusicDirectory.Entry>(albums.subList(0, Math.min(DEFAULT_ALBUMS, albums.size())));
- albumAdapter = new EntryAdapter(context, getImageLoader(), displayedAlbums, false);
- mergeAdapter.addAdapter(albumAdapter);
- if (albums.size() > DEFAULT_ALBUMS) {
- moreAlbumsAdapter = mergeAdapter.addView(moreAlbumsButton, true);
- }
- }
-
- List<MusicDirectory.Entry> songs = searchResult.getSongs();
- if (!songs.isEmpty()) {
- mergeAdapter.addView(songsHeading);
- List<MusicDirectory.Entry> displayedSongs = new ArrayList<MusicDirectory.Entry>(songs.subList(0, Math.min(DEFAULT_SONGS, songs.size())));
- songAdapter = new EntryAdapter(context, getImageLoader(), displayedSongs, false);
- mergeAdapter.addAdapter(songAdapter);
- if (songs.size() > DEFAULT_SONGS) {
- moreSongsAdapter = mergeAdapter.addView(moreSongsButton, true);
- }
- }
-
- boolean empty = searchResult.getArtists().isEmpty() && searchResult.getAlbums().isEmpty() && searchResult.getSongs().isEmpty();
- if(empty) {
- setEmpty(true);
- }
- }
-
- list.setAdapter(mergeAdapter);
- }
-
- private void expandArtists() {
- artistAdapter.clear();
- for (Artist artist : searchResult.getArtists()) {
- artistAdapter.add(artist);
- }
- artistAdapter.notifyDataSetChanged();
- mergeAdapter.removeAdapter(moreArtistsAdapter);
- mergeAdapter.notifyDataSetChanged();
- }
-
- private void expandAlbums() {
- albumAdapter.clear();
- for (MusicDirectory.Entry album : searchResult.getAlbums()) {
- albumAdapter.add(album);
- }
- albumAdapter.notifyDataSetChanged();
- mergeAdapter.removeAdapter(moreAlbumsAdapter);
- mergeAdapter.notifyDataSetChanged();
- }
-
- private void expandSongs() {
- songAdapter.clear();
- for (MusicDirectory.Entry song : searchResult.getSongs()) {
- songAdapter.add(song);
- }
- songAdapter.notifyDataSetChanged();
- mergeAdapter.removeAdapter(moreSongsAdapter);
- mergeAdapter.notifyDataSetChanged();
- }
-
private void onArtistSelected(Artist artist, boolean autoplay) {
SubsonicFragment fragment = new SelectDirectoryFragment();
Bundle args = new Bundle();
diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/SelectArtistFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SelectArtistFragment.java
index 5488c95b..a9d5afd6 100644
--- a/app/src/main/java/github/daneren2005/dsub/fragments/SelectArtistFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/SelectArtistFragment.java
@@ -11,10 +11,11 @@ import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
import android.widget.LinearLayout;
-import android.widget.TextView;
+
import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.adapter.ArtistAdapter;
+import github.daneren2005.dsub.adapter.SectionAdapter;
import github.daneren2005.dsub.domain.Artist;
import github.daneren2005.dsub.domain.Indexes;
import github.daneren2005.dsub.domain.MusicDirectory;
@@ -23,19 +24,16 @@ import github.daneren2005.dsub.service.MusicService;
import github.daneren2005.dsub.util.Constants;
import github.daneren2005.dsub.util.ProgressListener;
import github.daneren2005.dsub.util.Util;
-import github.daneren2005.dsub.adapter.ArtistAdapter;
+import github.daneren2005.dsub.view.UpdateView;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
-public class SelectArtistFragment extends SelectListFragment<Artist> {
+public class SelectArtistFragment extends SelectRecyclerFragment<Artist> implements ArtistAdapter.OnMusicFolderChanged {
private static final String TAG = SelectArtistFragment.class.getSimpleName();
private static final int MENU_GROUP_MUSIC_FOLDER = 10;
- private View folderButtonParent;
- private View folderButton;
- private TextView folderName;
private List<MusicFolder> musicFolders = null;
private List<MusicDirectory.Entry> entries;
private String groupId;
@@ -75,110 +73,44 @@ public class SelectArtistFragment extends SelectListFragment<Artist> {
}
}
- folderButton = null;
super.onCreateView(inflater, container, bundle);
-
- if("4.4.2".equals(Build.VERSION.RELEASE)) {
- listView.setFastScrollAlwaysVisible(true);
- }
-
- if(objects != null && currentTask == null) {
- if (Util.isOffline(context) || Util.isTagBrowsing(context) || groupId != null) {
- folderButton.setVisibility(View.GONE);
- }
- setMusicFolders();
- }
return rootView;
}
@Override
- public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) {
- super.onCreateContextMenu(menu, view, menuInfo);
-
- AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
- Object entry = listView.getItemAtPosition(info.position);
-
- if (entry instanceof Artist) {
- onCreateContextMenu(menu, view, menuInfo, entry);
- } else if (info.position == 0) {
- String musicFolderId = Util.getSelectedMusicFolderId(context);
- MenuItem menuItem = menu.add(MENU_GROUP_MUSIC_FOLDER, -1, 0, R.string.select_artist_all_folders);
- if (musicFolderId == null) {
- menuItem.setChecked(true);
- }
- if (musicFolders != null) {
- for (int i = 0; i < musicFolders.size(); i++) {
- MusicFolder musicFolder = musicFolders.get(i);
- menuItem = menu.add(MENU_GROUP_MUSIC_FOLDER, i, i + 1, musicFolder.getName());
- if (musicFolder.getId().equals(musicFolderId)) {
- menuItem.setChecked(true);
- }
- }
- }
- menu.setGroupCheckable(MENU_GROUP_MUSIC_FOLDER, true, true);
- }
-
+ public void onCreateContextMenu(Menu menu, MenuInflater menuInflater, UpdateView<Artist> updateView, Artist item) {
+ onCreateContextMenuSupport(menu, menuInflater, updateView, item);
recreateContextMenu(menu);
}
@Override
- public boolean onContextItemSelected(MenuItem menuItem) {
- if(menuItem.getGroupId() != getSupportTag()) {
- return false;
- }
-
- AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo();
- Artist artist = (Artist) listView.getItemAtPosition(info.position);
-
- if (artist != null) {
- return onContextItemSelected(menuItem, artist);
- } else if (info.position == 0) {
- MusicFolder selectedFolder = menuItem.getItemId() == -1 ? null : musicFolders.get(menuItem.getItemId());
- String musicFolderId = selectedFolder == null ? null : selectedFolder.getId();
- String musicFolderName = selectedFolder == null ? context.getString(R.string.select_artist_all_folders)
- : selectedFolder.getName();
- Util.setSelectedMusicFolderId(context, musicFolderId);
- folderName.setText(musicFolderName);
- context.invalidate();
- }
-
- return true;
+ public boolean onContextItemSelected(MenuItem menuItem, UpdateView<Artist> updateView, Artist item) {
+ return onContextItemSelected(menuItem, item);
}
@Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- if (view == folderButtonParent) {
- selectFolder();
- } else {
- Artist artist = (Artist) parent.getItemAtPosition(position);
-
- SubsonicFragment fragment;
- if((Util.isFirstLevelArtist(context) || Util.isOffline(context) || Util.isTagBrowsing(context)) || "root".equals(artist.getId()) || groupId != null) {
- fragment = new SelectDirectoryFragment();
- Bundle args = new Bundle();
- args.putString(Constants.INTENT_EXTRA_NAME_ID, artist.getId());
- args.putString(Constants.INTENT_EXTRA_NAME_NAME, artist.getName());
- if ("root".equals(artist.getId())) {
- args.putSerializable(Constants.FRAGMENT_LIST, (Serializable) entries);
- }
- args.putBoolean(Constants.INTENT_EXTRA_NAME_ARTIST, true);
- fragment.setArguments(args);
- } else {
- fragment = new SelectArtistFragment();
- Bundle args = new Bundle();
- args.putString(Constants.INTENT_EXTRA_NAME_ID, artist.getId());
- args.putString(Constants.INTENT_EXTRA_NAME_NAME, artist.getName());
- fragment.setArguments(args);
+ public void onItemClicked(Artist artist) {
+ SubsonicFragment fragment;
+ if((Util.isFirstLevelArtist(context) || Util.isOffline(context) || Util.isTagBrowsing(context)) || "root".equals(artist.getId()) || groupId != null) {
+ fragment = new SelectDirectoryFragment();
+ Bundle args = new Bundle();
+ args.putString(Constants.INTENT_EXTRA_NAME_ID, artist.getId());
+ args.putString(Constants.INTENT_EXTRA_NAME_NAME, artist.getName());
+ if ("root".equals(artist.getId())) {
+ args.putSerializable(Constants.FRAGMENT_LIST, (Serializable) entries);
}
-
- replaceFragment(fragment);
+ args.putBoolean(Constants.INTENT_EXTRA_NAME_ARTIST, true);
+ fragment.setArguments(args);
+ } else {
+ fragment = new SelectArtistFragment();
+ Bundle args = new Bundle();
+ args.putString(Constants.INTENT_EXTRA_NAME_ID, artist.getId());
+ args.putString(Constants.INTENT_EXTRA_NAME_NAME, artist.getName());
+ fragment.setArguments(args);
}
- }
- @Override
- public void onFinishRefresh() {
- setMusicFolders();
+ replaceFragment(fragment);
}
@Override
@@ -215,9 +147,8 @@ public class SelectArtistFragment extends SelectListFragment<Artist> {
}
@Override
- public ArrayAdapter getAdapter(List<Artist> objects) {
- createMusicFolderButton();
- return new ArtistAdapter(context, objects);
+ public SectionAdapter getAdapter(List<Artist> objects) {
+ return new ArtistAdapter(context, objects, musicFolders, this, this);
}
@Override
@@ -236,12 +167,12 @@ public class SelectArtistFragment extends SelectListFragment<Artist> {
String musicFolderId = Util.getSelectedMusicFolderId(context);
Indexes indexes = musicService.getIndexes(musicFolderId, refresh, context, listener);
- artists = new ArrayList<Artist>(indexes.getShortcuts().size() + indexes.getArtists().size());
+ artists = new ArrayList<>(indexes.getShortcuts().size() + indexes.getArtists().size());
artists.addAll(indexes.getShortcuts());
artists.addAll(indexes.getArtists());
entries = indexes.getEntries();
} else {
- artists = new ArrayList<Artist>();
+ artists = new ArrayList<>();
MusicDirectory dir = musicService.getMusicDirectory(groupId, groupName, refresh, context, listener);
for(MusicDirectory.Entry entry: dir.getChildren(true, false)) {
Artist artist = new Artist();
@@ -251,7 +182,7 @@ public class SelectArtistFragment extends SelectListFragment<Artist> {
artists.add(artist);
}
- entries = new ArrayList<MusicDirectory.Entry>();
+ entries = new ArrayList<>();
entries.addAll(dir.getChildren(false, true));
if(!entries.isEmpty()) {
Artist root = new Artist();
@@ -270,32 +201,14 @@ public class SelectArtistFragment extends SelectListFragment<Artist> {
return groupId == null ? R.string.button_bar_browse : 0;
}
- private void createMusicFolderButton() {
- if(folderButton == null) {
- folderButtonParent = context.getLayoutInflater().inflate(R.layout.select_artist_header, listView, false);
- folderName = (TextView) folderButtonParent.findViewById(R.id.select_artist_folder_2);
- listView.addHeaderView(folderButtonParent);
- folderButton = folderButtonParent.findViewById(R.id.select_artist_folder);
- }
-
- if (Util.isOffline(context) || Util.isTagBrowsing(context) || musicFolders == null) {
- folderButton.setVisibility(View.GONE);
- } else {
- folderButton.setVisibility(View.VISIBLE);
- }
- }
-
@Override
public void setEmpty(boolean empty) {
super.setEmpty(empty);
if(empty && !Util.isOffline(context)) {
- createMusicFolderButton();
- setMusicFolders();
-
objects.clear();
- listView.setAdapter(new ArtistAdapter(context, objects));
- listView.setVisibility(View.VISIBLE);
+ recyclerView.setAdapter(new ArtistAdapter(context, objects, musicFolders, this, this));
+ recyclerView.setVisibility(View.VISIBLE);
View view = rootView.findViewById(R.id.tab_progress);
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) view.getLayoutParams();
@@ -305,29 +218,19 @@ public class SelectArtistFragment extends SelectListFragment<Artist> {
}
}
- private void setMusicFolders() {
- // Display selected music folder
- if (musicFolders != null) {
- String musicFolderId = Util.getSelectedMusicFolderId(context);
- if (musicFolderId == null) {
- folderName.setText(R.string.select_artist_all_folders);
- } else {
- for (MusicFolder musicFolder : musicFolders) {
- if (musicFolder.getId().equals(musicFolderId)) {
- folderName.setText(musicFolder.getName());
- break;
- }
- }
- }
- }
- }
-
- private void selectFolder() {
- folderButton.showContextMenu();
- }
-
private void toggleFirstLevelArtist() {
Util.toggleFirstLevelArtist(context);
context.invalidateOptionsMenu();
}
+
+ @Override
+ public void onMusicFolderChanged(MusicFolder selectedFolder) {
+ String startMusicFolderId = Util.getSelectedMusicFolderId(context);
+ String musicFolderId = selectedFolder == null ? null : selectedFolder.getId();
+
+ if(!Util.equals(startMusicFolderId, musicFolderId)) {
+ Util.setSelectedMusicFolderId(context, musicFolderId);
+ context.invalidate();
+ }
+ }
}
diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/SelectBookmarkFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SelectBookmarkFragment.java
index c71d99f6..d992319b 100644
--- a/app/src/main/java/github/daneren2005/dsub/fragments/SelectBookmarkFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/SelectBookmarkFragment.java
@@ -19,43 +19,40 @@
package github.daneren2005.dsub.fragments;
import android.view.ContextMenu;
+import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import github.daneren2005.dsub.R;
-import github.daneren2005.dsub.activity.DownloadActivity;
+import github.daneren2005.dsub.adapter.SectionAdapter;
import github.daneren2005.dsub.domain.Bookmark;
import github.daneren2005.dsub.domain.MusicDirectory;
import github.daneren2005.dsub.service.DownloadService;
import github.daneren2005.dsub.service.MusicService;
+import github.daneren2005.dsub.util.MenuUtil;
import github.daneren2005.dsub.util.ProgressListener;
import github.daneren2005.dsub.util.SilentBackgroundTask;
import github.daneren2005.dsub.util.Util;
import github.daneren2005.dsub.adapter.BookmarkAdapter;
+import github.daneren2005.dsub.view.UpdateView;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
-public class SelectBookmarkFragment extends SelectListFragment<MusicDirectory.Entry> {
+public class SelectBookmarkFragment extends SelectRecyclerFragment<MusicDirectory.Entry> {
private static final String TAG = SelectBookmarkFragment.class.getSimpleName();
@Override
- public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) {
- super.onCreateContextMenu(menu, view, menuInfo);
-
- MenuInflater inflater = context.getMenuInflater();
- inflater.inflate(R.menu.select_bookmark_context, menu);
-
- hideMenuItems(menu, (AdapterView.AdapterContextMenuInfo) menuInfo);
+ public void onCreateContextMenu(Menu menu, MenuInflater menuInflater, UpdateView<MusicDirectory.Entry> updateView, MusicDirectory.Entry item) {
+ menuInflater.inflate(R.menu.select_bookmark_context, menu);
+ MenuUtil.hideMenuItems(context, menu);
}
@Override
- public boolean onContextItemSelected(MenuItem menuItem) {
- AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo();
- MusicDirectory.Entry bookmark = objects.get(info.position);
-
+ public boolean onContextItemSelected(MenuItem menuItem, UpdateView<MusicDirectory.Entry> updateView, MusicDirectory.Entry bookmark) {
switch(menuItem.getItemId()) {
case R.id.bookmark_menu_info:
displayBookmarkInfo(bookmark);
@@ -64,12 +61,8 @@ public class SelectBookmarkFragment extends SelectListFragment<MusicDirectory.En
deleteBookmark(bookmark, adapter);
return true;
}
-
- if(onContextItemSelected(menuItem, bookmark)) {
- return true;
- }
- return true;
+ return onContextItemSelected(menuItem, bookmark);
}
@Override
@@ -78,8 +71,8 @@ public class SelectBookmarkFragment extends SelectListFragment<MusicDirectory.En
}
@Override
- public ArrayAdapter getAdapter(List<MusicDirectory.Entry> bookmarks) {
- return new BookmarkAdapter(context, bookmarks);
+ public SectionAdapter getAdapter(List<MusicDirectory.Entry> bookmarks) {
+ return new BookmarkAdapter(context, bookmarks, this);
}
@Override
@@ -93,13 +86,12 @@ public class SelectBookmarkFragment extends SelectListFragment<MusicDirectory.En
}
@Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ public void onItemClicked(final MusicDirectory.Entry bookmark) {
final DownloadService downloadService = getDownloadService();
if(downloadService == null) {
return;
}
- final MusicDirectory.Entry bookmark = (MusicDirectory.Entry) parent.getItemAtPosition(position);
new SilentBackgroundTask<Void>(context) {
@Override
protected Void doInBackground() throws Throwable {
@@ -110,22 +102,42 @@ public class SelectBookmarkFragment extends SelectListFragment<MusicDirectory.En
@Override
protected void done(Void result) {
- Util.startActivityWithoutTransition(context, DownloadActivity.class);
+ context.openNowPlaying();
}
}.execute();
}
-
+
private void displayBookmarkInfo(final MusicDirectory.Entry entry) {
Bookmark bookmark = entry.getBookmark();
- String comment = bookmark.getComment();
- if(comment == null) {
- comment = "";
+ List<Integer> headers = new ArrayList<>();
+ List<String> details = new ArrayList<>();
+
+ headers.add(R.string.details_song);
+ details.add(entry.getTitle());
+
+ if(entry.getArtist() != null) {
+ headers.add(R.string.details_artist);
+ details.add(entry.getArtist());
+ }
+ if(entry.getAlbum() != null) {
+ headers.add(R.string.details_album);
+ details.add(entry.getAlbum());
+ }
+
+ headers.add(R.string.details_position);
+ details.add(Util.formatDuration(bookmark.getPosition() / 1000));
+
+ headers.add(R.string.details_created);
+ details.add(Util.formatDate(bookmark.getCreated()));
+
+ headers.add(R.string.details_updated);
+ details.add(Util.formatDate(bookmark.getChanged()));
+
+ if(bookmark.getComment() != null) {
+ headers.add(R.string.details_comments);
+ details.add(bookmark.getComment());
}
- String msg = context.getResources().getString(R.string.bookmark_details,
- entry.getTitle(), Util.formatDuration(bookmark.getPosition() / 1000),
- Util.formatDate(bookmark.getCreated()), Util.formatDate(bookmark.getChanged()), comment);
-
- Util.info(context, R.string.bookmark_details_title, msg, false);
+ Util.showDetailsDialog(context, R.string.bookmark_details_title, headers, details);
}
}
diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java
index 841a6369..5fdd5c5f 100644
--- a/app/src/main/java/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java
@@ -1,7 +1,7 @@
package github.daneren2005.dsub.fragments;
import android.annotation.TargetApi;
-import android.app.AlertDialog;
+import android.support.v7.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
@@ -9,12 +9,14 @@ import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.widget.SwipeRefreshLayout;
+import android.support.v7.widget.GridLayoutManager;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
import android.text.Html;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.method.LinkMovementMethod;
import android.util.Log;
-import android.view.ContextMenu;
import android.view.Display;
import android.view.LayoutInflater;
import android.view.Menu;
@@ -22,31 +24,27 @@ import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.AdapterView;
-import android.widget.BaseAdapter;
-import android.widget.GridView;
import android.widget.ImageButton;
import android.widget.ImageView;
-import android.widget.ListAdapter;
-import android.widget.ListView;
import android.widget.RatingBar;
import android.widget.RelativeLayout;
import android.widget.TextView;
import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.adapter.EntryInfiniteGridAdapter;
+import github.daneren2005.dsub.adapter.EntryGridAdapter;
+import github.daneren2005.dsub.adapter.SectionAdapter;
import github.daneren2005.dsub.domain.ArtistInfo;
import github.daneren2005.dsub.domain.MusicDirectory;
import github.daneren2005.dsub.domain.ServerInfo;
import github.daneren2005.dsub.domain.Share;
import github.daneren2005.dsub.service.DownloadService;
+import github.daneren2005.dsub.util.DrawableTint;
import github.daneren2005.dsub.util.ImageLoader;
-import github.daneren2005.dsub.adapter.AlbumGridAdapter;
-import github.daneren2005.dsub.adapter.EntryAdapter;
import java.io.Serializable;
import java.util.Iterator;
import java.util.List;
-import github.daneren2005.dsub.activity.DownloadActivity;
import github.daneren2005.dsub.domain.PodcastEpisode;
import github.daneren2005.dsub.service.MusicService;
import github.daneren2005.dsub.service.MusicServiceFactory;
@@ -59,9 +57,9 @@ import github.daneren2005.dsub.util.SilentBackgroundTask;
import github.daneren2005.dsub.util.TabBackgroundTask;
import github.daneren2005.dsub.util.UserUtil;
import github.daneren2005.dsub.util.Util;
-import github.daneren2005.dsub.adapter.AlbumListAdapter;
-import github.daneren2005.dsub.view.HeaderGridView;
+import github.daneren2005.dsub.view.GridSpacingDecoration;
import github.daneren2005.dsub.view.MyLeadingMarginSpan2;
+import github.daneren2005.dsub.view.UpdateView;
import java.util.ArrayList;
import java.util.Arrays;
@@ -70,17 +68,14 @@ import java.util.Set;
import static github.daneren2005.dsub.domain.MusicDirectory.Entry;
-public class SelectDirectoryFragment extends SubsonicFragment implements AdapterView.OnItemClickListener {
+public class SelectDirectoryFragment extends SubsonicFragment implements SectionAdapter.OnItemClickedListener<Entry> {
private static final String TAG = SelectDirectoryFragment.class.getSimpleName();
- private GridView albumList;
- private ListView entryList;
+ private RecyclerView recyclerView;
+ private EntryGridAdapter entryGridAdapter;
private Boolean licenseValid;
- private EntryAdapter entryAdapter;
private List<Entry> albums;
private List<Entry> entries;
- private boolean albumContext = false;
- private boolean addAlbumHeader = false;
private LoadTask currentTask;
private ArtistInfo artistInfo;
private String artistInfoDelayed;
@@ -114,7 +109,6 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter
super.onCreate(bundle);
if(bundle != null) {
entries = (List<Entry>) bundle.getSerializable(Constants.FRAGMENT_LIST);
- albums = (List<Entry>) bundle.getSerializable(Constants.FRAGMENT_LIST2);
artistInfo = (ArtistInfo) bundle.getSerializable(Constants.FRAGMENT_EXTRA);
restoredInstance = true;
}
@@ -172,28 +166,35 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter
refreshLayout = (SwipeRefreshLayout) rootView.findViewById(R.id.refresh_layout);
refreshLayout.setOnRefreshListener(this);
- entryList = (ListView) rootView.findViewById(R.id.select_album_entries);
- entryList.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
- entryList.setOnItemClickListener(this);
- setupScrollList(entryList);
-
if(Util.getPreferences(context).getBoolean(Constants.PREFERENCES_KEY_LARGE_ALBUM_ART, true)) {
largeAlbums = true;
}
- if(albumListType == null || "starred".equals(albumListType) || !largeAlbums) {
- albumList = (GridView) inflater.inflate(R.layout.unscrollable_grid_view, entryList, false);
- addAlbumHeader = true;
- } else {
- ViewGroup rootGroup = (ViewGroup) rootView.findViewById(R.id.select_album_layout);
- albumList = (GridView) inflater.inflate(R.layout.grid_view, rootGroup, false);
- rootGroup.removeView(entryList);
- rootGroup.addView(albumList);
+ recyclerView = (RecyclerView) rootView.findViewById(R.id.select_album_entries);
+ recyclerView.setHasFixedSize(true);
+ setupScrollList(recyclerView);
- setupScrollList(albumList);
+ if(largeAlbums) {
+ final int columns = context.getResources().getInteger(R.integer.Grid_Columns);
+ GridLayoutManager gridLayoutManager = new GridLayoutManager(context, columns);
+ gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
+ @Override
+ public int getSpanSize(int position) {
+ int viewType = entryGridAdapter.getItemViewType(position);
+ if(viewType == EntryGridAdapter.VIEW_TYPE_SONG || viewType == EntryGridAdapter.VIEW_TYPE_HEADER || viewType == EntryInfiniteGridAdapter.VIEW_TYPE_LOADING) {
+ return columns;
+ } else {
+ return 1;
+ }
+ }
+ });
+ recyclerView.addItemDecoration(new GridSpacingDecoration());
+ recyclerView.setLayoutManager(gridLayoutManager);
+ } else {
+ LinearLayoutManager layoutManager = new LinearLayoutManager(context);
+ layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
+ recyclerView.setLayoutManager(layoutManager);
}
- registerForContextMenu(entryList);
- setupAlbumList();
if(entries == null) {
if(primaryFragment || secondaryFragment) {
@@ -273,36 +274,6 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
- case R.id.menu_play_now:
- playNow(false, false);
- return true;
- case R.id.menu_play_last:
- playNow(false, true);
- return true;
- case R.id.menu_play_next:
- playNow(false, true, true);
- return true;
- case R.id.menu_shuffle:
- playNow(true, false);
- return true;
- case R.id.menu_download:
- downloadBackground(false);
- selectAll(false, false);
- return true;
- case R.id.menu_cache:
- downloadBackground(true);
- selectAll(false, false);
- return true;
- case R.id.menu_delete:
- delete();
- selectAll(false, false);
- return true;
- case R.id.menu_add_playlist:
- if(getSelectedSongs().isEmpty()) {
- selectAll(true, false);
- }
- addToPlaylist(getSelectedSongs());
- return true;
case R.id.menu_remove_playlist:
removeFromPlaylist(playlistId, playlistName, getSelectedIndexes());
return true;
@@ -331,32 +302,8 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter
}
@Override
- public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) {
- super.onCreateContextMenu(menu, view, menuInfo);
-
- AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
-
- Entry entry;
- if(view.getId() == R.id.select_album_entries) {
- if(info.position == 0) {
- return;
- }
- entry = (Entry) entryList.getItemAtPosition(info.position);
- // When List has Grid embedded in header, this is called against the header as well
- if(entry != null) {
- albumContext = false;
- }
- } else {
- entry = (Entry) albumList.getItemAtPosition(info.position);
- albumContext = true;
- }
-
- // Don't try to display a context menu if error here
- if(entry == null) {
- return;
- }
-
- onCreateContextMenu(menu, view, menuInfo, entry);
+ public void onCreateContextMenu(Menu menu, MenuInflater menuInflater, UpdateView updateView, Entry entry) {
+ onCreateContextMenuSupport(menu, menuInflater, updateView, entry);
if(!entry.isVideo() && !Util.isOffline(context) && (playlistId == null || !playlistOwner) && (podcastId == null || Util.isOffline(context) && podcastId != null)) {
menu.removeItem(R.id.song_menu_remove_playlist);
}
@@ -378,88 +325,70 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter
recreateContextMenu(menu);
}
-
@Override
- public boolean onContextItemSelected(MenuItem menuItem) {
- if(menuItem.getGroupId() != getSupportTag()) {
- return false;
- }
-
- AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo();
- Object selectedItem;
- int headers = entryList.getHeaderViewsCount();
- if(albumContext) {
- selectedItem = albumList.getItemAtPosition(info.position);
- } else {
- if(info.position == 0) {
- return false;
- }
- selectedItem = entries.get(info.position - headers);
- }
-
- if(Util.getPreferences(context).getBoolean(Constants.PREFERENCES_KEY_PLAY_NOW_AFTER, false) && menuItem.getItemId() == R.id.song_menu_play_now) {
- List<Entry> songs = new ArrayList<Entry>();
- Iterator it = entries.listIterator(info.position - headers);
- while(it.hasNext()) {
- songs.add((Entry) it.next());
- }
-
- playNow(songs);
- return true;
- }
-
- if(onContextItemSelected(menuItem, selectedItem)) {
+ public boolean onContextItemSelected(MenuItem menuItem, UpdateView<Entry> updateView, Entry entry) {
+ if(onContextItemSelected(menuItem, entry)) {
return true;
}
switch (menuItem.getItemId()) {
case R.id.song_menu_remove_playlist:
- removeFromPlaylist(playlistId, playlistName, Arrays.<Integer>asList(info.position - headers));
+ removeFromPlaylist(playlistId, playlistName, Arrays.<Integer>asList(entries.indexOf(entry)));
break;
case R.id.song_menu_server_download:
- downloadPodcastEpisode((PodcastEpisode)selectedItem);
+ downloadPodcastEpisode((PodcastEpisode) entry);
break;
case R.id.song_menu_server_delete:
- deletePodcastEpisode((PodcastEpisode)selectedItem);
+ deletePodcastEpisode((PodcastEpisode) entry);
break;
}
-
+
return true;
}
@Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- if (position >= 0) {
- Entry entry = (Entry) parent.getItemAtPosition(position);
- if (entry.isDirectory()) {
- SubsonicFragment fragment = new SelectDirectoryFragment();
- Bundle args = new Bundle();
- args.putString(Constants.INTENT_EXTRA_NAME_ID, entry.getId());
- args.putString(Constants.INTENT_EXTRA_NAME_NAME, entry.getTitle());
- args.putSerializable(Constants.INTENT_EXTRA_NAME_DIRECTORY, entry);
- if ("newest".equals(albumListType)) {
- args.putBoolean(Constants.INTENT_EXTRA_REFRESH_LISTINGS, true);
- }
- if(entry.getArtist() == null && entry.getParent() == null) {
- args.putBoolean(Constants.INTENT_EXTRA_NAME_ARTIST, true);
- }
- fragment.setArguments(args);
+ public void onItemClicked(Entry entry) {
+ if (entry.isDirectory()) {
+ SubsonicFragment fragment = new SelectDirectoryFragment();
+ Bundle args = new Bundle();
+ args.putString(Constants.INTENT_EXTRA_NAME_ID, entry.getId());
+ args.putString(Constants.INTENT_EXTRA_NAME_NAME, entry.getTitle());
+ args.putSerializable(Constants.INTENT_EXTRA_NAME_DIRECTORY, entry);
+ if ("newest".equals(albumListType)) {
+ args.putBoolean(Constants.INTENT_EXTRA_REFRESH_LISTINGS, true);
+ }
+ if(!entry.isAlbum()) {
+ args.putBoolean(Constants.INTENT_EXTRA_NAME_ARTIST, true);
+ }
+ fragment.setArguments(args);
+
+ replaceFragment(fragment, true);
+ } else if (entry.isVideo()) {
+ playVideo(entry);
+ } else if(entry instanceof PodcastEpisode) {
+ String status = ((PodcastEpisode)entry).getStatus();
+ if("error".equals(status)) {
+ Util.toast(context, R.string.select_podcasts_error);
+ return;
+ } else if(!"completed".equals(status)) {
+ Util.toast(context, R.string.select_podcasts_skipped);
+ return;
+ }
- replaceFragment(fragment, true);
- } else if (entry.isVideo()) {
- playVideo(entry);
- } else if(entry instanceof PodcastEpisode) {
- String status = ((PodcastEpisode)entry).getStatus();
- if("error".equals(status)) {
- Util.toast(context, R.string.select_podcasts_error);
- return;
- } else if(!"completed".equals(status)) {
- Util.toast(context, R.string.select_podcasts_skipped);
- return;
+ playNow(Arrays.asList(entry));
+ } else {
+ List<Entry> songs = new ArrayList<Entry>();
+
+ if(Util.getPreferences(context).getBoolean(Constants.PREFERENCES_KEY_PLAY_NOW_AFTER, false)) {
+ Iterator it = entries.listIterator(entries.indexOf(entry));
+ while(it.hasNext()) {
+ songs.add((Entry) it.next());
}
-
- playNow(Arrays.asList(entry));
+ } else {
+ songs.add(entry);
}
+
+ playNow(songs);
}
}
@@ -478,8 +407,8 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter
if(currentTask != null) {
currentTask.cancel();
}
-
- entryList.setVisibility(View.INVISIBLE);
+
+ recyclerView.setVisibility(View.INVISIBLE);
if (playlistId != null) {
getPlaylist(playlistId, playlistName, refresh);
} else if(podcastId != null) {
@@ -491,7 +420,7 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter
getShare(share, refresh);
}
} else if (albumListType != null) {
- getAlbumList(albumListType, albumListSize);
+ getAlbumList(albumListType, albumListSize, refresh);
} else {
if(showAll) {
getRecursiveMusicDirectory(id, name, refresh);
@@ -626,7 +555,7 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter
}.execute();
}
- private void getAlbumList(final String albumListType, final int size) {
+ private void getAlbumList(final String albumListType, final int size, final boolean refresh) {
if ("newest".equals(albumListType)) {
setTitle(R.string.main_albums_newest);
} else if ("random".equals(albumListType)) {
@@ -652,7 +581,7 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter
if ("starred".equals(albumListType)) {
result = service.getStarredList(context, this);
} else if(("genres".equals(albumListType) && ServerInfo.checkServerVersion(context, "1.10.0")) || "years".equals(albumListType)) {
- result = service.getAlbumList(albumListType, albumListExtra, size, 0, context, this);
+ result = service.getAlbumList(albumListType, albumListExtra, size, 0, refresh, context, this);
if(result.getChildrenSize() == 0 && "genres".equals(albumListType)) {
SelectDirectoryFragment.this.albumListType = "genres-songs";
result = service.getSongsByGenre(albumListExtra, size, 0, context, this);
@@ -660,7 +589,7 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter
} else if("genres".equals(albumListType) || "genres-songs".equals(albumListType)) {
result = service.getSongsByGenre(albumListExtra, size, 0, context, this);
} else {
- result = service.getAlbumList(albumListType, size, 0, context, this);
+ result = service.getAlbumList(albumListType, size, 0, refresh, context, this);
}
return result;
}
@@ -686,11 +615,7 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter
licenseValid = musicService.isLicenseValid(context, this);
albums = dir.getChildren(true, false);
- if(largeAlbums) {
- entries = dir.getChildren(false, true);
- } else {
- entries = dir.getChildren();
- }
+ entries = dir.getChildren();
// This isn't really an artist if no albums on it!
if(albums.size() == 0) {
@@ -727,121 +652,127 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter
}
}
+ @Override
+ protected SectionAdapter<Entry> getCurrentAdapter() {
+ return entryGridAdapter;
+ }
+
private void finishLoading() {
- // Show header if not album list type and not root and not artist
- // For Subsonic 5.1+ display a header for artists with getArtistInfo data if it exists
- View header = null;
- if(albumListType == null && !"root".equals(id) && (!artist || artistInfo != null || artistInfoDelayed != null)) {
- header = createHeader();
+ boolean validData = !entries.isEmpty() || !albums.isEmpty();
+ if(!validData) {
+ setEmpty(true);
+ }
- if(header != null && artistInfoDelayed != null) {
- final View finalHeader = header.findViewById(R.id.select_album_header);
- final View headerProgress = header.findViewById(R.id.header_progress);
+ if(validData) {
+ recyclerView.setVisibility(View.VISIBLE);
+ }
- finalHeader.setVisibility(View.INVISIBLE);
- headerProgress.setVisibility(View.VISIBLE);
+ if(albumListType == null || "starred".equals(albumListType)) {
+ entryGridAdapter = new EntryGridAdapter(context, entries, getImageLoader(), largeAlbums);
+ entryGridAdapter.setRemoveFromPlaylist(playlistId != null);
+ } else {
+ entryGridAdapter = new EntryInfiniteGridAdapter(context, entries, getImageLoader(), largeAlbums);
- new SilentBackgroundTask<Void>(context) {
- @Override
- protected Void doInBackground() throws Throwable {
- MusicService musicService = MusicServiceFactory.getMusicService(context);
- artistInfo = musicService.getArtistInfo(artistInfoDelayed, false, true, context, this);
+ // Setup infinite loading based on scrolling
+ final EntryInfiniteGridAdapter infiniteGridAdapter = (EntryInfiniteGridAdapter) entryGridAdapter;
+ infiniteGridAdapter.setData(albumListType, albumListExtra, albumListSize);
- return null;
- }
+ recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
+ @Override
+ public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
+ super.onScrollStateChanged(recyclerView, newState);
+ }
- @Override
- protected void done(Void result) {
- /*if(albumList instanceof HeaderGridView) {
- HeaderGridView headerGridView = (HeaderGridView) albumList;
- headerGridView.invalidateRowHeight();
- ((BaseAdapter) headerGridView.getAdapter()).notifyDataSetChanged();
- }*/
-
- setupCoverArt(finalHeader);
- setupTextDisplay(finalHeader);
- setupButtonEvents(finalHeader);
-
- finalHeader.setVisibility(View.VISIBLE);
- headerProgress.setVisibility(View.GONE);
+ @Override
+ public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
+ super.onScrolled(recyclerView, dx, dy);
+
+ RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
+ int totalItemCount = layoutManager.getItemCount();
+ int lastVisibleItem;
+ if(layoutManager instanceof GridLayoutManager) {
+ lastVisibleItem = ((GridLayoutManager) layoutManager).findLastVisibleItemPosition();
+ } else if(layoutManager instanceof LinearLayoutManager) {
+ lastVisibleItem = ((LinearLayoutManager) layoutManager).findLastVisibleItemPosition();
+ } else {
+ return;
}
- }.execute();
- }
- // Only add header to entry list if we aren't going recreate album grid as root anyways
- if(header != null && entryList != null && (!addAlbumHeader || entries.size() > 0)) {
- entryList.addHeaderView(header, null, false);
- header = null;
- }
- }
-
- // Needs to be added here, GB crashes if you to try to remove the header view before adapter is set
- if(addAlbumHeader) {
- if(entries.size() > 0 || playlistId != null || podcastId != null) {
- entryList.addHeaderView(albumList);
- } else {
- ViewGroup rootGroup = (ViewGroup) rootView.findViewById(R.id.select_album_layout);
- albumList = (GridView) context.getLayoutInflater().inflate(R.layout.grid_view, rootGroup, false);
- rootGroup.removeView(entryList);
- rootGroup.addView(albumList);
-
- setupScrollList(albumList);
- setupAlbumList();
-
- // This should only not be null for a artist with only albums
- if(header != null) {
- HeaderGridView headerGridView = (HeaderGridView) albumList;
- headerGridView.addHeaderView(header);
+ if(totalItemCount > 0 && lastVisibleItem >= totalItemCount - 2) {
+ infiniteGridAdapter.loadMore();
+ }
}
- }
- addAlbumHeader = false;
+ });
}
-
- boolean validData = !entries.isEmpty() || !albums.isEmpty();
- if(!validData) {
- setEmpty(true);
+ entryGridAdapter.setOnItemClickedListener(this);
+ // Always show artist if this is not a artist we are viewing
+ if(!artist) {
+ entryGridAdapter.setShowArtist(true);
+ }
+ // Podcasts are not checkable
+ if(podcastId != null) {
+ entryGridAdapter.setCheckable(false);
}
- // Always going to have entries in entryAdapter
- entryAdapter = new EntryAdapter(context, getImageLoader(), entries, (podcastId == null));
- ListAdapter listAdapter = entryAdapter;
- // Song-only genre needs to always be entry list + infinite adapter
- if("genres-songs".equals(albumListType)) {
- ViewGroup rootGroup = (ViewGroup) rootView.findViewById(R.id.select_album_layout);
- if(rootGroup.findViewById(R.id.gridview) != null && largeAlbums) {
- rootGroup.removeView(albumList);
- rootGroup.addView(entryList);
- }
- listAdapter = new AlbumListAdapter(context, entryAdapter, albumListType, albumListExtra, albumListSize);
- } else if(albumListType == null || "starred".equals(albumListType)) {
- // Only set standard album adapter if not album list and largeAlbums is true
- if(largeAlbums) {
- albumList.setAdapter(new AlbumGridAdapter(context, getImageLoader(), albums, !artist));
- }
- } else {
- // If album list, use infinite adapters for either depending on whether or not largeAlbums is true
- if(largeAlbums) {
- albumList.setAdapter(new AlbumListAdapter(context, new AlbumGridAdapter(context, getImageLoader(), albums, true), albumListType, albumListExtra, albumListSize));
- } else {
- listAdapter = new AlbumListAdapter(context, entryAdapter, albumListType, albumListExtra, albumListSize);
+ // Show header if not album list type and not root and not artist
+ // For Subsonic 5.1+ display a header for artists with getArtistInfo data if it exists
+ boolean addedHeader = false;
+ if(albumListType == null && !"root".equals(id) && (!artist || artistInfo != null || artistInfoDelayed != null)) {
+ View header = createHeader();
+
+ if(header != null) {
+ if (artistInfoDelayed != null) {
+ final View finalHeader = header.findViewById(R.id.select_album_header);
+ final View headerProgress = header.findViewById(R.id.header_progress);
+
+ finalHeader.setVisibility(View.INVISIBLE);
+ headerProgress.setVisibility(View.VISIBLE);
+
+ new SilentBackgroundTask<Void>(context) {
+ @Override
+ protected Void doInBackground() throws Throwable {
+ MusicService musicService = MusicServiceFactory.getMusicService(context);
+ artistInfo = musicService.getArtistInfo(artistInfoDelayed, false, true, context, this);
+
+ return null;
+ }
+
+ @Override
+ protected void done(Void result) {
+ setupCoverArt(finalHeader);
+ setupTextDisplay(finalHeader);
+ setupButtonEvents(finalHeader);
+
+ finalHeader.setVisibility(View.VISIBLE);
+ headerProgress.setVisibility(View.GONE);
+ }
+ }.execute();
+ }
+
+ entryGridAdapter.setHeader(header);
+ addedHeader = true;
}
}
- entryList.setAdapter(listAdapter);
- if(validData) {
- entryList.setVisibility(View.VISIBLE);
- }
- context.supportInvalidateOptionsMenu();
+ int scrollToPosition = -1;
if(lookupEntry != null) {
for(int i = 0; i < entries.size(); i++) {
if(lookupEntry.equals(entries.get(i).getTitle())) {
- entryList.setSelection(i + entryList.getHeaderViewsCount());
+ scrollToPosition = i;
+ entryGridAdapter.addSelected(entries.get(i));
lookupEntry = null;
break;
}
}
}
+ recyclerView.setAdapter(entryGridAdapter);
+ context.supportInvalidateOptionsMenu();
+
+ if(scrollToPosition != -1) {
+ recyclerView.scrollToPosition(scrollToPosition + (addedHeader ? 1 : 0));
+ }
+
Bundle args = getArguments();
boolean playAll = args.getBoolean(Constants.INTENT_EXTRA_NAME_AUTOPLAY, false);
if (playAll && !restoredInstance) {
@@ -849,169 +780,52 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter
}
}
- private void setupAlbumList() {
- albumList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- Entry entry = (Entry) parent.getItemAtPosition(position);
- SubsonicFragment fragment = new SelectDirectoryFragment();
- Bundle args = new Bundle();
- args.putString(Constants.INTENT_EXTRA_NAME_ID, entry.getId());
- args.putString(Constants.INTENT_EXTRA_NAME_NAME, entry.getTitle());
- args.putSerializable(Constants.INTENT_EXTRA_NAME_DIRECTORY, entry);
- if ("newest".equals(albumListType)) {
- args.putBoolean(Constants.INTENT_EXTRA_REFRESH_LISTINGS, true);
- }
- if(entry.getArtist() == null && entry.getParent() == null) {
- args.putBoolean(Constants.INTENT_EXTRA_NAME_ARTIST, true);
- }
- fragment.setArguments(args);
-
- replaceFragment(fragment, true);
- }
- });
-
- registerForContextMenu(entryList);
- registerForContextMenu(albumList);
- }
-
- private void playNow(final boolean shuffle, final boolean append) {
- playNow(shuffle, append, false);
- }
- private void playNow(final boolean shuffle, final boolean append, final boolean playNext) {
- if(getSelectedSongs().size() > 0) {
- download(append, false, !append, playNext, shuffle);
- selectAll(false, false);
+ @Override
+ protected void playNow(final boolean shuffle, final boolean append, final boolean playNext) {
+ List<Entry> songs = getSelectedEntries();
+ if(!songs.isEmpty()) {
+ download(songs, append, false, !append, playNext, shuffle);
+ entryGridAdapter.clearSelected();
}
else {
playAll(shuffle, append);
}
}
private void playAll(final boolean shuffle, final boolean append) {
- boolean hasSubFolders = false;
- for (int i = 0; i < entryList.getCount(); i++) {
- Entry entry = (Entry) entryList.getItemAtPosition(i);
- if (entry != null && entry.isDirectory()) {
- hasSubFolders = true;
- break;
- }
- }
- if(albums.size() > 0) {
- hasSubFolders = true;
- }
+ boolean hasSubFolders = !albums.isEmpty();
if (hasSubFolders && (id != null || share != null || "starred".equals(albumListType))) {
downloadRecursively(id, false, append, !append, shuffle, false);
} else if(hasSubFolders && albumListType != null) {
downloadRecursively(albums, shuffle, append);
} else {
- selectAll(true, false);
- download(append, false, !append, false, shuffle);
- selectAll(false, false);
- }
- }
-
- private void selectAll(boolean selected, boolean toast) {
- int count = entryList.getCount();
- int selectedCount = 0;
- for (int i = 0; i < count; i++) {
- Entry entry = (Entry) entryList.getItemAtPosition(i);
- if (entry != null && !entry.isDirectory() && !entry.isVideo()) {
- entryList.setItemChecked(i, selected);
- selectedCount++;
- }
- }
-
- // Display toast: N tracks selected / N tracks unselected
- if (toast) {
- int toastResId = selected ? R.string.select_album_n_selected
- : R.string.select_album_n_unselected;
- Util.toast(context, context.getString(toastResId, selectedCount));
+ download(entries, append, false, !append, false, shuffle);
}
}
- private List<Entry> getSelectedSongs() {
- List<Entry> songs = new ArrayList<Entry>(10);
- int count = entryList.getCount();
- for (int i = 0; i < count; i++) {
- if (entryList.isItemChecked(i)) {
- Entry entry = (Entry) entryList.getItemAtPosition(i);
- // Don't try to add directories or 1-starred songs
- if(!entry.isDirectory() && entry.getRating() != 1) {
- songs.add(entry);
- }
- }
- }
- return songs;
- }
-
private List<Integer> getSelectedIndexes() {
+ List<Entry> selected = entryGridAdapter.getSelected();
List<Integer> indexes = new ArrayList<Integer>();
- int count = entryList.getCount();
- int headers = entryList.getHeaderViewsCount();
- for (int i = 0; i < count; i++) {
- if (entryList.isItemChecked(i)) {
- indexes.add(i - headers);
- }
+ for(Entry entry: selected) {
+ indexes.add(entries.indexOf(entry));
}
return indexes;
}
- private void download(final boolean append, final boolean save, final boolean autoplay, final boolean playNext, final boolean shuffle) {
- if (getDownloadService() == null) {
- return;
- }
-
- final List<Entry> songs = getSelectedSongs();
- warnIfStorageUnavailable();
-
- // Conditions for using play now button
- if(!append && !save && autoplay && !playNext && !shuffle) {
- // Call playNow which goes through and tries to use bookmark information
- playNow(songs, playlistName, playlistId);
- return;
- }
-
- LoadingTask<Void> onValid = new LoadingTask<Void>(context) {
- @Override
- protected Void doInBackground() throws Throwable {
- if (!append) {
- getDownloadService().clear();
- }
-
- getDownloadService().download(songs, save, autoplay, playNext, shuffle);
- if (playlistName != null) {
- getDownloadService().setSuggestedPlaylistName(playlistName, playlistId);
- } else {
- getDownloadService().setSuggestedPlaylistName(null, null);
- }
- return null;
- }
-
- @Override
- protected void done(Void result) {
- if (autoplay) {
- Util.startActivityWithoutTransition(context, DownloadActivity.class);
- } else if (save) {
- Util.toast(context,
- context.getResources().getQuantityString(R.plurals.select_album_n_songs_downloading, songs.size(), songs.size()));
- } else if (append) {
- Util.toast(context,
- context.getResources().getQuantityString(R.plurals.select_album_n_songs_added, songs.size(), songs.size()));
- }
- }
- };
-
+ @Override
+ protected void executeOnValid(RecursiveLoader onValid) {
checkLicenseAndTrialPeriod(onValid);
}
- private void downloadBackground(final boolean save) {
+
+ @Override
+ protected void downloadBackground(final boolean save) {
+ List<Entry> songs = getSelectedEntries();
if(playlistId != null) {
- selectAll(true, false);
+ songs = entries;
}
- List<Entry> songs = getSelectedSongs();
if(songs.isEmpty()) {
// Get both songs and albums
downloadRecursively(id, save, false, false, false, true);
@@ -1019,21 +833,23 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter
downloadBackground(save, songs);
}
}
- private void downloadBackground(final boolean save, final List<Entry> songs) {
+ @Override
+ protected void downloadBackground(final boolean save, final List<Entry> entries) {
if (getDownloadService() == null) {
return;
}
warnIfStorageUnavailable();
- LoadingTask<Void> onValid = new LoadingTask<Void>(context) {
+ RecursiveLoader onValid = new RecursiveLoader(context) {
@Override
- protected Void doInBackground() throws Throwable {
+ protected Boolean doInBackground() throws Throwable {
+ getSongsRecursively(entries, songs);
getDownloadService().downloadBackground(songs, save);
return null;
}
@Override
- protected void done(Void result) {
+ protected void done(Boolean result) {
Util.toast(context, context.getResources().getQuantityString(R.plurals.select_album_n_songs_downloading, songs.size(), songs.size()));
}
};
@@ -1041,15 +857,21 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter
checkLicenseAndTrialPeriod(onValid);
}
- private void delete() {
- List<Entry> songs = getSelectedSongs();
- if(songs.isEmpty()) {
- selectAll(true, false);
- songs = getSelectedSongs();
+ @Override
+ protected void download(List<Entry> entries, boolean append, boolean save, boolean autoplay, boolean playNext, boolean shuffle) {
+ download(entries, append, save, autoplay, playNext, shuffle, playlistName, playlistId);
+ }
- // Also delete all directories
- for(Entry album: albums) {
- deleteRecursively(album);
+ @Override
+ protected void delete() {
+ List<Entry> songs = getSelectedEntries();
+ if(songs.isEmpty()) {
+ for(Entry entry: entries) {
+ if(entry.isDirectory()) {
+ deleteRecursively(entry);
+ } else {
+ songs.add(entry);
+ }
}
}
if (getDownloadService() != null) {
@@ -1068,11 +890,9 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter
@Override
protected void done(Void result) {
- for(int i = indexes.size() - 1; i >= 0; i--) {
- entryList.setItemChecked(indexes.get(i) + 1, false);
- entryAdapter.removeAt(indexes.get(i));
+ for(Integer index: indexes) {
+ entryGridAdapter.removeAt(index);
}
- entryAdapter.notifyDataSetChanged();
Util.toast(context, context.getResources().getString(R.string.removed_playlist, indexes.size(), name));
}
@@ -1144,7 +964,7 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter
public void onClick(DialogInterface dialog, int which) {
new LoadingTask<Void>(context, true) {
@Override
- protected Void doInBackground() throws Throwable {
+ protected Void doInBackground() throws Throwable {
MusicService musicService = MusicServiceFactory.getMusicService(context);
musicService.deletePodcastEpisode(episode.getEpisodeId(), episode.getParent(), null, context);
if (getDownloadService() != null) {
@@ -1157,8 +977,7 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter
@Override
protected void done(Void result) {
- entries.remove(episode);
- entryAdapter.notifyDataSetChanged();
+ entryGridAdapter.removeItem(episode);
}
@Override
@@ -1172,7 +991,7 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter
}
public void unstarSelected() {
- List<Entry> selected = getSelectedSongs();
+ List<Entry> selected = getSelectedEntries();
if(selected.size() == 0) {
selected = entries;
}
@@ -1219,10 +1038,8 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter
Util.toast(context, context.getResources().getString(R.string.starring_content_unstarred, Integer.toString(unstar.size())));
for(Entry entry: unstar) {
- entries.remove(entry);
+ entryGridAdapter.removeItem(entry);
}
- entryAdapter.notifyDataSetChanged();
- selectAll(false, false);
}
@Override
@@ -1335,31 +1152,19 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter
@Override
protected void done(Void result) {
- Util.startActivityWithoutTransition(context, DownloadActivity.class);
+ context.openNowPlaying();
}
}.execute();
}
private View createHeader() {
- View header = entryList.findViewById(R.id.select_album_header_wrapper);
- boolean add = false;
- if(header == null) {
- header = LayoutInflater.from(context).inflate(R.layout.select_album_header, entryList, false);
- add = true;
- }
+ View header = LayoutInflater.from(context).inflate(R.layout.select_album_header, null, false);
setupCoverArt(header);
setupTextDisplay(header);
+ setupButtonEvents(header);
- if(add) {
- setupButtonEvents(header);
- }
-
- if(add) {
- return header;
- } else {
- return null;
- }
+ return header;
}
private void setupCoverArt(View header) {
@@ -1508,11 +1313,6 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter
} else {
artistView.setMaxLines(minLines);
}
-
- if(albumList instanceof HeaderGridView) {
- HeaderGridView headerGridView = (HeaderGridView) albumList;
- ((BaseAdapter) headerGridView.getAdapter()).notifyDataSetChanged();
- }
}
});
artistView.setMovementMethod(LinkMovementMethod.getInstance());
@@ -1559,14 +1359,23 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter
final ImageButton starButton = (ImageButton) header.findViewById(R.id.select_album_star);
if(directory != null && Util.getPreferences(context).getBoolean(Constants.PREFERENCES_KEY_MENU_STAR, true) && artistInfo == null) {
- starButton.setImageResource(directory.isStarred() ? android.R.drawable.btn_star_big_on : android.R.drawable.btn_star_big_off);
+ if(directory.isStarred()) {
+ starButton.setImageDrawable(DrawableTint.getTintedDrawable(context, R.drawable.ic_toggle_star));
+ } else {
+ starButton.setImageResource(DrawableTint.getDrawableRes(context, R.attr.star_outline));
+ }
starButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
toggleStarred(directory, new OnStarChange() {
@Override
void starChange(boolean starred) {
- starButton.setImageResource(directory.isStarred() ? android.R.drawable.btn_star_big_on : android.R.drawable.btn_star_big_off);
+ if(directory.isStarred()) {
+ starButton.setImageResource(DrawableTint.getDrawableRes(context, R.attr.star_outline));
+ starButton.setImageDrawable(DrawableTint.getTintedDrawable(context, R.drawable.ic_toggle_star));
+ } else {
+ starButton.setImageResource(DrawableTint.getDrawableRes(context, R.attr.star_outline));
+ }
}
});
}
diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/SelectGenreFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SelectGenreFragment.java
index 2d310172..cb57c280 100644
--- a/app/src/main/java/github/daneren2005/dsub/fragments/SelectGenreFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/SelectGenreFragment.java
@@ -1,37 +1,39 @@
/*
- This file is part of Subsonic.
+ 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 <http://www.gnu.org/licenses/>.
+ Copyright 2015 (C) Scott Jackson
+*/
- 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 <http://www.gnu.org/licenses/>.
-
- Copyright 2010 (C) Sindre Mehus
- */
package github.daneren2005.dsub.fragments;
import android.os.Bundle;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.adapter.SectionAdapter;
import github.daneren2005.dsub.domain.Genre;
import github.daneren2005.dsub.service.MusicService;
import github.daneren2005.dsub.util.Constants;
import github.daneren2005.dsub.util.ProgressListener;
import github.daneren2005.dsub.adapter.GenreAdapter;
+import github.daneren2005.dsub.view.UpdateView;
import java.util.List;
-public class SelectGenreFragment extends SelectListFragment<Genre> {
+public class SelectGenreFragment extends SelectRecyclerFragment<Genre> {
private static final String TAG = SelectGenreFragment.class.getSimpleName();
@Override
@@ -40,8 +42,8 @@ public class SelectGenreFragment extends SelectListFragment<Genre> {
}
@Override
- public ArrayAdapter getAdapter(List<Genre> objs) {
- return new GenreAdapter(context, objs);
+ public SectionAdapter getAdapter(List<Genre> objs) {
+ return new GenreAdapter(context, objs, this);
}
@Override
@@ -55,9 +57,7 @@ public class SelectGenreFragment extends SelectListFragment<Genre> {
}
@Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- Genre genre = (Genre) parent.getItemAtPosition(position);
-
+ public void onItemClicked(Genre genre) {
SubsonicFragment fragment = new SelectDirectoryFragment();
Bundle args = new Bundle();
args.putString(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TYPE, "genres");
@@ -68,4 +68,12 @@ public class SelectGenreFragment extends SelectListFragment<Genre> {
replaceFragment(fragment);
}
+
+ @Override
+ public void onCreateContextMenu(Menu menu, MenuInflater menuInflater, UpdateView<Genre> updateView, Genre item) {}
+
+ @Override
+ public boolean onContextItemSelected(MenuItem menuItem, UpdateView<Genre> updateView, Genre item) {
+ return false;
+ }
}
diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/SelectListFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SelectListFragment.java
deleted file mode 100644
index 6f73f6e8..00000000
--- a/app/src/main/java/github/daneren2005/dsub/fragments/SelectListFragment.java
+++ /dev/null
@@ -1,163 +0,0 @@
-/*
- 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 <http://www.gnu.org/licenses/>.
-
- Copyright 2010 (C) Sindre Mehus
- */
-package github.daneren2005.dsub.fragments;
-
-import android.os.Bundle;
-import android.support.v4.widget.SwipeRefreshLayout;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
-import android.widget.ListView;
-
-import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.List;
-
-import github.daneren2005.dsub.R;
-import github.daneren2005.dsub.service.MusicService;
-import github.daneren2005.dsub.service.MusicServiceFactory;
-import github.daneren2005.dsub.util.BackgroundTask;
-import github.daneren2005.dsub.util.Constants;
-import github.daneren2005.dsub.util.ProgressListener;
-import github.daneren2005.dsub.util.TabBackgroundTask;
-
-public abstract class SelectListFragment<T> extends SubsonicFragment implements AdapterView.OnItemClickListener {
- private static final String TAG = SelectListFragment.class.getSimpleName();
- protected ListView listView;
- protected ArrayAdapter adapter;
- protected BackgroundTask<List<T>> currentTask;
- protected List<T> objects;
- protected boolean serialize = true;
-
- @Override
- public void onCreate(Bundle bundle) {
- super.onCreate(bundle);
-
- if(bundle != null && serialize) {
- objects = (List<T>) bundle.getSerializable(Constants.FRAGMENT_LIST);
- }
- }
-
- @Override
- public void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- if(serialize) {
- outState.putSerializable(Constants.FRAGMENT_LIST, (Serializable) objects);
- }
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
- rootView = inflater.inflate(R.layout.abstract_list_fragment, container, false);
-
- refreshLayout = (SwipeRefreshLayout) rootView.findViewById(R.id.refresh_layout);
- refreshLayout.setOnRefreshListener(this);
-
- listView = (ListView)rootView.findViewById(R.id.fragment_list);
- listView.setOnItemClickListener(this);
- setupScrollList(listView);
- registerForContextMenu(listView);
-
- if(objects == null) {
- refresh(false);
- } else {
- listView.setAdapter(adapter = getAdapter(objects));
- }
-
- return rootView;
- }
-
- @Override
- public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) {
- if(!primaryFragment) {
- return;
- }
-
- menuInflater.inflate(getOptionsMenu(), menu);
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- return super.onOptionsItemSelected(item);
- }
-
- @Override
- protected void refresh(final boolean refresh) {
- int titleRes = getTitleResource();
- if(titleRes != 0) {
- setTitle(getTitleResource());
- }
- listView.setVisibility(View.GONE);
-
- // Cancel current running task before starting another one
- if(currentTask != null) {
- currentTask.cancel();
- }
-
- currentTask = new TabBackgroundTask<List<T>>(this) {
- @Override
- protected List<T> doInBackground() throws Throwable {
- MusicService musicService = MusicServiceFactory.getMusicService(context);
-
- objects = new ArrayList<T>();
-
- try {
- objects = getObjects(musicService, refresh, this);
- } catch (Exception x) {
- Log.e(TAG, "Failed to load", x);
- }
-
- return objects;
- }
-
- @Override
- protected void done(List<T> result) {
- if (result != null && !result.isEmpty()) {
- // Toggle fast scroll to get around issue when length of list changes
- listView.setFastScrollEnabled(false);
- listView.setAdapter(adapter = getAdapter(result));
- listView.setFastScrollEnabled(true);
-
- onFinishRefresh();
- listView.setVisibility(View.VISIBLE);
- } else {
- setEmpty(true);
- }
-
- currentTask = null;
- }
- };
- currentTask.execute();
- }
-
- public abstract int getOptionsMenu();
- public abstract ArrayAdapter getAdapter(List<T> objs);
- public abstract List<T> getObjects(MusicService musicService, boolean refresh, ProgressListener listener) throws Exception;
- public abstract int getTitleResource();
-
- public void onFinishRefresh() {
-
- }
-}
diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/SelectPlaylistFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SelectPlaylistFragment.java
index 3d7e664f..a2f1aabd 100644
--- a/app/src/main/java/github/daneren2005/dsub/fragments/SelectPlaylistFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/SelectPlaylistFragment.java
@@ -1,19 +1,21 @@
package github.daneren2005.dsub.fragments;
-import android.app.AlertDialog;
+import android.support.v7.app.AlertDialog;
import android.content.DialogInterface;
+import android.content.res.Resources;
import android.os.Bundle;
import android.support.v4.app.FragmentTransaction;
+import android.support.v7.widget.RecyclerView;
import android.view.ContextMenu;
+import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
-import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
import android.widget.CheckBox;
import android.widget.EditText;
import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.adapter.SectionAdapter;
import github.daneren2005.dsub.domain.MusicDirectory;
import github.daneren2005.dsub.domain.Playlist;
import github.daneren2005.dsub.domain.ServerInfo;
@@ -30,31 +32,37 @@ import github.daneren2005.dsub.util.LoadingTask;
import github.daneren2005.dsub.util.UserUtil;
import github.daneren2005.dsub.util.Util;
import github.daneren2005.dsub.adapter.PlaylistAdapter;
+import github.daneren2005.dsub.view.UpdateView;
+import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
-public class SelectPlaylistFragment extends SelectListFragment<Playlist> {
+public class SelectPlaylistFragment extends SelectRecyclerFragment<Playlist> {
private static final String TAG = SelectPlaylistFragment.class.getSimpleName();
@Override
- public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) {
- super.onCreateContextMenu(menu, view, menuInfo);
+ public void onCreate(Bundle bundle) {
+ super.onCreate(bundle);
+ if (Util.getPreferences(context).getBoolean(Constants.PREFERENCES_KEY_LARGE_ALBUM_ART, true)) {
+ largeAlbums = true;
+ }
+ }
- MenuInflater inflater = context.getMenuInflater();
+ @Override
+ public void onCreateContextMenu(Menu menu, MenuInflater menuInflater, UpdateView<Playlist> updateView, Playlist playlist) {
if (Util.isOffline(context)) {
- inflater.inflate(R.menu.select_playlist_context_offline, menu);
+ menuInflater.inflate(R.menu.select_playlist_context_offline, menu);
}
else {
- inflater.inflate(R.menu.select_playlist_context, menu);
+ menuInflater.inflate(R.menu.select_playlist_context, menu);
- AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
- Playlist playlist = (Playlist) listView.getItemAtPosition(info.position);
if(SyncUtil.isSyncedPlaylist(context, playlist.getId())) {
menu.removeItem(R.id.playlist_menu_sync);
} else {
menu.removeItem(R.id.playlist_menu_stop_sync);
}
-
+
if(!ServerInfo.checkServerVersion(context, "1.8")) {
menu.removeItem(R.id.playlist_update_info);
} else if(playlist.getPublic() != null && playlist.getPublic() == true && playlist.getId().indexOf(".m3u") == -1 && !UserUtil.getCurrentUsername(context).equals(playlist.getOwner())) {
@@ -67,48 +75,14 @@ public class SelectPlaylistFragment extends SelectListFragment<Playlist> {
}
@Override
- public boolean onContextItemSelected(MenuItem menuItem) {
- if(menuItem.getGroupId() != getSupportTag()) {
- return false;
- }
-
- AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo();
- Playlist playlist = (Playlist) listView.getItemAtPosition(info.position);
-
- SubsonicFragment fragment;
- Bundle args;
- FragmentTransaction trans;
+ public boolean onContextItemSelected(MenuItem menuItem, UpdateView<Playlist> updateView, Playlist playlist) {
switch (menuItem.getItemId()) {
- case R.id.playlist_menu_download:
- downloadPlaylist(playlist.getId(), playlist.getName(), false, true, false, false, true);
- break;
case R.id.playlist_menu_sync:
syncPlaylist(playlist);
break;
case R.id.playlist_menu_stop_sync:
stopSyncPlaylist(playlist);
break;
- case R.id.playlist_menu_play_now:
- fragment = new SelectDirectoryFragment();
- args = new Bundle();
- args.putString(Constants.INTENT_EXTRA_NAME_PLAYLIST_ID, playlist.getId());
- args.putString(Constants.INTENT_EXTRA_NAME_PLAYLIST_NAME, playlist.getName());
- args.putBoolean(Constants.INTENT_EXTRA_NAME_AUTOPLAY, true);
- fragment.setArguments(args);
-
- replaceFragment(fragment);
- break;
- case R.id.playlist_menu_play_shuffled:
- fragment = new SelectDirectoryFragment();
- args = new Bundle();
- args.putString(Constants.INTENT_EXTRA_NAME_PLAYLIST_ID, playlist.getId());
- args.putString(Constants.INTENT_EXTRA_NAME_PLAYLIST_NAME, playlist.getName());
- args.putBoolean(Constants.INTENT_EXTRA_NAME_SHUFFLE, true);
- args.putBoolean(Constants.INTENT_EXTRA_NAME_AUTOPLAY, true);
- fragment.setArguments(args);
-
- replaceFragment(fragment);
- break;
case R.id.playlist_menu_delete:
deletePlaylist(playlist);
break;
@@ -118,10 +92,9 @@ public class SelectPlaylistFragment extends SelectListFragment<Playlist> {
case R.id.playlist_update_info:
updatePlaylistInfo(playlist);
break;
- default:
- return false;
}
- return true;
+
+ return false;
}
@Override
@@ -130,8 +103,31 @@ public class SelectPlaylistFragment extends SelectListFragment<Playlist> {
}
@Override
- public ArrayAdapter getAdapter(List<Playlist> playlists) {
- return new PlaylistAdapter(context, playlists);
+ public SectionAdapter<Playlist> getAdapter(List<Playlist> playlists) {
+ List<Playlist> mine = new ArrayList<>();
+ List<Playlist> shared = new ArrayList<>();
+
+ String currentUsername = UserUtil.getCurrentUsername(context);
+ for(Playlist playlist: playlists) {
+ if(playlist.getOwner() == null || playlist.getOwner().equals(currentUsername)) {
+ mine.add(playlist);
+ } else {
+ shared.add(playlist);
+ }
+ }
+
+ if(shared.isEmpty()) {
+ return new PlaylistAdapter(context, playlists, getImageLoader(), largeAlbums, this);
+ } else {
+ Resources res = context.getResources();
+ List<String> headers = Arrays.asList(res.getString(R.string.playlist_mine), res.getString(R.string.playlist_shared));
+
+ List<List<Playlist>> sections = new ArrayList<>();
+ sections.add(mine);
+ sections.add(shared);
+
+ return new PlaylistAdapter(context, headers, sections, getImageLoader(), largeAlbums, this);
+ }
}
@Override
@@ -149,9 +145,7 @@ public class SelectPlaylistFragment extends SelectListFragment<Playlist> {
}
@Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- Playlist playlist = (Playlist) parent.getItemAtPosition(position);
-
+ public void onItemClicked(Playlist playlist) {
SubsonicFragment fragment = new SelectDirectoryFragment();
Bundle args = new Bundle();
args.putString(Constants.INTENT_EXTRA_NAME_PLAYLIST_ID, playlist.getId());
@@ -179,8 +173,7 @@ public class SelectPlaylistFragment extends SelectListFragment<Playlist> {
@Override
protected void done(Void result) {
- adapter.remove(playlist);
- adapter.notifyDataSetChanged();
+ adapter.removeItem(playlist);
Util.toast(context, context.getResources().getString(R.string.menu_deleted_playlist, playlist.getName()));
}
@@ -201,12 +194,33 @@ public class SelectPlaylistFragment extends SelectListFragment<Playlist> {
}
private void displayPlaylistInfo(final Playlist playlist) {
- String message = "Owner: " + playlist.getOwner() + "\nComments: " +
- ((playlist.getComment() == null) ? "" : playlist.getComment()) +
- "\nSong Count: " + playlist.getSongCount() +
- ((playlist.getPublic() == null) ? "" : ("\nPublic: " + playlist.getPublic())) +
- "\nCreation Date: " + playlist.getCreated().replace('T', ' ');
- Util.info(context, playlist.getName(), message);
+ List<Integer> headers = new ArrayList<>();
+ List<String> details = new ArrayList<>();
+
+ if(playlist.getOwner() != null) {
+ headers.add(R.string.details_owner);
+ details.add(playlist.getOwner());
+ }
+
+ if(playlist.getComment() != null) {
+ headers.add(R.string.details_comments);
+ details.add(playlist.getComment());
+ }
+
+ headers.add(R.string.details_song_count);
+ details.add(playlist.getSongCount());
+
+ if(playlist.getPublic() != null) {
+ headers.add(R.string.details_public);
+ details.add(Boolean.toString(playlist.getPublic()));
+ }
+
+ if(playlist.getCreated() != null) {
+ headers.add(R.string.details_created);
+ details.add(Util.formatDate(context, playlist.getCreated()));
+ }
+
+ Util.showDetailsDialog(context, R.string.details_title_playlist, headers, details);
}
private void updatePlaylistInfo(final Playlist playlist) {
diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/SelectPodcastsFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SelectPodcastsFragment.java
index 3a564f1c..381453c0 100644
--- a/app/src/main/java/github/daneren2005/dsub/fragments/SelectPodcastsFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/SelectPodcastsFragment.java
@@ -1,33 +1,30 @@
/*
- 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 <http://www.gnu.org/licenses/>.
-
- Copyright 2010 (C) Sindre Mehus
- */
+ 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 <http://www.gnu.org/licenses/>.
+ Copyright 2015 (C) Scott Jackson
+*/
package github.daneren2005.dsub.fragments;
-import android.app.AlertDialog;
+import android.support.v7.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.view.ContextMenu;
+import android.view.Menu;
+import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
-import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
import android.widget.TextView;
import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.adapter.SectionAdapter;
import github.daneren2005.dsub.domain.MusicDirectory;
import github.daneren2005.dsub.domain.PodcastChannel;
import github.daneren2005.dsub.service.MusicService;
@@ -42,15 +39,12 @@ import github.daneren2005.dsub.util.SilentBackgroundTask;
import github.daneren2005.dsub.util.UserUtil;
import github.daneren2005.dsub.util.Util;
import github.daneren2005.dsub.adapter.PodcastChannelAdapter;
+import github.daneren2005.dsub.view.UpdateView;
import java.util.ArrayList;
import java.util.List;
-/**
- *
- * @author Scott
- */
-public class SelectPodcastsFragment extends SelectListFragment<PodcastChannel> {
+public class SelectPodcastsFragment extends SelectRecyclerFragment<PodcastChannel> {
private static final String TAG = SelectPodcastsFragment.class.getSimpleName();
@Override
@@ -70,38 +64,26 @@ public class SelectPodcastsFragment extends SelectListFragment<PodcastChannel> {
return false;
}
-
- @Override
- public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) {
- super.onCreateContextMenu(menu, view, menuInfo);
- android.view.MenuInflater inflater = context.getMenuInflater();
+ @Override
+ public void onCreateContextMenu(Menu menu, MenuInflater menuInflater, UpdateView<PodcastChannel> updateView, PodcastChannel podcast) {
if(!Util.isOffline(context) && UserUtil.canPodcast()) {
- inflater.inflate(R.menu.select_podcasts_context, menu);
+ menuInflater.inflate(R.menu.select_podcasts_context, menu);
- AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
- PodcastChannel podcast = (PodcastChannel) listView.getItemAtPosition(info.position);
if(SyncUtil.isSyncedPodcast(context, podcast.getId())) {
menu.removeItem(R.id.podcast_menu_sync);
} else {
menu.removeItem(R.id.podcast_menu_stop_sync);
}
} else {
- inflater.inflate(R.menu.select_podcasts_context_offline, menu);
+ menuInflater.inflate(R.menu.select_podcasts_context_offline, menu);
}
recreateContextMenu(menu);
}
@Override
- public boolean onContextItemSelected(MenuItem menuItem) {
- if(menuItem.getGroupId() != getSupportTag()) {
- return false;
- }
-
- AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo();
- PodcastChannel channel = (PodcastChannel) listView.getItemAtPosition(info.position);
-
+ public boolean onContextItemSelected(MenuItem menuItem, UpdateView<PodcastChannel> updateView, PodcastChannel channel) {
switch (menuItem.getItemId()) {
case R.id.podcast_menu_sync:
syncPodcast(channel);
@@ -116,7 +98,7 @@ public class SelectPodcastsFragment extends SelectListFragment<PodcastChannel> {
deletePodcast(channel);
break;
}
-
+
return true;
}
@@ -126,8 +108,8 @@ public class SelectPodcastsFragment extends SelectListFragment<PodcastChannel> {
}
@Override
- public ArrayAdapter getAdapter(List<PodcastChannel> channels) {
- return new PodcastChannelAdapter(context, channels);
+ public SectionAdapter getAdapter(List<PodcastChannel> channels) {
+ return new PodcastChannelAdapter(context, channels, this);
}
@Override
@@ -141,9 +123,7 @@ public class SelectPodcastsFragment extends SelectListFragment<PodcastChannel> {
}
@Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- PodcastChannel channel = (PodcastChannel) parent.getItemAtPosition(position);
-
+ public void onItemClicked(PodcastChannel channel) {
if("error".equals(channel.getStatus())) {
Util.toast(context, context.getResources().getString(R.string.select_podcasts_invalid_podcast_channel, channel.getErrorMessage() == null ? "error" : channel.getErrorMessage()));
} else if("downloading".equals(channel.getStatus())) {
@@ -159,7 +139,7 @@ public class SelectPodcastsFragment extends SelectListFragment<PodcastChannel> {
replaceFragment(fragment);
}
}
-
+
public void refreshPodcasts() {
new SilentBackgroundTask<Void>(context) {
@Override
@@ -234,13 +214,29 @@ public class SelectPodcastsFragment extends SelectListFragment<PodcastChannel> {
}
private void displayPodcastInfo(final PodcastChannel channel) {
- String message = ((channel.getName()) == null ? "" : "Title: " + channel.getName()) +
- "\nURL: " + channel.getUrl() +
- "\nStatus: " + channel.getStatus() +
- ((channel.getErrorMessage()) == null ? "" : "\nError Message: " + channel.getErrorMessage()) +
- ((channel.getDescription()) == null ? "" : "\n\nDescription: " + channel.getDescription());
-
- Util.info(context, channel.getName(), message);
+ List<Integer> headers = new ArrayList<>();
+ List<String> details = new ArrayList<>();
+
+ if(channel.getName() != null) {
+ headers.add(R.string.details_title);
+ details.add(channel.getName());
+ }
+
+ headers.add(R.string.details_url);
+ details.add(channel.getUrl());
+ headers.add(R.string.details_status);
+ details.add(channel.getStatus());
+
+ if(channel.getErrorMessage() != null) {
+ headers.add(R.string.details_error);
+ details.add(channel.getErrorMessage());
+ }
+ if(channel.getDescription() != null) {
+ headers.add(R.string.details_description);
+ details.add(channel.getDescription());
+ }
+
+ Util.showDetailsDialog(context, R.string.details_title_podcast, headers, details);
}
private void deletePodcast(final PodcastChannel channel) {
@@ -258,8 +254,7 @@ public class SelectPodcastsFragment extends SelectListFragment<PodcastChannel> {
@Override
protected void done(Void result) {
- adapter.remove(channel);
- adapter.notifyDataSetChanged();
+ adapter.removeItem(channel);
Util.toast(context, context.getResources().getString(R.string.select_podcasts_deleted, channel.getName()));
}
diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/SelectRecyclerFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SelectRecyclerFragment.java
new file mode 100644
index 00000000..9dc3e363
--- /dev/null
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/SelectRecyclerFragment.java
@@ -0,0 +1,197 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+ Copyright 2015 (C) Scott Jackson
+*/
+
+package github.daneren2005.dsub.fragments;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.support.v4.widget.SwipeRefreshLayout;
+import android.support.v7.widget.RecyclerView;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.adapter.SectionAdapter;
+import github.daneren2005.dsub.service.MusicService;
+import github.daneren2005.dsub.service.MusicServiceFactory;
+import github.daneren2005.dsub.util.Constants;
+import github.daneren2005.dsub.util.ProgressListener;
+import github.daneren2005.dsub.util.TabBackgroundTask;
+
+public abstract class SelectRecyclerFragment<T> extends SubsonicFragment implements SectionAdapter.OnItemClickedListener<T> {
+ private static final String TAG = SelectRecyclerFragment.class.getSimpleName();
+ protected RecyclerView recyclerView;
+ protected SectionAdapter<T> adapter;
+ protected UpdateTask currentTask;
+ protected List<T> objects;
+ protected boolean serialize = true;
+ protected boolean largeAlbums = false;
+ protected int columns;
+ protected boolean pullToRefresh = true;
+ protected boolean backgroundUpdate = true;
+
+ @Override
+ public void onCreate(Bundle bundle) {
+ super.onCreate(bundle);
+
+ if(bundle != null && serialize) {
+ objects = (List<T>) bundle.getSerializable(Constants.FRAGMENT_LIST);
+ }
+ columns = context.getResources().getInteger(R.integer.Grid_Columns);
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ if(serialize) {
+ outState.putSerializable(Constants.FRAGMENT_LIST, (Serializable) objects);
+ }
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
+ rootView = inflater.inflate(R.layout.abstract_recycler_fragment, container, false);
+
+ refreshLayout = (SwipeRefreshLayout) rootView.findViewById(R.id.refresh_layout);
+ refreshLayout.setOnRefreshListener(this);
+
+ recyclerView = (RecyclerView) rootView.findViewById(R.id.fragment_recycler);
+ setupLayoutManager();
+
+ if(pullToRefresh) {
+ setupScrollList(recyclerView);
+ } else {
+ refreshLayout.setEnabled(false);
+ }
+
+ if(objects == null) {
+ refresh(false);
+ } else {
+ recyclerView.setAdapter(adapter = getAdapter(objects));
+ }
+
+ return rootView;
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) {
+ if(!primaryFragment) {
+ return;
+ }
+
+ menuInflater.inflate(getOptionsMenu(), menu);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ return super.onOptionsItemSelected(item);
+ }
+
+ @Override
+ protected void refresh(final boolean refresh) {
+ int titleRes = getTitleResource();
+ if(titleRes != 0) {
+ setTitle(getTitleResource());
+ }
+ if(backgroundUpdate) {
+ recyclerView.setVisibility(View.GONE);
+ }
+
+ // Cancel current running task before starting another one
+ if(currentTask != null) {
+ currentTask.cancel();
+ }
+
+ currentTask = new UpdateTask(this, refresh);
+
+ if(backgroundUpdate) {
+ currentTask.execute();
+ } else {
+ objects = new ArrayList<T>();
+
+ try {
+ objects = getObjects(null, refresh, null);
+ } catch (Exception x) {
+ Log.e(TAG, "Failed to load", x);
+ }
+
+ currentTask.done(objects);
+ }
+ }
+
+ protected SectionAdapter getCurrentAdapter() {
+ return adapter;
+ }
+
+ private void setupLayoutManager() {
+ setupLayoutManager(recyclerView, largeAlbums);
+ }
+
+ public abstract int getOptionsMenu();
+ public abstract SectionAdapter<T> getAdapter(List<T> objs);
+ public abstract List<T> getObjects(MusicService musicService, boolean refresh, ProgressListener listener) throws Exception;
+ public abstract int getTitleResource();
+
+ public void onFinishRefresh() {
+
+ }
+
+ private class UpdateTask extends TabBackgroundTask<List<T>> {
+ private boolean refresh;
+
+ public UpdateTask(SubsonicFragment fragment, boolean refresh) {
+ super(fragment);
+ this.refresh = refresh;
+ }
+
+ @Override
+ public List<T> doInBackground() throws Exception {
+ MusicService musicService = MusicServiceFactory.getMusicService(context);
+
+ objects = new ArrayList<T>();
+
+ try {
+ objects = getObjects(musicService, refresh, this);
+ } catch (Exception x) {
+ Log.e(TAG, "Failed to load", x);
+ }
+
+ return objects;
+ }
+
+ @Override
+ public void done(List<T> result) {
+ if (result != null && !result.isEmpty()) {
+ recyclerView.setAdapter(adapter = getAdapter(result));
+
+ onFinishRefresh();
+ recyclerView.setVisibility(View.VISIBLE);
+ } else {
+ setEmpty(true);
+ }
+
+ currentTask = null;
+ }
+ }
+}
diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/SelectShareFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SelectShareFragment.java
index 07cd3bef..65723618 100644
--- a/app/src/main/java/github/daneren2005/dsub/fragments/SelectShareFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/SelectShareFragment.java
@@ -1,13 +1,28 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+ Copyright 2015 (C) Scott Jackson
+*/
+
package github.daneren2005.dsub.fragments;
-import android.app.AlertDialog;
+import android.support.v7.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.view.ContextMenu;
+import android.view.Menu;
+import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
-import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.DatePicker;
@@ -17,6 +32,7 @@ import java.util.Date;
import java.util.List;
import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.adapter.SectionAdapter;
import github.daneren2005.dsub.domain.Share;
import github.daneren2005.dsub.service.MusicService;
import github.daneren2005.dsub.service.MusicServiceFactory;
@@ -27,30 +43,19 @@ import github.daneren2005.dsub.util.LoadingTask;
import github.daneren2005.dsub.util.ProgressListener;
import github.daneren2005.dsub.util.Util;
import github.daneren2005.dsub.adapter.ShareAdapter;
+import github.daneren2005.dsub.view.UpdateView;
-/**
- * Created by Scott on 12/28/13.
- */
-public class SelectShareFragment extends SelectListFragment<Share> {
+public class SelectShareFragment extends SelectRecyclerFragment<Share> {
private static final String TAG = SelectShareFragment.class.getSimpleName();
@Override
- public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) {
- super.onCreateContextMenu(menu, view, menuInfo);
- android.view.MenuInflater inflater = context.getMenuInflater();
- inflater.inflate(R.menu.select_share_context, menu);
+ public void onCreateContextMenu(Menu menu, MenuInflater menuInflater, UpdateView<Share> updateView, Share item) {
+ menuInflater.inflate(R.menu.select_share_context, menu);
recreateContextMenu(menu);
}
@Override
- public boolean onContextItemSelected(MenuItem menuItem) {
- if(menuItem.getGroupId() != getSupportTag()) {
- return false;
- }
-
- AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo();
- Share share = (Share) listView.getItemAtPosition(info.position);
-
+ public boolean onContextItemSelected(MenuItem menuItem, UpdateView<Share> updateView, Share share) {
switch (menuItem.getItemId()) {
case R.id.share_menu_share:
shareExternal(share);
@@ -75,8 +80,8 @@ public class SelectShareFragment extends SelectListFragment<Share> {
}
@Override
- public ArrayAdapter getAdapter(List<Share> objs) {
- return new ShareAdapter(context, objs);
+ public SectionAdapter getAdapter(List<Share> objs) {
+ return new ShareAdapter(context, objs, this);
}
@Override
@@ -90,9 +95,7 @@ public class SelectShareFragment extends SelectListFragment<Share> {
}
@Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- Share share = (Share) parent.getItemAtPosition(position);
-
+ public void onItemClicked(Share share) {
SubsonicFragment fragment = new SelectDirectoryFragment();
Bundle args = new Bundle();
args.putSerializable(Constants.INTENT_EXTRA_NAME_SHARE, share);
@@ -193,8 +196,7 @@ public class SelectShareFragment extends SelectListFragment<Share> {
@Override
protected void done(Void result) {
- adapter.remove(share);
- adapter.notifyDataSetChanged();
+ adapter.removeItem(share);
Util.toast(context, context.getResources().getString(R.string.share_deleted, share.getName()));
}
diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/SelectVideoFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SelectVideoFragment.java
index b4d34ff9..0a79b3d5 100644
--- a/app/src/main/java/github/daneren2005/dsub/fragments/SelectVideoFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/SelectVideoFragment.java
@@ -16,28 +16,33 @@
package github.daneren2005.dsub.fragments;
import android.view.ContextMenu;
+import android.view.Menu;
+import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
import java.util.List;
import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.adapter.EntryGridAdapter;
+import github.daneren2005.dsub.adapter.SectionAdapter;
import github.daneren2005.dsub.domain.MusicDirectory;
import github.daneren2005.dsub.service.MusicService;
import github.daneren2005.dsub.util.ProgressListener;
-import github.daneren2005.dsub.adapter.EntryAdapter;
+import github.daneren2005.dsub.view.UpdateView;
-public class SelectVideoFragment extends SelectListFragment<MusicDirectory.Entry> {
+public class SelectVideoFragment extends SelectRecyclerFragment<MusicDirectory.Entry> {
@Override
public int getOptionsMenu() {
return R.menu.empty;
}
@Override
- public ArrayAdapter getAdapter(List<MusicDirectory.Entry> objs) {
- return new EntryAdapter(context, null, objs, false);
+ public SectionAdapter getAdapter(List<MusicDirectory.Entry> objs) {
+ SectionAdapter adapter = new EntryGridAdapter(context, objs, null, false);
+ adapter.setOnItemClickedListener(this);
+ return adapter;
}
@Override
@@ -52,31 +57,18 @@ public class SelectVideoFragment extends SelectListFragment<MusicDirectory.Entry
}
@Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- MusicDirectory.Entry entry = (MusicDirectory.Entry) parent.getItemAtPosition(position);
+ public void onItemClicked(MusicDirectory.Entry entry) {
playVideo(entry);
}
@Override
- public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) {
- super.onCreateContextMenu(menu, view, menuInfo);
-
- AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
- Object entry = listView.getItemAtPosition(info.position);
-
- onCreateContextMenu(menu, view, menuInfo, entry);
+ public void onCreateContextMenu(Menu menu, MenuInflater menuInflater, UpdateView<MusicDirectory.Entry> updateView, MusicDirectory.Entry item) {
+ onCreateContextMenuSupport(menu, menuInflater, updateView, item);
recreateContextMenu(menu);
}
@Override
- public boolean onContextItemSelected(MenuItem menuItem) {
- if (menuItem.getGroupId() != getSupportTag()) {
- return false;
- }
-
- AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo();
- Object entry = listView.getItemAtPosition(info.position);
-
+ public boolean onContextItemSelected(MenuItem menuItem, UpdateView<MusicDirectory.Entry> updateView, MusicDirectory.Entry entry) {
return onContextItemSelected(menuItem, entry);
}
}
diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/SelectYearFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SelectYearFragment.java
index dc19acad..22676d8b 100644
--- a/app/src/main/java/github/daneren2005/dsub/fragments/SelectYearFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/SelectYearFragment.java
@@ -1,24 +1,24 @@
/*
- This file is part of Subsonic.
-
+ 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
+ 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 <http://www.gnu.org/licenses/>.
-
- Copyright 2010 (C) Sindre Mehus
+ along with Subsonic. If not, see <http://www.gnu.org/licenses/>.
+ Copyright 2015 (C) Scott Jackson
*/
+
package github.daneren2005.dsub.fragments;
import android.os.Bundle;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
@@ -27,14 +27,21 @@ import java.util.ArrayList;
import java.util.List;
import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.adapter.BasicListAdapter;
+import github.daneren2005.dsub.adapter.SectionAdapter;
import github.daneren2005.dsub.service.MusicService;
import github.daneren2005.dsub.util.Constants;
import github.daneren2005.dsub.util.ProgressListener;
+import github.daneren2005.dsub.view.UpdateView;
+
+public class SelectYearFragment extends SelectRecyclerFragment<String> {
-/**
- * Created by Scott on 12/23/13.
- */
-public class SelectYearFragment extends SelectListFragment<Integer> {
+ public SelectYearFragment() {
+ super();
+ pullToRefresh = false;
+ serialize = false;
+ backgroundUpdate = false;
+ }
@Override
public int getOptionsMenu() {
@@ -42,15 +49,15 @@ public class SelectYearFragment extends SelectListFragment<Integer> {
}
@Override
- public ArrayAdapter getAdapter(List<Integer> objs) {
- return new ArrayAdapter<Integer>(context, android.R.layout.simple_list_item_1, objs);
+ public SectionAdapter getAdapter(List<String> objs) {
+ return new BasicListAdapter(context, objs, this);
}
@Override
- public List<Integer> getObjects(MusicService musicService, boolean refresh, ProgressListener listener) throws Exception {
- List<Integer> decades = new ArrayList<Integer>();
+ public List<String> getObjects(MusicService musicService, boolean refresh, ProgressListener listener) throws Exception {
+ List<String> decades = new ArrayList<>();
for(int i = 2010; i >= 1920; i -= 10) {
- decades.add(i);
+ decades.add(String.valueOf(i));
}
return decades;
@@ -62,17 +69,23 @@ public class SelectYearFragment extends SelectListFragment<Integer> {
}
@Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- Integer decade = (Integer) parent.getItemAtPosition(position);
-
+ public void onItemClicked(String decade) {
SubsonicFragment fragment = new SelectDirectoryFragment();
Bundle args = new Bundle();
args.putString(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TYPE, "years");
args.putInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_SIZE, 20);
args.putInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_OFFSET, 0);
- args.putString(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_EXTRA, Integer.toString(decade));
+ args.putString(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_EXTRA, decade);
fragment.setArguments(args);
replaceFragment(fragment);
}
+
+ @Override
+ public void onCreateContextMenu(Menu menu, MenuInflater menuInflater, UpdateView<String> updateView, String item) {}
+
+ @Override
+ public boolean onContextItemSelected(MenuItem menuItem, UpdateView<String> updateView, String item) {
+ return false;
+ }
}
diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/SimilarArtistFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SimilarArtistFragment.java
index 79e759cc..5677e445 100644
--- a/app/src/main/java/github/daneren2005/dsub/fragments/SimilarArtistFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/SimilarArtistFragment.java
@@ -16,13 +16,13 @@
package github.daneren2005.dsub.fragments;
import android.os.Bundle;
-import android.view.ContextMenu;
+import android.view.Menu;
+import android.view.MenuInflater;
import android.view.MenuItem;
-import android.view.View;
-import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.adapter.ArtistAdapter;
+import github.daneren2005.dsub.adapter.SectionAdapter;
import github.daneren2005.dsub.domain.Artist;
import github.daneren2005.dsub.domain.ArtistInfo;
import github.daneren2005.dsub.domain.MusicDirectory;
@@ -32,13 +32,13 @@ import github.daneren2005.dsub.service.MusicServiceFactory;
import github.daneren2005.dsub.util.Constants;
import github.daneren2005.dsub.util.ProgressListener;
import github.daneren2005.dsub.util.Util;
-import github.daneren2005.dsub.adapter.ArtistAdapter;
+import github.daneren2005.dsub.view.UpdateView;
import java.net.URLEncoder;
import java.util.LinkedList;
import java.util.List;
-public class SimilarArtistFragment extends SelectListFragment<Artist> {
+public class SimilarArtistFragment extends SelectRecyclerFragment<Artist> {
private static final String TAG = SimilarArtistFragment.class.getSimpleName();
private ArtistInfo info;
private String artistId;
@@ -52,6 +52,18 @@ public class SimilarArtistFragment extends SelectListFragment<Artist> {
}
@Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) {
+ super.onCreateOptionsMenu(menu, menuInflater);
+ if(!primaryFragment) {
+ return;
+ }
+
+ if(info.getMissingArtists().isEmpty()) {
+ menu.removeItem(R.id.menu_show_missing);
+ }
+ }
+
+ @Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_play_now:
@@ -69,30 +81,18 @@ public class SimilarArtistFragment extends SelectListFragment<Artist> {
}
@Override
- public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) {
- super.onCreateContextMenu(menu, view, menuInfo);
-
- AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
- Object entry = listView.getItemAtPosition(info.position);
- onCreateContextMenu(menu, view, menuInfo, entry);
-
+ public void onCreateContextMenu(Menu menu, MenuInflater menuInflater, UpdateView<Artist> updateView, Artist item) {
+ onCreateContextMenuSupport(menu, menuInflater, updateView, item);
recreateContextMenu(menu);
}
@Override
- public boolean onContextItemSelected(MenuItem menuItem) {
- if(menuItem.getGroupId() != getSupportTag()) {
- return false;
- }
-
- AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo();
- Artist artist = (Artist) listView.getItemAtPosition(info.position);
+ public boolean onContextItemSelected(MenuItem menuItem, UpdateView<Artist> updateView, Artist artist) {
return onContextItemSelected(menuItem, artist);
}
@Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- Artist artist = (Artist) parent.getItemAtPosition(position);
+ public void onItemClicked(Artist artist) {
SubsonicFragment fragment = new SelectDirectoryFragment();
Bundle args = new Bundle();
args.putString(Constants.INTENT_EXTRA_NAME_ID, artist.getId());
@@ -109,8 +109,8 @@ public class SimilarArtistFragment extends SelectListFragment<Artist> {
}
@Override
- public ArrayAdapter getAdapter(List<Artist> objects) {
- return new ArtistAdapter(context, objects);
+ public SectionAdapter getAdapter(List<Artist> objects) {
+ return new ArtistAdapter(context, objects, this);
}
@Override
diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/SubsonicFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SubsonicFragment.java
index 109983ba..ddc39235 100644
--- a/app/src/main/java/github/daneren2005/dsub/fragments/SubsonicFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/SubsonicFragment.java
@@ -18,8 +18,9 @@
*/
package github.daneren2005.dsub.fragments;
+import android.annotation.TargetApi;
import android.app.Activity;
-import android.app.AlertDialog;
+import android.support.v7.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
@@ -28,12 +29,15 @@ import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.media.MediaMetadataRetriever;
import android.net.Uri;
+import android.os.Build;
import android.os.Bundle;
import android.os.StatFs;
import android.support.v4.app.Fragment;
import android.support.v4.widget.SwipeRefreshLayout;
+import android.support.v7.widget.GridLayoutManager;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
import android.util.Log;
-import android.view.ContextMenu;
import android.view.GestureDetector;
import android.view.Menu;
import android.view.MenuInflater;
@@ -41,7 +45,6 @@ import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
-import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CheckBox;
@@ -49,9 +52,9 @@ import android.widget.EditText;
import android.widget.RatingBar;
import android.widget.TextView;
import github.daneren2005.dsub.R;
-import github.daneren2005.dsub.activity.DownloadActivity;
import github.daneren2005.dsub.activity.SubsonicActivity;
import github.daneren2005.dsub.activity.SubsonicFragmentActivity;
+import github.daneren2005.dsub.adapter.SectionAdapter;
import github.daneren2005.dsub.domain.Artist;
import github.daneren2005.dsub.domain.Bookmark;
import github.daneren2005.dsub.domain.Genre;
@@ -70,15 +73,16 @@ import github.daneren2005.dsub.service.ServerTooOldException;
import github.daneren2005.dsub.util.Constants;
import github.daneren2005.dsub.util.FileUtil;
import github.daneren2005.dsub.util.ImageLoader;
+import github.daneren2005.dsub.util.MenuUtil;
import github.daneren2005.dsub.util.ProgressListener;
import github.daneren2005.dsub.util.SilentBackgroundTask;
import github.daneren2005.dsub.util.LoadingTask;
import github.daneren2005.dsub.util.UserUtil;
import github.daneren2005.dsub.util.Util;
-import github.daneren2005.dsub.view.AlbumCell;
import github.daneren2005.dsub.view.AlbumView;
import github.daneren2005.dsub.view.ArtistEntryView;
import github.daneren2005.dsub.view.ArtistView;
+import github.daneren2005.dsub.view.GridSpacingDecoration;
import github.daneren2005.dsub.view.PlaylistSongView;
import github.daneren2005.dsub.view.SongView;
import github.daneren2005.dsub.view.UpdateView;
@@ -101,7 +105,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
private static final String TAG = SubsonicFragment.class.getSimpleName();
private static int TAG_INC = 10;
private int tag;
-
+
protected SubsonicActivity context;
protected CharSequence title = null;
protected CharSequence subtitle = null;
@@ -116,7 +120,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
protected boolean artistOverride = false;
protected SwipeRefreshLayout refreshLayout;
protected boolean firstRun;
-
+
public SubsonicFragment() {
super();
tag = TAG_INC++;
@@ -171,10 +175,10 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
- case R.id.menu_shuffle:
+ case R.id.menu_global_shuffle:
onShuffleRequested();
return true;
- case R.id.menu_search:
+ case R.id.menu_global_search:
context.onSearchRequested();
return true;
case R.id.menu_exit:
@@ -183,23 +187,50 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
case R.id.menu_refresh:
refresh();
return true;
+ case R.id.menu_play_now:
+ playNow(false, false);
+ return true;
+ case R.id.menu_play_last:
+ playNow(false, true);
+ return true;
+ case R.id.menu_play_next:
+ playNow(false, true, true);
+ return true;
+ case R.id.menu_shuffle:
+ playNow(true, false);
+ return true;
+ case R.id.menu_download:
+ downloadBackground(false);
+ clearSelected();
+ return true;
+ case R.id.menu_cache:
+ downloadBackground(true);
+ clearSelected();
+ return true;
+ case R.id.menu_delete:
+ delete();
+ clearSelected();
+ return true;
+ case R.id.menu_add_playlist:
+ List<Entry> songs = getSelectedEntries();
+ addToPlaylist(songs);
+ clearSelected();
+ return true;
}
return false;
}
-
- public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo, Object selected) {
- MenuInflater inflater = context.getMenuInflater();
+ public void onCreateContextMenuSupport(Menu menu, MenuInflater menuInflater, UpdateView updateView, Object selected) {
if(selected instanceof Entry) {
Entry entry = (Entry) selected;
if(entry instanceof PodcastEpisode && !entry.isVideo()) {
if(Util.isOffline(context)) {
- inflater.inflate(R.menu.select_podcast_episode_context_offline, menu);
+ menuInflater.inflate(R.menu.select_podcast_episode_context_offline, menu);
}
else {
- inflater.inflate(R.menu.select_podcast_episode_context, menu);
-
+ menuInflater.inflate(R.menu.select_podcast_episode_context, menu);
+
if(entry.getBookmark() == null) {
menu.removeItem(R.id.bookmark_menu_delete);
}
@@ -207,10 +238,10 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
}
else if (entry.isDirectory()) {
if(Util.isOffline(context)) {
- inflater.inflate(R.menu.select_album_context_offline, menu);
+ menuInflater.inflate(R.menu.select_album_context_offline, menu);
}
else {
- inflater.inflate(R.menu.select_album_context, menu);
+ menuInflater.inflate(R.menu.select_album_context, menu);
if(Util.isTagBrowsing(context)) {
menu.removeItem(R.id.menu_rate);
@@ -219,11 +250,11 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
menu.findItem(entry.isDirectory() ? R.id.album_menu_star : R.id.song_menu_star).setTitle(entry.isStarred() ? R.string.common_unstar : R.string.common_star);
} else if(!entry.isVideo()) {
if(Util.isOffline(context)) {
- inflater.inflate(R.menu.select_song_context_offline, menu);
+ menuInflater.inflate(R.menu.select_song_context_offline, menu);
}
else {
- inflater.inflate(R.menu.select_song_context, menu);
-
+ menuInflater.inflate(R.menu.select_song_context, menu);
+
if(entry.getBookmark() == null) {
menu.removeItem(R.id.bookmark_menu_delete);
}
@@ -231,112 +262,27 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
menu.findItem(entry.isDirectory() ? R.id.album_menu_star : R.id.song_menu_star).setTitle(entry.isStarred() ? R.string.common_unstar : R.string.common_star);
} else {
if(Util.isOffline(context)) {
- inflater.inflate(R.menu.select_video_context_offline, menu);
+ menuInflater.inflate(R.menu.select_video_context_offline, menu);
}
else {
- inflater.inflate(R.menu.select_video_context, menu);
+ menuInflater.inflate(R.menu.select_video_context, menu);
}
}
} else if(selected instanceof Artist) {
Artist artist = (Artist) selected;
if(Util.isOffline(context)) {
- inflater.inflate(R.menu.select_artist_context_offline, menu);
- }
- else {
- inflater.inflate(R.menu.select_artist_context, menu);
+ // menuInflater.inflate(R.menu.select_artist_context_offline, menu);
+ } else {
+ menuInflater.inflate(R.menu.select_artist_context, menu);
menu.findItem(R.id.artist_menu_star).setTitle(artist.isStarred() ? R.string.common_unstar : R.string.common_star);
}
}
- hideMenuItems(menu, (AdapterView.AdapterContextMenuInfo) menuInfo);
- }
-
- protected void hideMenuItems(ContextMenu menu, AdapterView.AdapterContextMenuInfo info) {
- if(!ServerInfo.checkServerVersion(context, "1.8")) {
- menu.setGroupVisible(R.id.server_1_8, false);
- menu.setGroupVisible(R.id.hide_star, false);
- }
- if(!ServerInfo.checkServerVersion(context, "1.9")) {
- menu.setGroupVisible(R.id.server_1_9, false);
- }
- if(!ServerInfo.checkServerVersion(context, "1.10.1")) {
- menu.setGroupVisible(R.id.server_1_10, false);
- }
-
- SharedPreferences prefs = Util.getPreferences(context);
- if(!prefs.getBoolean(Constants.PREFERENCES_KEY_MENU_PLAY_NEXT, true)) {
- menu.setGroupVisible(R.id.hide_play_next, false);
- }
- if(!prefs.getBoolean(Constants.PREFERENCES_KEY_MENU_PLAY_LAST, true)) {
- menu.setGroupVisible(R.id.hide_play_last, false);
- }
- if(!prefs.getBoolean(Constants.PREFERENCES_KEY_MENU_STAR, true)) {
- menu.setGroupVisible(R.id.hide_star, false);
- }
- if(!prefs.getBoolean(Constants.PREFERENCES_KEY_MENU_SHARED, true) || !UserUtil.canShare()) {
- menu.setGroupVisible(R.id.hide_share, false);
- }
- if(!prefs.getBoolean(Constants.PREFERENCES_KEY_MENU_RATING, true)) {
- menu.setGroupVisible(R.id.hide_rating, false);
- }
-
- if(!Util.isOffline(context)) {
- // If we are looking at a standard song view, get downloadFile to cache what options to show
- if(info.targetView instanceof SongView) {
- SongView songView = (SongView) info.targetView;
- DownloadFile downloadFile = songView.getDownloadFile();
-
- try {
- if(downloadFile != null) {
- if(downloadFile.isWorkDone()) {
- // Remove permanent cache menu if already perma cached
- if(downloadFile.isSaved()) {
- menu.removeItem(R.id.song_menu_pin);
- }
-
- // Remove cache option no matter what if already downloaded
- menu.removeItem(R.id.song_menu_download);
- } else {
- // Remove delete option if nothing to delete
- menu.removeItem(R.id.song_menu_delete);
- }
- }
- } catch(Exception e) {
- Log.w(TAG, "Failed to lookup downloadFile info", e);
- }
- }
- // Apply similar logic to album views
- else if(info.targetView instanceof AlbumCell || info.targetView instanceof AlbumView
- || info.targetView instanceof ArtistView || info.targetView instanceof ArtistEntryView) {
- File folder = null;
- int id = 0;
- if(info.targetView instanceof AlbumCell) {
- folder = ((AlbumCell) info.targetView).getFile();
- id = R.id.album_menu_delete;
- } else if(info.targetView instanceof AlbumView) {
- folder = ((AlbumView) info.targetView).getFile();
- id = R.id.album_menu_delete;
- } else if(info.targetView instanceof ArtistView) {
- folder = ((ArtistView) info.targetView).getFile();
- id = R.id.artist_menu_delete;
- } else if(info.targetView instanceof ArtistEntryView) {
- folder = ((ArtistEntryView) info.targetView).getFile();
- id = R.id.artist_menu_delete;
- }
-
- try {
- if(folder != null && !folder.exists()) {
- menu.removeItem(id);
- }
- } catch(Exception e) {
- Log.w(TAG, "Failed to lookup album directory info", e);
- }
- }
- }
+ MenuUtil.hideMenuItems(context, menu);
}
- protected void recreateContextMenu(ContextMenu menu) {
+ protected void recreateContextMenu(Menu menu) {
List<MenuItem> menuItems = new ArrayList<MenuItem>();
for(int i = 0; i < menu.size(); i++) {
MenuItem item = menu.getItem(i);
@@ -358,60 +304,12 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
songs.add(entry);
switch (menuItem.getItemId()) {
- case R.id.artist_menu_play_now:
- downloadRecursively(artist.getId(), false, false, true, false, false);
- break;
- case R.id.artist_menu_play_shuffled:
- downloadRecursively(artist.getId(), false, false, true, true, false);
- break;
- case R.id.artist_menu_play_next:
- downloadRecursively(artist.getId(), false, true, false, false, false, true);
- break;
- case R.id.artist_menu_play_last:
- downloadRecursively(artist.getId(), false, true, false, false, false);
- break;
- case R.id.artist_menu_download:
- downloadRecursively(artist.getId(), false, true, false, false, true);
- break;
- case R.id.artist_menu_pin:
- downloadRecursively(artist.getId(), true, true, false, false, true);
- break;
- case R.id.artist_menu_delete:
- deleteRecursively(artist);
- break;
case R.id.artist_menu_star:
toggleStarred(artist);
break;
- case R.id.album_menu_play_now:
- artistOverride = true;
- downloadRecursively(entry.getId(), false, false, true, false, false);
- break;
- case R.id.album_menu_play_shuffled:
- artistOverride = true;
- downloadRecursively(entry.getId(), false, false, true, true, false);
- break;
- case R.id.album_menu_play_next:
- artistOverride = true;
- downloadRecursively(entry.getId(), false, true, false, false, false, true);
- break;
- case R.id.album_menu_play_last:
- artistOverride = true;
- downloadRecursively(entry.getId(), false, true, false, false, false);
- break;
- case R.id.album_menu_download:
- artistOverride = true;
- downloadRecursively(entry.getId(), false, true, false, false, true);
- break;
- case R.id.album_menu_pin:
- artistOverride = true;
- downloadRecursively(entry.getId(), true, true, false, false, true);
- break;
case R.id.album_menu_star:
toggleStarred(entry);
break;
- case R.id.album_menu_delete:
- deleteRecursively(entry);
- break;
case R.id.album_menu_info:
displaySongInfo(entry);
break;
@@ -421,21 +319,9 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
case R.id.album_menu_share:
createShare(songs);
break;
- case R.id.song_menu_play_now:
- playNow(songs);
- break;
- case R.id.song_menu_play_next:
- getDownloadService().download(songs, false, false, true, false);
- break;
- case R.id.song_menu_play_last:
- getDownloadService().download(songs, false, false, false, false);
- break;
case R.id.song_menu_download:
getDownloadService().downloadBackground(songs, false);
break;
- case R.id.song_menu_pin:
- getDownloadService().downloadBackground(songs, true);
- break;
case R.id.song_menu_delete:
getDownloadService().delete(songs);
break;
@@ -475,7 +361,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
return true;
}
-
+
public void replaceFragment(SubsonicFragment fragment) {
replaceFragment(fragment, true);
}
@@ -492,7 +378,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
public int getSupportTag() {
return tag;
}
-
+
public void setPrimaryFragment(boolean primary) {
primaryFragment = primary;
if(primary) {
@@ -631,6 +517,68 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
R.color.holo_red_light);
}
}
+ protected void setupScrollList(final RecyclerView recyclerView) {
+ if(!context.isTouchscreen()) {
+ refreshLayout.setEnabled(false);
+ } else {
+ recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
+ @Override
+ public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
+ super.onScrollStateChanged(recyclerView, newState);
+ }
+
+ @Override
+ public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
+ refreshLayout.setEnabled(!recyclerView.canScrollVertically(-1));
+ }
+ });
+
+ refreshLayout.setColorScheme(
+ R.color.holo_blue_light,
+ R.color.holo_orange_light,
+ R.color.holo_green_light,
+ R.color.holo_red_light);
+ }
+ }
+
+ public void setupLayoutManager(RecyclerView recyclerView, boolean largeAlbums) {
+ recyclerView.setLayoutManager(getLayoutManager(recyclerView, largeAlbums));
+ }
+ public RecyclerView.LayoutManager getLayoutManager(RecyclerView recyclerView, boolean largeCells) {
+ if(largeCells) {
+ return getGridLayoutManager(recyclerView);
+ } else {
+ return getLinearLayoutManager();
+ }
+ }
+ public GridLayoutManager getGridLayoutManager(RecyclerView recyclerView) {
+ final int columns = getRecyclerColumnCount();
+ GridLayoutManager gridLayoutManager = new GridLayoutManager(context, columns);
+
+ GridLayoutManager.SpanSizeLookup spanSizeLookup = getSpanSizeLookup();
+ if(spanSizeLookup != null) {
+ gridLayoutManager.setSpanSizeLookup(spanSizeLookup);
+ }
+ RecyclerView.ItemDecoration itemDecoration = getItemDecoration();
+ if(itemDecoration != null) {
+ recyclerView.addItemDecoration(itemDecoration);
+ }
+ return gridLayoutManager;
+ }
+ public LinearLayoutManager getLinearLayoutManager() {
+ LinearLayoutManager layoutManager = new LinearLayoutManager(context);
+ layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
+ return layoutManager;
+ }
+ public GridLayoutManager.SpanSizeLookup getSpanSizeLookup() {
+ return null;
+ }
+ public RecyclerView.ItemDecoration getItemDecoration() {
+ return new GridSpacingDecoration();
+ }
+ public int getRecyclerColumnCount() {
+ return context.getResources().getInteger(R.integer.Grid_Columns);
+ }
protected void warnIfStorageUnavailable() {
if (!Util.isExternalStoragePresent()) {
@@ -650,9 +598,12 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
protected void onShuffleRequested() {
if(Util.isOffline(context)) {
- Intent intent = new Intent(context, DownloadActivity.class);
- intent.putExtra(Constants.INTENT_EXTRA_NAME_SHUFFLE, true);
- Util.startActivityWithoutTransition(context, intent);
+ DownloadService downloadService = getDownloadService();
+ if(downloadService == null) {
+ return;
+ }
+ downloadService.setShufflePlayEnabled(true);
+ context.openNowPlaying();
return;
}
@@ -691,21 +642,21 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(R.string.shuffle_pick_genre)
- .setItems(names.toArray(new CharSequence[names.size()]), new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- if(which == 0) {
- genreCombo.setText("");
- } else {
- genreCombo.setText(finalNames.get(which));
- }
- }
- });
+ .setItems(names.toArray(new CharSequence[names.size()]), new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ if(which == 0) {
+ genreCombo.setText("");
+ } else {
+ genreCombo.setText(finalNames.get(which));
+ }
+ }
+ });
AlertDialog dialog = builder.create();
dialog.show();
}
@Override
- protected void error(Throwable error) {
+ protected void error(Throwable error) {
String msg;
if (error instanceof OfflineException || error instanceof ServerTooOldException) {
msg = getErrorMessage(error);
@@ -731,31 +682,34 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(R.string.shuffle_title)
- .setView(dialogView)
- .setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int id) {
- Intent intent = new Intent(context, DownloadActivity.class);
- intent.putExtra(Constants.INTENT_EXTRA_NAME_SHUFFLE, true);
- String genre;
- if(useCombo) {
- genre = genreCombo.getText().toString();
- } else {
- genre = genreBox.getText().toString();
+ .setView(dialogView)
+ .setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int id) {
+ String genre;
+ if (useCombo) {
+ genre = genreCombo.getText().toString();
+ } else {
+ genre = genreBox.getText().toString();
+ }
+ String startYear = startYearBox.getText().toString();
+ String endYear = endYearBox.getText().toString();
+
+ SharedPreferences.Editor editor = prefs.edit();
+ editor.putString(Constants.PREFERENCES_KEY_SHUFFLE_START_YEAR, startYear);
+ editor.putString(Constants.PREFERENCES_KEY_SHUFFLE_END_YEAR, endYear);
+ editor.putString(Constants.PREFERENCES_KEY_SHUFFLE_GENRE, genre);
+ editor.commit();
+
+ DownloadService downloadService = getDownloadService();
+ if (downloadService == null) {
+ return;
+ }
+ downloadService.setShufflePlayEnabled(true);
+ context.openNowPlaying();
}
- String startYear = startYearBox.getText().toString();
- String endYear = endYearBox.getText().toString();
-
- SharedPreferences.Editor editor = prefs.edit();
- editor.putString(Constants.PREFERENCES_KEY_SHUFFLE_START_YEAR, startYear);
- editor.putString(Constants.PREFERENCES_KEY_SHUFFLE_END_YEAR, endYear);
- editor.putString(Constants.PREFERENCES_KEY_SHUFFLE_GENRE, genre);
- editor.commit();
-
- Util.startActivityWithoutTransition(context, intent);
- }
- })
- .setNegativeButton(R.string.common_cancel, null);
+ })
+ .setNegativeButton(R.string.common_cancel, null);
AlertDialog dialog = builder.create();
dialog.show();
}
@@ -763,6 +717,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
public void toggleStarred(Entry entry) {
toggleStarred(entry, null);
}
+
public void toggleStarred(final Entry entry, final OnStarChange onStarChange) {
final boolean starred = !entry.isStarred();
entry.setStarred(starred);
@@ -868,9 +823,11 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
protected void downloadPlaylist(final String id, final String name, final boolean save, final boolean append, final boolean autoplay, final boolean shuffle, final boolean background) {
downloadRecursively(id, name, false, save, append, autoplay, shuffle, background);
}
+
protected void downloadRecursively(final String id, final String name, final boolean isDirectory, final boolean save, final boolean append, final boolean autoplay, final boolean shuffle, final boolean background) {
downloadRecursively(id, name, isDirectory, save, append, autoplay, shuffle, background, false);
}
+
protected void downloadRecursively(final String id, final String name, final boolean isDirectory, final boolean save, final boolean append, final boolean autoplay, final boolean shuffle, final boolean background, final boolean playNext) {
new RecursiveLoader(context) {
@Override
@@ -906,7 +863,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
playNowOverride = true;
return false;
}
-
+
if (!append && !background) {
downloadService.clear();
}
@@ -994,7 +951,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
MusicService musicService = MusicServiceFactory.getMusicService(context);
List<Playlist> playlists = new ArrayList<Playlist>();
playlists.addAll(musicService.getPlaylists(false, context, this));
-
+
// Iterate through and remove all non owned public playlists
Iterator<Playlist> it = playlists.iterator();
while(it.hasNext()) {
@@ -1003,7 +960,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
it.remove();
}
}
-
+
return playlists;
}
@@ -1016,7 +973,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
@Override
public View getView(int position, View convertView, ViewGroup parent) {
Playlist playlist = getItem(position);
-
+
// Create new if not getting a convert view to use
PlaylistSongView view;
if(convertView instanceof PlaylistSongView) {
@@ -1033,21 +990,21 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(R.string.playlist_add_to)
- .setAdapter(playlistAdapter, new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- if (which > 0) {
- addToPlaylist(playlists.get(which), songs);
- } else {
- createNewPlaylist(songs, false);
+ .setAdapter(playlistAdapter, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ if (which > 0) {
+ addToPlaylist(playlists.get(which), songs);
+ } else {
+ createNewPlaylist(songs, false);
+ }
}
- }
- });
+ });
AlertDialog dialog = builder.create();
dialog.show();
}
@Override
- protected void error(Throwable error) {
+ protected void error(Throwable error) {
String msg;
if (error instanceof OfflineException || error instanceof ServerTooOldException) {
msg = getErrorMessage(error);
@@ -1075,7 +1032,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
}
@Override
- protected void error(Throwable error) {
+ protected void error(Throwable error) {
String msg;
if (error instanceof OfflineException || error instanceof ServerTooOldException) {
msg = getErrorMessage(error);
@@ -1087,7 +1044,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
}
}.execute();
}
-
+
protected void createNewPlaylist(final List<Entry> songs, final boolean getSuggestion) {
View layout = context.getLayoutInflater().inflate(R.layout.save_playlist, null);
final EditText playlistNameView = (EditText) layout.findViewById(R.id.save_playlist_name);
@@ -1115,34 +1072,34 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(R.string.download_playlist_title)
- .setMessage(R.string.download_playlist_name)
- .setView(layout)
- .setPositiveButton(R.string.common_save, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int id) {
- String playlistName = String.valueOf(playlistNameView.getText());
- if(overwriteCheckBox.isChecked()) {
- overwritePlaylist(songs, playlistName, getDownloadService().getSuggestedPlaylistId());
- } else {
- createNewPlaylist(songs, playlistName);
-
- if(getSuggestion) {
- DownloadService downloadService = getDownloadService();
- if(downloadService != null) {
- downloadService.setSuggestedPlaylistName(playlistName, null);
+ .setMessage(R.string.download_playlist_name)
+ .setView(layout)
+ .setPositiveButton(R.string.common_save, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int id) {
+ String playlistName = String.valueOf(playlistNameView.getText());
+ if(overwriteCheckBox.isChecked()) {
+ overwritePlaylist(songs, playlistName, getDownloadService().getSuggestedPlaylistId());
+ } else {
+ createNewPlaylist(songs, playlistName);
+
+ if(getSuggestion) {
+ DownloadService downloadService = getDownloadService();
+ if(downloadService != null) {
+ downloadService.setSuggestedPlaylistName(playlistName, null);
+ }
}
}
}
- }
- })
- .setNegativeButton(R.string.common_cancel, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int id) {
- dialog.cancel();
- }
- })
- .setCancelable(true);
-
+ })
+ .setNegativeButton(R.string.common_cancel, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int id) {
+ dialog.cancel();
+ }
+ })
+ .setCancelable(true);
+
AlertDialog dialog = builder.create();
dialog.show();
}
@@ -1184,7 +1141,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
}
@Override
- protected void error(Throwable error) {
+ protected void error(Throwable error) {
String msg;
if (error instanceof OfflineException || error instanceof ServerTooOldException) {
msg = getErrorMessage(error);
@@ -1209,12 +1166,12 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
if(file.exists()) {
MediaMetadataRetriever metadata = new MediaMetadataRetriever();
metadata.setDataSource(file.getAbsolutePath());
-
+
String tmp = metadata.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
duration = Integer.parseInt((tmp != null) ? tmp : "0") / 1000;
format = FileUtil.getExtension(file.getName());
size = file.length();
-
+
// If no duration try to read bitrate tag
if(duration == null) {
tmp = metadata.extractMetadata(MediaMetadataRetriever.METADATA_KEY_BITRATE);
@@ -1224,7 +1181,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
// Divide by 1000 so in kbps
bitrate = (int) (size / duration) / 1000 * 8;
}
-
+
if(Util.isOffline(context)) {
song.setGenre(metadata.extractMetadata(MediaMetadataRetriever.METADATA_KEY_GENRE));
String year = metadata.extractMetadata(MediaMetadataRetriever.METADATA_KEY_YEAR);
@@ -1236,57 +1193,85 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
}
}
- String msg = "";
+ List<Integer> headers = new ArrayList<>();
+ List<String> details = new ArrayList<>();
if(song instanceof PodcastEpisode) {
- msg += "Podcast: " + song.getArtist() + "\nStatus: " + ((PodcastEpisode)song).getStatus();
+ headers.add(R.string.details_podcast);
+ details.add(song.getArtist());
+
+ headers.add(R.string.details_status);
+ details.add(((PodcastEpisode)song).getStatus());
} else if(!song.isVideo()) {
if(song.getArtist() != null && !"".equals(song.getArtist())) {
- msg += "Artist: " + song.getArtist();
+ headers.add(R.string.details_artist);
+ details.add(song.getArtist());
}
if(song.getAlbum() != null && !"".equals(song.getAlbum())) {
- msg += "\nAlbum: " + song.getAlbum();
+ headers.add(R.string.details_album);
+ details.add(song.getAlbum());
}
}
if(song.getTrack() != null && song.getTrack() != 0) {
- msg += "\nTrack: " + song.getTrack();
+ headers.add(R.string.details_track);
+ details.add(Integer.toString(song.getTrack()));
}
if(song.getGenre() != null && !"".equals(song.getGenre())) {
- msg += "\nGenre: " + song.getGenre();
+ headers.add(R.string.details_genre);
+ details.add(song.getGenre());
}
if(song.getYear() != null && song.getYear() != 0) {
- msg += "\nYear: " + song.getYear();
+ headers.add(R.string.details_year);
+ details.add(Integer.toString(song.getYear()));
}
if(!Util.isOffline(context) && song.getSuffix() != null) {
- msg += "\nServer Format: " + song.getSuffix();
+ headers.add(R.string.details_server_format);
+ details.add(song.getSuffix());
+
if(song.getBitRate() != null && song.getBitRate() != 0) {
- msg += "\nServer Bitrate: " + song.getBitRate() + " kbps";
+ headers.add(R.string.details_server_bitrate);
+ details.add(song.getBitRate() + " kbps");
}
}
if(format != null && !"".equals(format)) {
- msg += "\nCached Format: " + format;
+ headers.add(R.string.details_cached_format);
+ details.add(format);
}
if(bitrate != null && bitrate != 0) {
- msg += "\nCached Bitrate: " + bitrate + " kbps";
+ headers.add(R.string.details_cached_bitrate);
+ details.add(bitrate + " kbps");
}
if(size != 0) {
- msg += "\nSize: " + Util.formatLocalizedBytes(size, context);
+ headers.add(R.string.details_size);
+ details.add(Util.formatLocalizedBytes(size, context));
}
if(song.getDuration() != null && song.getDuration() != 0) {
- msg += "\nLength: " + Util.formatDuration(song.getDuration());
+ headers.add(R.string.details_length);
+ details.add(Util.formatDuration(song.getDuration()));
}
if(song.getBookmark() != null) {
- msg += "\nBookmark Position: " + Util.formatDuration(song.getBookmark().getPosition() / 1000);
+ headers.add(R.string.details_bookmark_position);
+ details.add(Util.formatDuration(song.getBookmark().getPosition() / 1000));
}
if(song.getRating() != 0) {
- msg += "\nRating: " + song.getRating() + " stars";
+ headers.add(R.string.details_rating);
+ details.add(song.getRating() + " stars");
}
if(song instanceof PodcastEpisode) {
- msg += "\n\nDescription: " + song.getAlbum();
+ headers.add(R.string.details_description);
+ details.add(song.getAlbum());
}
- Util.info(context, song.getTitle(), msg);
+ int title;
+ if(song.isDirectory()) {
+ title = R.string.details_title_album;
+ } else if(song instanceof PodcastEpisode) {
+ title = R.string.details_title_podcast;
+ } else {
+ title = R.string.details_title_song;
+ }
+ Util.showDetailsDialog(context, title, headers, details);
}
-
+
protected void playVideo(Entry entry) {
if(entryExists(entry)) {
playExternalPlayer(entry);
@@ -1315,7 +1300,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
intent.putExtra(Intent.EXTRA_TITLE, entry.getTitle());
List<ResolveInfo> intents = context.getPackageManager()
- .queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
+ .queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
if(intents != null && intents.size() > 0) {
startActivity(intent);
}else {
@@ -1373,11 +1358,12 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
public void deleteRecursively(Artist artist) {
deleteRecursively(FileUtil.getArtistDirectory(context, artist));
}
-
+
public void deleteRecursively(Entry album) {
deleteRecursively(FileUtil.getAlbumDirectory(context, album));
}
+
public void deleteRecursively(final File dir) {
if(dir == null) {
return;
@@ -1434,6 +1420,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
replaceFragment(fragment, true);
}
+
public void showAlbum(Entry entry) {
SubsonicFragment fragment = new SelectDirectoryFragment();
Bundle args = new Bundle();
@@ -1490,7 +1477,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
intent.putExtra(Intent.EXTRA_TEXT, share.getUrl());
context.startActivity(Intent.createChooser(intent, context.getResources().getString(R.string.share_via)));
}
-
+
public GestureDetector getGestureDetector() {
return gestureScanner;
}
@@ -1498,6 +1485,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
protected void playBookmark(List<Entry> songs, Entry song) {
playBookmark(songs, song, null, null);
}
+
protected void playBookmark(final List<Entry> songs, final Entry song, final String playlistName, final String playlistId) {
final Integer position = song.getBookmark().getPosition();
@@ -1515,7 +1503,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
public void onClick(DialogInterface dialog, int id) {
final Bookmark oldBookmark = song.getBookmark();
song.setBookmark(null);
-
+
new SilentBackgroundTask<Void>(context) {
@Override
protected Void doInBackground() throws Throwable {
@@ -1528,7 +1516,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
@Override
protected void error(Throwable error) {
song.setBookmark(oldBookmark);
-
+
String msg;
if (error instanceof OfflineException || error instanceof ServerTooOldException) {
msg = getErrorMessage(error);
@@ -1546,26 +1534,37 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
AlertDialog dialog = builder.create();
dialog.show();
}
-
+
protected void playNow(List<Entry> entries) {
playNow(entries, null, null);
}
- protected void playNow(List<Entry> entries, String playlistName, String playlistId) {
- Entry bookmark = null;
- for(Entry entry: entries) {
- if(entry.getBookmark() != null) {
- bookmark = entry;
- break;
+ protected void playNow(final List<Entry> entries, final String playlistName, final String playlistId) {
+ new RecursiveLoader(context) {
+ @Override
+ protected Boolean doInBackground() throws Throwable {
+ getSongsRecursively(entries, songs);
+ return null;
}
- }
-
- // If no bookmark found, just play from start
- if(bookmark == null) {
- playNow(entries, 0, playlistName, playlistId);
- } else {
- // If bookmark found, then give user choice to start from there or to start over
- playBookmark(entries, bookmark, playlistName, playlistId);
- }
+
+ @Override
+ protected void done(Boolean result) {
+ Entry bookmark = null;
+ for(Entry entry: songs) {
+ if(entry.getBookmark() != null) {
+ bookmark = entry;
+ break;
+ }
+ }
+
+ // If no bookmark found, just play from start
+ if(bookmark == null) {
+ playNow(songs, 0, playlistName, playlistId);
+ } else {
+ // If bookmark found, then give user choice to start from there or to start over
+ playBookmark(songs, bookmark, playlistName, playlistId);
+ }
+ }
+ }.execute();
}
protected void playNow(List<Entry> entries, int position) {
playNow(entries, position, null, null);
@@ -1574,9 +1573,11 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
Entry selected = entries.isEmpty() ? null : entries.get(0);
playNow(entries, selected, position, playlistName, playlistId);
}
+
protected void playNow(List<Entry> entries, Entry song, int position) {
playNow(entries, song, position, null, null);
}
+
protected void playNow(final List<Entry> entries, final Entry song, final int position, final String playlistName, final String playlistId) {
new LoadingTask<Void>(context) {
@Override
@@ -1585,22 +1586,22 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
if(downloadService == null) {
return null;
}
-
+
downloadService.clear();
downloadService.download(entries, false, true, true, false, entries.indexOf(song), position);
downloadService.setSuggestedPlaylistName(playlistName, playlistId);
return null;
}
-
+
@Override
protected void done(Void result) {
- Util.startActivityWithoutTransition(context, DownloadActivity.class);
+ context.openNowPlaying();
}
}.execute();
}
- protected void deleteBookmark(final MusicDirectory.Entry entry, final ArrayAdapter adapter) {
+ protected void deleteBookmark(final MusicDirectory.Entry entry, final SectionAdapter adapter) {
Util.confirmDialog(context, R.string.bookmark_delete_title, entry.getTitle(), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
@@ -1626,8 +1627,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
@Override
protected void done(Void result) {
if (adapter != null) {
- adapter.remove(entry);
- adapter.notifyDataSetChanged();
+ adapter.removeItem(entry);
}
Util.toast(context, context.getResources().getString(R.string.bookmark_deleted, entry.getTitle()));
}
@@ -1657,19 +1657,19 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
View layout = context.getLayoutInflater().inflate(R.layout.rating, null);
final RatingBar ratingBar = (RatingBar) layout.findViewById(R.id.rating_bar);
ratingBar.setRating((float) entry.getRating());
-
+
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(context.getResources().getString(R.string.rating_title, entry.getTitle()))
- .setView(layout)
- .setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int id) {
- int rating = (int) ratingBar.getRating();
- setRating(entry, rating, onRatingChange);
- }
- })
- .setNegativeButton(R.string.common_cancel, null);
-
+ .setView(layout)
+ .setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int id) {
+ int rating = (int) ratingBar.getRating();
+ setRating(entry, rating, onRatingChange);
+ }
+ })
+ .setNegativeButton(R.string.common_cancel, null);
+
AlertDialog dialog = builder.create();
dialog.show();
}
@@ -1684,7 +1684,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
if(onRatingChange != null) {
onRatingChange.ratingChange(rating);
}
-
+
new SilentBackgroundTask<Void>(context) {
@Override
protected Void doInBackground() throws Throwable {
@@ -1711,7 +1711,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
if(onRatingChange != null) {
onRatingChange.ratingChange(oldRating);
}
-
+
String msg;
if (error instanceof OfflineException || error instanceof ServerTooOldException) {
msg = getErrorMessage(error);
@@ -1723,16 +1723,134 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
}
}.execute();
}
-
+
+ protected SectionAdapter getCurrentAdapter() { return null; }
+ public void stopActionMode() {
+ SectionAdapter adapter = getCurrentAdapter();
+ if(adapter != null) {
+ adapter.stopActionMode();
+ }
+ }
+ protected void clearSelected() {
+ if(getCurrentAdapter() != null) {
+ getCurrentAdapter().clearSelected();
+ }
+ }
+ protected List<Entry> getSelectedEntries() {
+ return getCurrentAdapter().getSelected();
+ }
+
+ protected void playNow(final boolean shuffle, final boolean append) {
+ playNow(shuffle, append, false);
+ }
+ protected void playNow(final boolean shuffle, final boolean append, final boolean playNext) {
+ List<Entry> songs = getSelectedEntries();
+ if(!songs.isEmpty()) {
+ download(songs, append, false, !append, playNext, shuffle);
+ clearSelected();
+ }
+ }
+
+ protected void download(List<Entry> entries, boolean append, boolean save, boolean autoplay, boolean playNext, boolean shuffle) {
+ download(entries, append, save, autoplay, playNext, shuffle, null, null);
+ }
+ protected void download(final List<Entry> entries, final boolean append, final boolean save, final boolean autoplay, final boolean playNext, final boolean shuffle, final String playlistName, final String playlistId) {
+ final DownloadService downloadService = getDownloadService();
+ if (downloadService == null) {
+ return;
+ }
+ warnIfStorageUnavailable();
+
+ // Conditions for using play now button
+ if(!append && !save && autoplay && !playNext && !shuffle) {
+ // Call playNow which goes through and tries to use bookmark information
+ playNow(entries, playlistName, playlistId);
+ return;
+ }
+
+ RecursiveLoader onValid = new RecursiveLoader(context) {
+ @Override
+ protected Boolean doInBackground() throws Throwable {
+ if (!append) {
+ getDownloadService().clear();
+ }
+ getSongsRecursively(entries, songs);
+
+ downloadService.download(songs, save, autoplay, playNext, shuffle);
+ if (playlistName != null) {
+ downloadService.setSuggestedPlaylistName(playlistName, playlistId);
+ } else {
+ downloadService.setSuggestedPlaylistName(null, null);
+ }
+ return null;
+ }
+
+ @Override
+ protected void done(Boolean result) {
+ if (autoplay) {
+ context.openNowPlaying();
+ } else if (save) {
+ Util.toast(context,
+ context.getResources().getQuantityString(R.plurals.select_album_n_songs_downloading, songs.size(), songs.size()));
+ } else if (append) {
+ Util.toast(context,
+ context.getResources().getQuantityString(R.plurals.select_album_n_songs_added, songs.size(), songs.size()));
+ }
+ }
+ };
+
+ executeOnValid(onValid);
+ }
+ protected void executeOnValid(RecursiveLoader onValid) {
+ onValid.execute();
+ }
+ protected void downloadBackground(final boolean save) {
+ List<Entry> songs = getSelectedEntries();
+ if(!songs.isEmpty()) {
+ downloadBackground(save, songs);
+ }
+ }
+
+ protected void downloadBackground(final boolean save, final List<Entry> entries) {
+ if (getDownloadService() == null) {
+ return;
+ }
+
+ warnIfStorageUnavailable();
+ new RecursiveLoader(context) {
+ @Override
+ protected Boolean doInBackground() throws Throwable {
+ getSongsRecursively(entries, songs);
+ getDownloadService().downloadBackground(songs, save);
+ return null;
+ }
+
+ @Override
+ protected void done(Boolean result) {
+ Util.toast(context, context.getResources().getQuantityString(R.plurals.select_album_n_songs_downloading, songs.size(), songs.size()));
+ }
+ }.execute();
+ }
+
+ protected void delete() {
+ List<Entry> songs = getSelectedEntries();
+ if(!songs.isEmpty()) {
+ DownloadService downloadService = getDownloadService();
+ if(downloadService != null) {
+ downloadService.delete(songs);
+ }
+ }
+ }
+
protected abstract class EntryInstanceUpdater {
private Entry entry;
-
+
public EntryInstanceUpdater(Entry entry) {
this.entry = entry;
}
-
+
public abstract void update(Entry found);
-
+
public void execute() {
DownloadService downloadService = getDownloadService();
if(downloadService != null && !entry.isDirectory()) {
@@ -1745,12 +1863,12 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
serializeChanges = true;
}
}
-
+
if(serializeChanges) {
downloadService.serializeQueue();
}
}
-
+
Entry find = UpdateView.findEntry(entry);
if(find != null) {
update(find);
@@ -1769,22 +1887,26 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
protected MusicService musicService;
protected static final int MAX_SONGS = 500;
protected boolean playNowOverride = false;
- protected List<Entry> songs;
+ protected List<Entry> songs = new ArrayList<>();
public RecursiveLoader(Activity context) {
super(context);
+ musicService = MusicServiceFactory.getMusicService(context);
}
+ protected void getSongsRecursively(List<Entry> entry) throws Exception {
+ getSongsRecursively(entry, songs);
+ }
+ protected void getSongsRecursively(List<Entry> entry, List<Entry> songs) throws Exception {
+ MusicDirectory dir = new MusicDirectory();
+ dir.addChildren(entry);
+ getSongsRecursively(dir, songs);
+ }
protected void getSongsRecursively(MusicDirectory parent, List<Entry> songs) throws Exception {
if (songs.size() > MAX_SONGS) {
return;
}
- for (Entry song : parent.getChildren(false, true)) {
- if (!song.isVideo() && song.getRating() != 1) {
- songs.add(song);
- }
- }
for (Entry dir : parent.getChildren(true, false)) {
if(dir.getRating() == 1) {
continue;
@@ -1798,6 +1920,12 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
}
getSongsRecursively(musicDirectory, songs);
}
+
+ for (Entry song : parent.getChildren(false, true)) {
+ if (!song.isVideo() && song.getRating() != 1) {
+ songs.add(song);
+ }
+ }
}
@Override
@@ -1810,7 +1938,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
}
if(result) {
- Util.startActivityWithoutTransition(context, DownloadActivity.class);
+ context.openNowPlaying();
}
}
}
diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/UserFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/UserFragment.java
index 00c7c603..bdd0472b 100644
--- a/app/src/main/java/github/daneren2005/dsub/fragments/UserFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/UserFragment.java
@@ -15,71 +15,33 @@
package github.daneren2005.dsub.fragments;
-import android.app.Activity;
import android.os.Bundle;
-import android.support.v4.widget.SwipeRefreshLayout;
-import android.view.LayoutInflater;
import android.view.Menu;
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;
+
+import java.util.List;
import github.daneren2005.dsub.R;
-import github.daneren2005.dsub.activity.SubsonicActivity;
+import github.daneren2005.dsub.adapter.SectionAdapter;
import github.daneren2005.dsub.domain.ServerInfo;
import github.daneren2005.dsub.domain.User;
+import github.daneren2005.dsub.service.MusicService;
import github.daneren2005.dsub.util.Constants;
-import github.daneren2005.dsub.util.ImageLoader;
+import github.daneren2005.dsub.util.ProgressListener;
import github.daneren2005.dsub.util.UserUtil;
import github.daneren2005.dsub.adapter.SettingsAdapter;
+import github.daneren2005.dsub.view.UpdateView;
-public class UserFragment extends SubsonicFragment{
- private ListView listView;
+public class UserFragment extends SelectRecyclerFragment<User.Setting>{
private User user;
@Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
- rootView = inflater.inflate(R.layout.abstract_list_fragment, container, false);
-
- refreshLayout = (SwipeRefreshLayout) rootView.findViewById(R.id.refresh_layout);
- refreshLayout.setEnabled(false);
-
+ public void onCreate(Bundle bundle) {
+ super.onCreate(bundle);
Bundle args = getArguments();
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() && ServerInfo.checkServerVersion(context, "1.10")));
-
- setTitle(user.getUsername());
-
- return rootView;
- }
-
- @Override
- public void onAttach(Activity activity) {
- super.onAttach(activity);
- ((SubsonicActivity) activity).supportInvalidateOptionsMenu();
- }
-
- @Override
- public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) {
- // For some reason this is called before onAttach
- if(!primaryFragment || context == null) {
- return;
- }
-
- if(UserUtil.isCurrentAdmin() && ServerInfo.checkServerVersion(context, "1.10")) {
- menuInflater.inflate(R.menu.user, menu);
- } else if(UserUtil.isCurrentRole(User.SETTINGS)) {
- menuInflater.inflate(R.menu.user_user, menu);
- } else {
- menuInflater.inflate(R.menu.empty, menu);
- }
+ pullToRefresh = false;
}
@Override
@@ -103,23 +65,43 @@ public class UserFragment extends SubsonicFragment{
return false;
}
- private void createHeader() {
- View header = LayoutInflater.from(context).inflate(R.layout.user_header, listView, false);
+ @Override
+ public int getOptionsMenu() {
+ if(UserUtil.isCurrentAdmin() && ServerInfo.checkServerVersion(context, "1.10")) {
+ return R.menu.user;
+ } else if(UserUtil.isCurrentRole(User.SETTINGS)) {
+ return R.menu.user_user;
+ } else {
+ return R.menu.empty;
+ }
+ }
+
+ @Override
+ public SectionAdapter<User.Setting> getAdapter(List<User.Setting> objs) {
+ return new SettingsAdapter(context, user, getImageLoader(), UserUtil.isCurrentAdmin() && ServerInfo.checkServerVersion(context, "1.10"));
+ }
- final ImageLoader imageLoader = getImageLoader();
- ImageView coverArtView = (ImageView) header.findViewById(R.id.user_avatar);
- imageLoader.loadAvatar(context, coverArtView, user.getUsername());
+ @Override
+ public List<User.Setting> getObjects(MusicService musicService, boolean refresh, ProgressListener listener) throws Exception {
+ return user.getSettings();
+ }
- TextView usernameView = (TextView) header.findViewById(R.id.user_username);
- usernameView.setText(user.getUsername());
+ @Override
+ public int getTitleResource() {
+ setTitle(user.getUsername());
+ return 0;
+ }
- final TextView emailView = (TextView) header.findViewById(R.id.user_email);
- if(user.getEmail() != null) {
- emailView.setText(user.getEmail());
- } else {
- emailView.setVisibility(View.GONE);
- }
+ @Override
+ public void onItemClicked(User.Setting item) {
+
+ }
+
+ @Override
+ public void onCreateContextMenu(Menu menu, MenuInflater menuInflater, UpdateView<User.Setting> updateView, User.Setting item) {}
- listView.addHeaderView(header);
+ @Override
+ public boolean onContextItemSelected(MenuItem menuItem, UpdateView<User.Setting> updateView, User.Setting item) {
+ return false;
}
}
diff --git a/app/src/main/java/github/daneren2005/dsub/provider/DSubWidgetProvider.java b/app/src/main/java/github/daneren2005/dsub/provider/DSubWidgetProvider.java
index 444b6cff..7cb3ce8b 100644
--- a/app/src/main/java/github/daneren2005/dsub/provider/DSubWidgetProvider.java
+++ b/app/src/main/java/github/daneren2005/dsub/provider/DSubWidgetProvider.java
@@ -39,7 +39,6 @@ import android.util.Log;
import android.view.View;
import android.widget.RemoteViews;
import github.daneren2005.dsub.R;
-import github.daneren2005.dsub.activity.DownloadActivity;
import github.daneren2005.dsub.activity.SubsonicActivity;
import github.daneren2005.dsub.activity.SubsonicFragmentActivity;
import github.daneren2005.dsub.domain.MusicDirectory;
@@ -270,9 +269,7 @@ public class DSubWidgetProvider extends AppWidgetProvider {
/**
* Link up various button actions using {@link PendingIntent}.
*
- * @param playerActive True if player is active in background, which means
- * widget click will launch {@link DownloadActivity},
- * otherwise we launch {@link github.daneren2005.dsub.activity.SubsonicFragmentActivity}.
+ * @param playerActive @param playerActive True if player is active in background. Launch {@link github.daneren2005.dsub.activity.SubsonicFragmentActivity}.
*/
private void linkButtons(Context context, RemoteViews views, boolean playerActive) {
Intent intent = new Intent(context, SubsonicFragmentActivity.class);
diff --git a/app/src/main/java/github/daneren2005/dsub/service/CachedMusicService.java b/app/src/main/java/github/daneren2005/dsub/service/CachedMusicService.java
index 3bc7f2a3..f053c215 100644
--- a/app/src/main/java/github/daneren2005/dsub/service/CachedMusicService.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/CachedMusicService.java
@@ -51,7 +51,6 @@ 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.util.Pair;
import github.daneren2005.dsub.util.SilentBackgroundTask;
import github.daneren2005.dsub.util.ProgressListener;
import github.daneren2005.dsub.util.TimeLimitedCache;
@@ -384,9 +383,9 @@ public class CachedMusicService implements MusicService {
}
@Override
- public MusicDirectory getAlbumList(String type, int size, int offset, Context context, ProgressListener progressListener) throws Exception {
+ public MusicDirectory getAlbumList(String type, int size, int offset, boolean refresh, Context context, ProgressListener progressListener) throws Exception {
try {
- MusicDirectory dir = musicService.getAlbumList(type, size, offset, context, progressListener);
+ MusicDirectory dir = musicService.getAlbumList(type, size, offset, refresh, context, progressListener);
// Do some serialization updates for changes to recently added
if ("newest".equals(type) && offset == 0) {
@@ -490,6 +489,10 @@ public class CachedMusicService implements MusicService {
return dir;
} catch(IOException e) {
Log.w(TAG, "Failed to refresh album list: ", e);
+ if(refresh) {
+ throw e;
+ }
+
MusicDirectory dir = FileUtil.deserialize(context, getCacheName(context, type, Integer.toString(offset)), MusicDirectory.class);
if(dir == null) {
@@ -507,12 +510,17 @@ public class CachedMusicService implements MusicService {
}
@Override
- public MusicDirectory getAlbumList(String type, String extra, int size, int offset, Context context, ProgressListener progressListener) throws Exception {
+ public MusicDirectory getAlbumList(String type, String extra, int size, int offset, boolean refresh, Context context, ProgressListener progressListener) throws Exception {
try {
- MusicDirectory dir = musicService.getAlbumList(type, extra, size, offset, context, progressListener);
+ MusicDirectory dir = musicService.getAlbumList(type, extra, size, offset, refresh, context, progressListener);
FileUtil.serialize(context, dir, getCacheName(context, type + extra, Integer.toString(offset)));
return dir;
} catch(IOException e) {
+ Log.w(TAG, "Failed to refresh album list: ", e);
+ if(refresh) {
+ throw e;
+ }
+
MusicDirectory dir = FileUtil.deserialize(context, getCacheName(context, type + extra, Integer.toString(offset)), MusicDirectory.class);
if(dir == null) {
diff --git a/app/src/main/java/github/daneren2005/dsub/service/DownloadService.java b/app/src/main/java/github/daneren2005/dsub/service/DownloadService.java
index ae2eb073..211cf0d7 100644
--- a/app/src/main/java/github/daneren2005/dsub/service/DownloadService.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/DownloadService.java
@@ -139,6 +139,7 @@ public class DownloadService extends Service {
private boolean removePlayed;
private boolean shufflePlay;
private boolean artistRadio;
+ private final List<OnSongChangedListener> onSongChangedListeners = new ArrayList<>();
private long revision;
private static DownloadService instance;
private String suggestedPlaylistName;
@@ -156,6 +157,7 @@ public class DownloadService extends Service {
private Timer sleepTimer;
private int timerDuration;
+ private long timerStart;
private boolean autoPlayStart = false;
private MediaRouteManager mediaRouter;
@@ -362,7 +364,6 @@ public class DownloadService extends Service {
}
}
setNextPlaying();
- revision++;
} else {
int size = size();
int index = getCurrentPlayingIndex();
@@ -378,8 +379,9 @@ public class DownloadService extends Service {
if(!autoplay && (size - 1) == index) {
setNextPlaying();
}
- revision++;
}
+ revision++;
+ onSongsChanged();
updateRemotePlaylist();
if(shuffle) {
@@ -543,6 +545,7 @@ public class DownloadService extends Service {
currentPlayingIndex = 0;
}
revision++;
+ onSongsChanged();
lifecycleSupport.serializeDownloadQueue();
updateRemotePlaylist();
setNextPlaying();
@@ -601,10 +604,6 @@ public class DownloadService extends Service {
return downloadFile;
}
- public synchronized void clear() {
- clear(true);
- }
-
public synchronized void clearBackground() {
if(currentDownloading != null && backgroundDownloadList.contains(currentDownloading)) {
currentDownloading.cancelDownload();
@@ -666,6 +665,9 @@ public class DownloadService extends Service {
return downloadList.size();
}
+ public synchronized void clear() {
+ clear(true);
+ }
public synchronized void clear(boolean serialize) {
// Delete podcast if fully listened to
int position = getPlayerPosition();
@@ -694,7 +696,7 @@ public class DownloadService extends Service {
reset();
downloadList.clear();
- revision++;
+ onSongsChanged();
if (currentDownloading != null && !backgroundDownloadList.contains(currentDownloading)) {
currentDownloading.cancelDownload();
currentDownloading = null;
@@ -733,12 +735,23 @@ public class DownloadService extends Service {
currentPlayingIndex = downloadList.indexOf(currentPlaying);
backgroundDownloadList.remove(downloadFile);
revision++;
+ onSongsChanged();
lifecycleSupport.serializeDownloadQueue();
updateRemotePlaylist();
if(downloadFile == nextPlaying) {
setNextPlaying();
}
}
+ public synchronized void removeBackground(DownloadFile downloadFile) {
+ if (downloadFile == currentDownloading && downloadFile != currentPlaying && downloadFile != nextPlaying) {
+ currentDownloading.cancelDownload();
+ currentDownloading = null;
+ }
+
+ backgroundDownloadList.remove(downloadFile);
+ revision++;
+ checkDownloads();
+ }
public synchronized void delete(List<MusicDirectory.Entry> songs) {
for (MusicDirectory.Entry song : songs) {
@@ -779,6 +792,7 @@ public class DownloadService extends Service {
Util.broadcastNewTrackInfo(this, null);
Notifications.hidePlayingNotification(this, this, handler);
}
+ onSongChanged();
}
synchronized void setNextPlaying() {
@@ -886,11 +900,15 @@ public class DownloadService extends Service {
public synchronized void play(int index) {
play(index, true);
}
+ public synchronized void play(DownloadFile downloadFile) {
+ play(downloadList.indexOf(downloadFile));
+ }
private synchronized void play(int index, boolean start) {
play(index, start, 0);
}
private synchronized void play(int index, boolean start, int position) {
int size = this.size();
+ cachedPosition = 0;
if (index < 0 || index >= size) {
reset();
if(index >= size && size != 0) {
@@ -1271,6 +1289,7 @@ public class DownloadService extends Service {
positionCache.stop();
positionCache = null;
}
+ onStateUpdate();
}
private class PositionCache implements Runnable {
@@ -1305,6 +1324,7 @@ public class DownloadService extends Service {
subtractNextPosition = 0;
}
}
+ onSongProgress();
Thread.sleep(1000L);
}
catch(Exception e) {
@@ -1790,6 +1810,11 @@ public class DownloadService extends Service {
}
}, timerDuration * 60 * 1000);
+ timerStart = System.currentTimeMillis();
+ }
+
+ public int getSleepTimeRemaining() {
+ return (int) (timerStart + (timerDuration * 60 * 1000) - System.currentTimeMillis()) / 1000;
}
public void stopSleepTimer() {
@@ -1818,6 +1843,10 @@ public class DownloadService extends Service {
applyReplayGain(mediaPlayer, currentPlaying);
}
+ public synchronized void swap(boolean mainList, DownloadFile from, DownloadFile to) {
+ List<DownloadFile> list = mainList ? downloadList : backgroundDownloadList;
+ swap(mainList, list.indexOf(from), list.indexOf(to));
+ }
public synchronized void swap(boolean mainList, int from, int to) {
List<DownloadFile> list = mainList ? downloadList : backgroundDownloadList;
int max = list.size();
@@ -1972,12 +2001,18 @@ public class DownloadService extends Service {
}
private synchronized void checkRemovePlayed() {
+ boolean changed = false;
SharedPreferences prefs = Util.getPreferences(this);
int keepCount = Integer.parseInt(prefs.getString(Constants.PREFERENCES_KEY_KEEP_PLAYED_CNT, "0"));
while(currentPlayingIndex > keepCount) {
downloadList.remove(0);
currentPlayingIndex = downloadList.indexOf(currentPlaying);
+ changed = true;
+ }
+
+ if(changed) {
revision++;
+ onSongsChanged();
}
}
@@ -2015,6 +2050,7 @@ public class DownloadService extends Service {
currentPlayingIndex = downloadList.indexOf(currentPlaying);
if (revisionBefore != revision) {
+ onSongsChanged();
updateRemotePlaylist();
}
@@ -2056,6 +2092,7 @@ public class DownloadService extends Service {
currentPlayingIndex = downloadList.indexOf(currentPlaying);
if (revisionBefore != revision) {
+ onSongsChanged();
updateRemotePlaylist();
}
@@ -2323,6 +2360,87 @@ public class DownloadService extends Service {
}
}
+ public void addOnSongChangedListener(OnSongChangedListener listener) {
+ addOnSongChangedListener(listener, false);
+ }
+ public void addOnSongChangedListener(OnSongChangedListener listener, boolean run) {
+ int index = onSongChangedListeners.indexOf(listener);
+ if(index == -1) {
+ onSongChangedListeners.add(listener);
+ }
+
+ if(run) {
+ onSongsChanged();
+ onSongProgress();
+ onStateUpdate();
+ }
+ }
+ public void removeOnSongChangeListener(OnSongChangedListener listener) {
+ int index = onSongChangedListeners.indexOf(listener);
+ if(index != -1) {
+ onSongChangedListeners.remove(index);
+ }
+ }
+
+ private void onSongChanged() {
+ final long atRevision = revision;
+ for(final OnSongChangedListener listener: onSongChangedListeners) {
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ if(revision == atRevision) {
+ listener.onSongChanged(currentPlaying, currentPlayingIndex);
+ }
+ }
+ });
+ }
+
+ if(playerState != STARTED) {
+ onSongProgress();
+ }
+ }
+ private void onSongsChanged() {
+ final long atRevision = revision;
+ for(final OnSongChangedListener listener: onSongChangedListeners) {
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ if(revision == atRevision) {
+ listener.onSongsChanged(downloadList, currentPlaying, currentPlayingIndex);
+ }
+ }
+ });
+ }
+ }
+ private void onSongProgress() {
+ final long atRevision = revision;
+ final Integer duration = getPlayerDuration();
+ final boolean isSeekable = isSeekable();
+ for(final OnSongChangedListener listener: onSongChangedListeners) {
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ if(revision == atRevision) {
+ listener.onSongProgress(currentPlaying, cachedPosition, duration, isSeekable);
+ }
+ }
+ });
+ }
+ }
+ private void onStateUpdate() {
+ final long atRevision = revision;
+ for(final OnSongChangedListener listener: onSongChangedListeners) {
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ if(revision == atRevision) {
+ listener.onStateUpdate(currentPlaying, playerState);
+ }
+ }
+ });
+ }
+ }
+
private class BufferTask extends SilentBackgroundTask<Void> {
private final DownloadFile downloadFile;
private final int position;
@@ -2426,4 +2544,11 @@ public class DownloadService extends Service {
return "CheckCompletionTask (" + downloadFile + ")";
}
}
+
+ public interface OnSongChangedListener {
+ void onSongChanged(DownloadFile currentPlaying, int currentPlayingIndex);
+ void onSongsChanged(List<DownloadFile> songs, DownloadFile currentPlaying, int currentPlayingIndex);
+ void onSongProgress(DownloadFile currentPlaying, int millisPlayed, Integer duration, boolean isSeekable);
+ void onStateUpdate(DownloadFile downloadFile, PlayerState playerState);
+ }
}
diff --git a/app/src/main/java/github/daneren2005/dsub/service/MusicService.java b/app/src/main/java/github/daneren2005/dsub/service/MusicService.java
index 4d014462..0cc8d484 100644
--- a/app/src/main/java/github/daneren2005/dsub/service/MusicService.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/MusicService.java
@@ -90,9 +90,9 @@ public interface MusicService {
void scrobble(String id, boolean submission, Context context, ProgressListener progressListener) throws Exception;
- MusicDirectory getAlbumList(String type, int size, int offset, Context context, ProgressListener progressListener) throws Exception;
+ MusicDirectory getAlbumList(String type, int size, int offset, boolean refresh, Context context, ProgressListener progressListener) throws Exception;
- MusicDirectory getAlbumList(String type, String extra, int size, int offset, Context context, ProgressListener progressListener) throws Exception;
+ MusicDirectory getAlbumList(String type, String extra, int size, int offset, boolean refresh, Context context, ProgressListener progressListener) throws Exception;
MusicDirectory getRandomSongs(int size, String artistId, Context context, ProgressListener progressListener) throws Exception;
MusicDirectory getRandomSongs(int size, String folder, String genre, String startYear, String endYear, Context context, ProgressListener progressListener) throws Exception;
diff --git a/app/src/main/java/github/daneren2005/dsub/service/OfflineMusicService.java b/app/src/main/java/github/daneren2005/dsub/service/OfflineMusicService.java
index b4105d07..82716fa8 100644
--- a/app/src/main/java/github/daneren2005/dsub/service/OfflineMusicService.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/OfflineMusicService.java
@@ -380,8 +380,46 @@ public class OfflineMusicService implements MusicService {
for(File file: fileList) {
if(FileUtil.isPlaylistFile(file)) {
String id = file.getName();
- String filename = server + ": " + FileUtil.getBaseName(id);
- Playlist playlist = new Playlist(server, filename);
+ String filename = FileUtil.getBaseName(id);
+ String name = server + ": " + filename;
+ Playlist playlist = new Playlist(server, name);
+ playlist.setComment(filename);
+
+ Reader reader = null;
+ BufferedReader buffer = null;
+ try {
+ int songCount = 0;
+ reader = new FileReader(file);
+ buffer = new BufferedReader(reader);
+
+ String line = buffer.readLine();
+ while( (line = buffer.readLine()) != null ){
+ // No matter what, end file can't have .complete in it
+ line = line.replace(".complete", "");
+ File entryFile = new File(line);
+
+ // Don't add file to playlist if it doesn't exist as cached or pinned!
+ File checkFile = entryFile;
+ if(!checkFile.exists()) {
+ // If normal file doens't exist, check if .complete version does
+ checkFile = new File(entryFile.getParent(), FileUtil.getBaseName(entryFile.getName())
+ + ".complete." + FileUtil.getExtension(entryFile.getName()));
+ }
+
+ String entryName = getName(entryFile);
+ if(checkFile.exists() && entryName != null){
+ songCount++;
+ }
+ }
+
+ playlist.setSongCount(Integer.toString(songCount));
+ } catch(Exception e) {
+ Log.w(TAG, "Failed to count songs in playlist", e);
+ } finally {
+ Util.close(buffer);
+ Util.close(reader);
+ }
+
playlists.add(playlist);
}
}
@@ -523,12 +561,12 @@ public class OfflineMusicService implements MusicService {
}
@Override
- public MusicDirectory getAlbumList(String type, int size, int offset, Context context, ProgressListener progressListener) throws Exception {
+ public MusicDirectory getAlbumList(String type, int size, int offset, boolean refresh, Context context, ProgressListener progressListener) throws Exception {
throw new OfflineException(ERRORMSG);
}
@Override
- public MusicDirectory getAlbumList(String type, String extra, int size, int offset, Context context, ProgressListener progressListener) throws Exception {
+ public MusicDirectory getAlbumList(String type, String extra, int size, int offset, boolean refresh, Context context, ProgressListener progressListener) throws Exception {
throw new OfflineException(ERRORMSG);
}
@@ -684,10 +722,16 @@ public class OfflineMusicService implements MusicService {
for(File file: dir.listFiles()) {
BufferedReader br = new BufferedReader(new FileReader(file));
while ((line = br.readLine()) != null && !"".equals(line)) {
+ String[] parts = line.split("\t");
+
PodcastChannel channel = new PodcastChannel();
- channel.setId(line);
- channel.setName(line);
+ channel.setId(parts[0]);
+ channel.setName(parts[0]);
channel.setStatus("completed");
+
+ if(parts.length > 1) {
+ channel.setUrl(parts[1]);
+ }
if(FileUtil.getPodcastDirectory(context, channel).exists() && !channels.contains(channel)) {
channels.add(channel);
diff --git a/app/src/main/java/github/daneren2005/dsub/service/RESTMusicService.java b/app/src/main/java/github/daneren2005/dsub/service/RESTMusicService.java
index 459c8c9e..78a7ec51 100644
--- a/app/src/main/java/github/daneren2005/dsub/service/RESTMusicService.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/RESTMusicService.java
@@ -522,7 +522,7 @@ public class RESTMusicService implements MusicService {
}
@Override
- public MusicDirectory getAlbumList(String type, int size, int offset, Context context, ProgressListener progressListener) throws Exception {
+ public MusicDirectory getAlbumList(String type, int size, int offset, boolean refresh, Context context, ProgressListener progressListener) throws Exception {
List<String> names = new ArrayList<String>();
List<Object> values = new ArrayList<Object>();
@@ -553,7 +553,7 @@ public class RESTMusicService implements MusicService {
}
@Override
- public MusicDirectory getAlbumList(String type, String extra, int size, int offset, Context context, ProgressListener progressListener) throws Exception {
+ public MusicDirectory getAlbumList(String type, String extra, int size, int offset, boolean refresh, Context context, ProgressListener progressListener) throws Exception {
checkServerVersion(context, "1.10.1", "This type of album list is not supported");
List<String> names = new ArrayList<String>();
@@ -1204,7 +1204,7 @@ public class RESTMusicService implements MusicService {
String content = "";
for(PodcastChannel channel: channels) {
- content += channel.getName() + "\n";
+ content += channel.getName() + "\t" + channel.getUrl() + "\n";
}
File file = FileUtil.getPodcastFile(context, Util.getServerName(context, getInstance(context)));
diff --git a/app/src/main/java/github/daneren2005/dsub/service/parser/PlaylistsParser.java b/app/src/main/java/github/daneren2005/dsub/service/parser/PlaylistsParser.java
index 6f01d510..69e5af64 100644
--- a/app/src/main/java/github/daneren2005/dsub/service/parser/PlaylistsParser.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/parser/PlaylistsParser.java
@@ -22,7 +22,6 @@ import android.content.Context;
import github.daneren2005.dsub.domain.Playlist;
import github.daneren2005.dsub.util.ProgressListener;
-import github.daneren2005.dsub.adapter.PlaylistAdapter;
import org.xmlpull.v1.XmlPullParser;
import java.io.Reader;
@@ -64,7 +63,7 @@ public class PlaylistsParser extends AbstractParser {
validate();
- return PlaylistAdapter.PlaylistComparator.sort(result);
+ return Playlist.PlaylistComparator.sort(result);
}
} \ No newline at end of file
diff --git a/app/src/main/java/github/daneren2005/dsub/service/sync/MostRecentSyncAdapter.java b/app/src/main/java/github/daneren2005/dsub/service/sync/MostRecentSyncAdapter.java
index f7a8634e..8da83be1 100644
--- a/app/src/main/java/github/daneren2005/dsub/service/sync/MostRecentSyncAdapter.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/sync/MostRecentSyncAdapter.java
@@ -23,18 +23,14 @@ import android.annotation.TargetApi;
import android.content.Context;
import android.util.Log;
-import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import github.daneren2005.dsub.R;
import github.daneren2005.dsub.domain.MusicDirectory;
-import github.daneren2005.dsub.domain.PodcastEpisode;
-import github.daneren2005.dsub.service.DownloadFile;
import github.daneren2005.dsub.util.FileUtil;
import github.daneren2005.dsub.util.Notifications;
import github.daneren2005.dsub.util.SyncUtil;
-import github.daneren2005.dsub.util.SyncUtil.SyncSet;
import github.daneren2005.dsub.util.Util;
/**
@@ -56,7 +52,7 @@ public class MostRecentSyncAdapter extends SubsonicSyncAdapter {
public void onExecuteSync(Context context, int instance) {
try {
ArrayList<String> syncedList = SyncUtil.getSyncedMostRecent(context, instance);
- MusicDirectory albumList = musicService.getAlbumList("newest", 20, 0, context, null);
+ MusicDirectory albumList = musicService.getAlbumList("newest", 20, 0, tagBrowsing, context, null);
List<String> updated = new ArrayList<String>();
boolean firstRun = false;
if(syncedList.size() == 0) {
diff --git a/app/src/main/java/github/daneren2005/dsub/util/Constants.java b/app/src/main/java/github/daneren2005/dsub/util/Constants.java
index b3dd173b..5c477ad9 100644
--- a/app/src/main/java/github/daneren2005/dsub/util/Constants.java
+++ b/app/src/main/java/github/daneren2005/dsub/util/Constants.java
@@ -173,6 +173,8 @@ public final class Constants {
public static final String MAIN_BACK_STACK = "backStackIds";
public static final String MAIN_BACK_STACK_SIZE = "backStackIdsSize";
+ public static final String MAIN_NOW_PLAYING = "nowPlayingId";
+ public static final String MAIN_SLIDE_PANEL_STATE = "slidePanelState";
public static final String FRAGMENT_LIST = "fragmentList";
public static final String FRAGMENT_LIST2 = "fragmentList2";
public static final String FRAGMENT_EXTRA = "fragmentExtra";
diff --git a/app/src/main/java/github/daneren2005/dsub/util/DrawableTint.java b/app/src/main/java/github/daneren2005/dsub/util/DrawableTint.java
new file mode 100644
index 00000000..2da72579
--- /dev/null
+++ b/app/src/main/java/github/daneren2005/dsub/util/DrawableTint.java
@@ -0,0 +1,90 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+ Copyright 2015 (C) Scott Jackson
+*/
+
+package github.daneren2005.dsub.util;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.Drawable;
+import android.support.annotation.AttrRes;
+import android.support.annotation.DrawableRes;
+import android.util.TypedValue;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.WeakHashMap;
+
+import github.daneren2005.dsub.R;
+
+public class DrawableTint {
+ private static final Map<Integer, Integer> attrMap = new HashMap<>();
+ private static final WeakHashMap<Integer, Drawable> tintedDrawables = new WeakHashMap<>();
+
+ public static Drawable getTintedDrawable(Context context, @DrawableRes int drawableRes) {
+ return getTintedDrawable(context, drawableRes, R.attr.colorAccent);
+ }
+ public static Drawable getTintedDrawable(Context context, @DrawableRes int drawableRes, @AttrRes int colorAttr) {
+ if(tintedDrawables.containsKey(drawableRes)) {
+ return tintedDrawables.get(drawableRes);
+ }
+
+ int color = getColorRes(context, colorAttr);
+ Drawable background = context.getResources().getDrawable(drawableRes);
+ background.setColorFilter(color, PorterDuff.Mode.SRC_IN);
+ tintedDrawables.put(drawableRes, background);
+ return background;
+ }
+ public static int getColorRes(Context context, @AttrRes int colorAttr) {
+ int color;
+ if(attrMap.containsKey(colorAttr)) {
+ color = attrMap.get(colorAttr);
+ } else {
+ TypedValue typedValue = new TypedValue();
+ Resources.Theme theme = context.getTheme();
+ theme.resolveAttribute(colorAttr, typedValue, true);
+ color = typedValue.data;
+ attrMap.put(colorAttr, color);
+ }
+
+ return color;
+ }
+ public static int getDrawableRes(Context context, @AttrRes int drawableAttr) {
+ if(attrMap.containsKey(drawableAttr)) {
+ return attrMap.get(drawableAttr);
+ } else {
+ int[] attrs = new int[]{drawableAttr};
+ TypedArray typedArray = context.obtainStyledAttributes(attrs);
+ @DrawableRes int drawableRes = typedArray.getResourceId(0, 0);
+ typedArray.recycle();
+ attrMap.put(drawableAttr, drawableRes);
+ return drawableRes;
+ }
+ }
+ public static Drawable getTintedAttrDrawable(Context context, @AttrRes int drawableAttr, @AttrRes int colorAttr) {
+ if(tintedDrawables.containsKey(drawableAttr)) {
+ return getTintedDrawable(context, attrMap.get(drawableAttr), colorAttr);
+ }
+
+ @DrawableRes int drawableRes = getDrawableRes(context, drawableAttr);
+ return getTintedDrawable(context, drawableRes, colorAttr);
+ }
+
+ public static void wipeTintCache() {
+ attrMap.clear();
+ tintedDrawables.clear();
+ }
+}
diff --git a/app/src/main/java/github/daneren2005/dsub/util/FileUtil.java b/app/src/main/java/github/daneren2005/dsub/util/FileUtil.java
index 990eae06..332f775c 100644
--- a/app/src/main/java/github/daneren2005/dsub/util/FileUtil.java
+++ b/app/src/main/java/github/daneren2005/dsub/util/FileUtil.java
@@ -182,19 +182,25 @@ public class FileUtil {
}
public static File getAlbumArtFile(Context context, MusicDirectory.Entry entry) {
- File albumDir = getAlbumDirectory(context, entry);
- File artFile;
- File albumFile = getAlbumArtFile(albumDir);
- File hexFile = getHexAlbumArtFile(context, albumDir);
- if(albumDir.exists()) {
- if(hexFile.exists()) {
- hexFile.renameTo(albumFile);
+ if(entry.getId().indexOf("pl-") == -1) {
+ File albumDir = getAlbumDirectory(context, entry);
+ File artFile;
+ File albumFile = getAlbumArtFile(albumDir);
+ File hexFile = getHexAlbumArtFile(context, albumDir);
+ if (albumDir.exists()) {
+ if (hexFile.exists()) {
+ hexFile.renameTo(albumFile);
+ }
+ artFile = albumFile;
+ } else {
+ artFile = hexFile;
}
- artFile = albumFile;
+ return artFile;
} else {
- artFile = hexFile;
+ File playlistDir = getAlbumArtDirectory(context);
+ Log.d(TAG, entry.getTitle() + " => " + Util.md5Hex("pl-" + entry.getTitle()));
+ return new File(playlistDir, Util.md5Hex("pl-" + entry.getTitle()) + ".jpeg");
}
- return artFile;
}
public static File getAlbumArtFile(File albumDir) {
@@ -741,23 +747,28 @@ public class FileUtil {
return index == -1 ? name : name.substring(0, index);
}
- public static Pair<Long, Long> getUsedSize(Context context, File file) {
+ public static Long[] getUsedSize(Context context, File file) {
long number = 0L;
+ long permanent = 0L;
long size = 0L;
if(file.isFile()) {
if(isMediaFile(file)) {
- return new Pair<Long, Long>(1L, file.length());
+ if(file.getAbsolutePath().indexOf(".complete") == -1) {
+ permanent++;
+ }
+ return new Long[] {1L, permanent, file.length()};
} else {
- return new Pair<Long, Long>(0L, 0L);
+ return new Long[] {0L, 0L, 0L};
}
} else {
for (File child : FileUtil.listFiles(file)) {
- Pair<Long, Long> pair = getUsedSize(context, child);
- number += pair.getFirst();
- size += pair.getSecond();
+ Long[] pair = getUsedSize(context, child);
+ number += pair[0];
+ permanent += pair[1];
+ size += pair[2];
}
- return new Pair<Long, Long>(number, size);
+ return new Long[] {number, permanent, size};
}
}
diff --git a/app/src/main/java/github/daneren2005/dsub/util/ImageLoader.java b/app/src/main/java/github/daneren2005/dsub/util/ImageLoader.java
index 7ba2064b..8b027d70 100644
--- a/app/src/main/java/github/daneren2005/dsub/util/ImageLoader.java
+++ b/app/src/main/java/github/daneren2005/dsub/util/ImageLoader.java
@@ -42,6 +42,7 @@ import android.widget.TextView;
import github.daneren2005.dsub.R;
import github.daneren2005.dsub.domain.ArtistInfo;
import github.daneren2005.dsub.domain.MusicDirectory;
+import github.daneren2005.dsub.domain.Playlist;
import github.daneren2005.dsub.domain.ServerInfo;
import github.daneren2005.dsub.service.MusicService;
import github.daneren2005.dsub.service.MusicServiceFactory;
@@ -190,8 +191,11 @@ public class ImageLoader {
}
public SilentBackgroundTask loadImage(View view, MusicDirectory.Entry entry, boolean large, boolean crossfade) {
- // TODO: If we know this a artist, try to load artist info instead
int size = large ? imageSizeLarge : imageSizeDefault;
+ return loadImage(view, entry, large, size, crossfade);
+ }
+ public SilentBackgroundTask loadImage(View view, MusicDirectory.Entry entry, boolean large, int size, boolean crossfade) {
+ // TODO: If we know this a artist, try to load artist info instead
if(entry != null && !entry.isAlbum() && ServerInfo.checkServerVersion(context, "1.11") && !Util.isOffline(context)) {
SilentBackgroundTask task = new ArtistImageTask(view.getContext(), entry, size, imageSizeLarge, large, view, crossfade);
task.execute();
@@ -222,7 +226,7 @@ public class ImageLoader {
}
if (!large) {
- setImage(view, Util.createDrawableFromBitmap(context, null), false);
+ setImage(view, null, false);
}
ImageTask task = new ViewImageTask(view.getContext(), entry, size, imageSizeLarge, large, view, crossfade);
task.execute();
@@ -246,6 +250,7 @@ public class ImageLoader {
setImage(view, drawable, true);
return null;
}
+ setImage(view, null, false);
SilentBackgroundTask<Void> task = new ViewUrlTask(view.getContext(), view, url, size);
task.execute();
@@ -274,18 +279,42 @@ public class ImageLoader {
}
public SilentBackgroundTask<Void> loadAvatar(Context context, ImageView view, String username) {
+ if(username == null) {
+ view.setImageResource(R.drawable.ic_social_person);
+ return null;
+ }
+
Bitmap bitmap = cache.get(username);
if (bitmap != null && !bitmap.isRecycled()) {
Drawable drawable = Util.createDrawableFromBitmap(this.context, bitmap);
view.setImageDrawable(drawable);
return null;
}
+ view.setImageDrawable(null);
SilentBackgroundTask<Void> task = new AvatarTask(context, view, username);
task.execute();
return task;
}
+ public SilentBackgroundTask loadImage(View view, Playlist playlist, boolean large, boolean crossfade) {
+ MusicDirectory.Entry entry = new MusicDirectory.Entry();
+ String id;
+ if(Util.isOffline(context)) {
+ id = "pl-" + playlist.getName();
+ entry.setTitle(playlist.getComment());
+ } else {
+ id = "pl-" + playlist.getId();
+ entry.setTitle(playlist.getName());
+ }
+ entry.setId(id);
+ entry.setCoverArt(id);
+ // So this isn't treated as a artist
+ entry.setParent("");
+
+ return loadImage(view, entry, large, crossfade);
+ }
+
private String getKey(String coverArtId, int size) {
return coverArtId + size;
}
@@ -379,12 +408,16 @@ public class ImageLoader {
try {
MusicService musicService = MusicServiceFactory.getMusicService(mContext);
Bitmap bitmap = musicService.getCoverArt(mContext, mEntry, mSize, null, this);
- String key = getKey(mEntry.getCoverArt(), mSize);
- cache.put(key, bitmap);
- // Make sure key is the most recently "used"
- cache.get(key);
- if(mIsNowPlaying) {
- nowPlaying = bitmap;
+ if(bitmap != null) {
+ String key = getKey(mEntry.getCoverArt(), mSize);
+ cache.put(key, bitmap);
+ // Make sure key is the most recently "used"
+ cache.get(key);
+ if (mIsNowPlaying) {
+ nowPlaying = bitmap;
+ }
+ } else {
+ bitmap = getUnknownImage(mEntry, mSize);
}
mDrawable = Util.createDrawableFromBitmap(mContext, bitmap);
@@ -582,7 +615,6 @@ public class ImageLoader {
}
} catch (Throwable x) {
Log.e(TAG, "Failed to download album art.", x);
- cancelled.set(true);
}
return null;
@@ -592,6 +624,8 @@ public class ImageLoader {
protected void done(Void result) {
if(mDrawable != null) {
mView.setImageDrawable(mDrawable);
+ } else {
+ mView.setImageResource(R.drawable.ic_social_person);
}
}
}
diff --git a/app/src/main/java/github/daneren2005/dsub/util/MenuUtil.java b/app/src/main/java/github/daneren2005/dsub/util/MenuUtil.java
new file mode 100644
index 00000000..cd899bb4
--- /dev/null
+++ b/app/src/main/java/github/daneren2005/dsub/util/MenuUtil.java
@@ -0,0 +1,55 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+ Copyright 2015 (C) Scott Jackson
+*/
+
+package github.daneren2005.dsub.util;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.view.Menu;
+
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.domain.ServerInfo;
+
+public final class MenuUtil {
+ public static void hideMenuItems(Context context, Menu menu) {
+ if(!ServerInfo.checkServerVersion(context, "1.8")) {
+ menu.setGroupVisible(R.id.server_1_8, false);
+ menu.setGroupVisible(R.id.hide_star, false);
+ }
+ if(!ServerInfo.checkServerVersion(context, "1.9")) {
+ menu.setGroupVisible(R.id.server_1_9, false);
+ }
+ if(!ServerInfo.checkServerVersion(context, "1.10.1")) {
+ menu.setGroupVisible(R.id.server_1_10, false);
+ }
+
+ SharedPreferences prefs = Util.getPreferences(context);
+ if(!prefs.getBoolean(Constants.PREFERENCES_KEY_MENU_PLAY_NEXT, true)) {
+ menu.setGroupVisible(R.id.hide_play_next, false);
+ }
+ if(!prefs.getBoolean(Constants.PREFERENCES_KEY_MENU_PLAY_LAST, true)) {
+ menu.setGroupVisible(R.id.hide_play_last, false);
+ }
+ if(!prefs.getBoolean(Constants.PREFERENCES_KEY_MENU_STAR, true)) {
+ menu.setGroupVisible(R.id.hide_star, false);
+ }
+ if(!prefs.getBoolean(Constants.PREFERENCES_KEY_MENU_SHARED, true) || !UserUtil.canShare()) {
+ menu.setGroupVisible(R.id.hide_share, false);
+ }
+ if(!prefs.getBoolean(Constants.PREFERENCES_KEY_MENU_RATING, true)) {
+ menu.setGroupVisible(R.id.hide_rating, false);
+ }
+ }
+}
diff --git a/app/src/main/java/github/daneren2005/dsub/util/Notifications.java b/app/src/main/java/github/daneren2005/dsub/util/Notifications.java
index d078d77e..c7b6986e 100644
--- a/app/src/main/java/github/daneren2005/dsub/util/Notifications.java
+++ b/app/src/main/java/github/daneren2005/dsub/util/Notifications.java
@@ -260,10 +260,9 @@ public final class Notifications {
cancelPI);
Intent notificationIntent = new Intent(context, SubsonicFragmentActivity.class);
- notificationIntent.putExtra(Constants.INTENT_EXTRA_NAME_DOWNLOAD, true);
notificationIntent.putExtra(Constants.INTENT_EXTRA_NAME_DOWNLOAD_VIEW, true);
notificationIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
- builder.setContentIntent(PendingIntent.getActivity(context, 1, notificationIntent, 0));
+ builder.setContentIntent(PendingIntent.getActivity(context, 2, notificationIntent, 0));
final Notification notification = builder.build();
downloadShowing = true;
diff --git a/app/src/main/java/github/daneren2005/dsub/util/UserUtil.java b/app/src/main/java/github/daneren2005/dsub/util/UserUtil.java
index 29618424..d758c4c9 100644
--- a/app/src/main/java/github/daneren2005/dsub/util/UserUtil.java
+++ b/app/src/main/java/github/daneren2005/dsub/util/UserUtil.java
@@ -16,18 +16,22 @@
package github.daneren2005.dsub.util;
import android.app.Activity;
-import android.app.AlertDialog;
+import android.support.v7.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.support.v7.app.ActionBarActivity;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.View;
+import android.view.WindowManager;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;
import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.adapter.SectionAdapter;
import github.daneren2005.dsub.domain.User;
import github.daneren2005.dsub.fragments.SubsonicFragment;
import github.daneren2005.dsub.service.DownloadService;
@@ -182,6 +186,7 @@ public final class UserUtil {
.setCancelable(true);
AlertDialog dialog = builder.create();
+ dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
dialog.show();
}
}
@@ -247,7 +252,6 @@ public final class UserUtil {
protected Void doInBackground() throws Throwable {
MusicService musicService = MusicServiceFactory.getMusicService(context);
musicService.updateUser(user, context, null);
- user.setSettings(user.getSettings());
return null;
}
@@ -326,7 +330,7 @@ public final class UserUtil {
});
}
- public static void deleteUser(final Context context, final User user, final ArrayAdapter adapter) {
+ public static void deleteUser(final Context context, final User user, final SectionAdapter adapter) {
Util.confirmDialog(context, R.string.common_delete, user.getUsername(), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
@@ -341,8 +345,7 @@ public final class UserUtil {
@Override
protected void done(Void v) {
if(adapter != null) {
- adapter.remove(user);
- adapter.notifyDataSetChanged();
+ adapter.removeItem(user);
}
Util.toast(context, context.getResources().getString(R.string.admin_delete_user_success, user.getUsername()));
@@ -378,8 +381,11 @@ public final class UserUtil {
final TextView usernameView = (TextView) layout.findViewById(R.id.username);
final TextView emailView = (TextView) layout.findViewById(R.id.email);
final TextView passwordView = (TextView) layout.findViewById(R.id.password);
- final ListView listView = (ListView) layout.findViewById(R.id.settings_list);
- listView.setAdapter(new SettingsAdapter(context, user, true));
+ final RecyclerView recyclerView = (RecyclerView) layout.findViewById(R.id.settings_list);
+ LinearLayoutManager layoutManager = new LinearLayoutManager(context);
+ layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
+ recyclerView.setLayoutManager(layoutManager);
+ recyclerView.setAdapter(new SettingsAdapter(context, user, null, true));
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(R.string.menu_add_user)
diff --git a/app/src/main/java/github/daneren2005/dsub/util/Util.java b/app/src/main/java/github/daneren2005/dsub/util/Util.java
index 032facd6..db0cb7c1 100644
--- a/app/src/main/java/github/daneren2005/dsub/util/Util.java
+++ b/app/src/main/java/github/daneren2005/dsub/util/Util.java
@@ -19,7 +19,8 @@ package github.daneren2005.dsub.util;
import android.annotation.TargetApi;
import android.app.Activity;
-import android.app.AlertDialog;
+import android.support.annotation.StringRes;
+import android.support.v7.app.AlertDialog;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
@@ -45,12 +46,18 @@ import android.text.method.LinkMovementMethod;
import android.text.util.Linkify;
import android.util.Log;
import android.view.Gravity;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.activity.SettingsActivity;
+import github.daneren2005.dsub.activity.SubsonicFragmentActivity;
+import github.daneren2005.dsub.adapter.DetailsAdapter;
import github.daneren2005.dsub.domain.MusicDirectory;
import github.daneren2005.dsub.domain.PlayerState;
import github.daneren2005.dsub.domain.RepeatMode;
+import github.daneren2005.dsub.domain.ServerInfo;
import github.daneren2005.dsub.receiver.MediaButtonIntentReceiver;
import github.daneren2005.dsub.service.DownloadService;
@@ -68,10 +75,14 @@ import java.lang.reflect.Method;
import java.security.MessageDigest;
import java.text.DecimalFormat;
import java.text.NumberFormat;
+import java.text.ParseException;
import java.text.SimpleDateFormat;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
+import java.util.List;
import java.util.Locale;
+import java.util.TimeZone;
/**
* @author Sindre Mehus
@@ -249,6 +260,32 @@ public final class Util {
SharedPreferences prefs = getPreferences(context);
return prefs.getString(Constants.PREFERENCES_KEY_THEME, null);
}
+ public static int getThemeRes(Context context) {
+ return getThemeRes(context, getTheme(context));
+ }
+ public static int getThemeRes(Context context, String theme) {
+ if(context instanceof SubsonicFragmentActivity || context instanceof SettingsActivity) {
+ if ("dark".equals(theme)) {
+ return R.style.Theme_DSub_Dark_No_Actionbar;
+ } else if ("black".equals(theme)) {
+ return R.style.Theme_DSub_Black_No_Actionbar;
+ } else if ("holo".equals(theme)) {
+ return R.style.Theme_DSub_Holo_No_Actionbar;
+ } else {
+ return R.style.Theme_DSub_Light_No_Actionbar;
+ }
+ } else {
+ if ("dark".equals(theme)) {
+ return R.style.Theme_DSub_Dark;
+ } else if ("black".equals(theme)) {
+ return R.style.Theme_DSub_Black;
+ } else if ("holo".equals(theme)) {
+ return R.style.Theme_DSub_Holo;
+ } else {
+ return R.style.Theme_DSub_Light;
+ }
+ }
+ }
public static void setTheme(Context context, String theme) {
SharedPreferences.Editor editor = getPreferences(context).edit();
editor.putString(Constants.PREFERENCES_KEY_THEME, theme);
@@ -256,15 +293,7 @@ public final class Util {
}
public static void applyTheme(Context context, String theme) {
- if ("dark".equals(theme)) {
- context.setTheme(R.style.Theme_DSub_Dark);
- } else if ("black".equals(theme)) {
- context.setTheme(R.style.Theme_DSub_Black);
- } else if ("holo".equals(theme)) {
- context.setTheme(R.style.Theme_DSub_Holo);
- } else {
- context.setTheme(R.style.Theme_DSub_Light);
- }
+ context.setTheme(getThemeRes(context, theme));
SharedPreferences prefs = Util.getPreferences(context);
if(prefs.getBoolean(Constants.PREFERENCES_KEY_OVERRIDE_SYSTEM_LANGUAGE, false)) {
@@ -809,6 +838,19 @@ public final class Util {
return builder.toString();
}
+ public static String formatDate(Context context, String dateString) {
+ try {
+ boolean isDateNormalized = ServerInfo.checkServerVersion(context, "1.11");
+ SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.ENGLISH);
+ if (isDateNormalized) {
+ dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ }
+
+ return formatDate(dateFormat.parse(dateString));
+ } catch(ParseException e) {
+ return dateString;
+ }
+ }
public static String formatDate(Date date) {
if(date == null) {
return "Never";
@@ -1101,6 +1143,32 @@ public final class Util {
((TextView)dialog.findViewById(android.R.id.message)).setMovementMethod(LinkMovementMethod.getInstance());
}
+ public static void showDetailsDialog(Context context, @StringRes int title, List<Integer> headers, List<String> details) {
+ List<String> headerStrings = new ArrayList<>();
+ for(@StringRes Integer res: headers) {
+ headerStrings.add(context.getResources().getString(res));
+ }
+ showDetailsDialog(context, context.getResources().getString(title), headerStrings, details);
+ }
+ public static void showDetailsDialog(Context context, String title, List<String> headers, List<String> details) {
+ ListView listView = new ListView(context);
+ listView.setAdapter(new DetailsAdapter(context, R.layout.details_item, headers, details));
+ listView.setDivider(null);
+ listView.setScrollbarFadingEnabled(false);
+
+ new AlertDialog.Builder(context)
+ // .setIcon(android.R.drawable.ic_dialog_info)
+ .setTitle(title)
+ .setView(listView)
+ .setPositiveButton(R.string.common_close, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int i) {
+ dialog.dismiss();
+ }
+ })
+ .show();
+ }
+
public static void sleepQuietly(long millis) {
try {
Thread.sleep(millis);
@@ -1141,15 +1209,6 @@ public final class Util {
}
}
- public static int getAttribute(Context context, int attr) {
- int res;
- int[] attrs = new int[] {attr};
- TypedArray typedArray = context.obtainStyledAttributes(attrs);
- res = typedArray.getResourceId(0, 0);
- typedArray.recycle();
- return res;
- }
-
public static void registerMediaButtonEventReceiver(Context context) {
// Only do it if enabled in the settings.
diff --git a/app/src/main/java/github/daneren2005/dsub/view/AlbumCell.java b/app/src/main/java/github/daneren2005/dsub/view/AlbumCell.java
deleted file mode 100644
index 8707ece7..00000000
--- a/app/src/main/java/github/daneren2005/dsub/view/AlbumCell.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- 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 <http://www.gnu.org/licenses/>.
- 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.ImageButton;
-import android.widget.ImageView;
-import android.widget.RatingBar;
-import android.widget.TextView;
-
-import java.io.File;
-
-import github.daneren2005.dsub.R;
-import github.daneren2005.dsub.domain.MusicDirectory;
-import github.daneren2005.dsub.util.FileUtil;
-import github.daneren2005.dsub.util.ImageLoader;
-
-public class AlbumCell extends UpdateView {
- private static final String TAG = AlbumCell.class.getSimpleName();
-
- private Context context;
- private MusicDirectory.Entry album;
- private File file;
-
- private View coverArtView;
- private TextView titleView;
- private TextView artistView;
- private boolean showArtist = true;
-
- public AlbumCell(Context context) {
- super(context);
- this.context = context;
- LayoutInflater.from(context).inflate(R.layout.album_cell_item, this, true);
-
- coverArtView = findViewById(R.id.album_coverart);
- titleView = (TextView) findViewById(R.id.album_title);
- artistView = (TextView) findViewById(R.id.album_artist);
-
- ratingBar = (RatingBar) findViewById(R.id.album_rating);
- ratingBar.setFocusable(false);
- starButton = (ImageButton) findViewById(R.id.album_star);
- starButton.setFocusable(false);
- moreButton = (ImageView) findViewById(R.id.album_more);
- moreButton.setOnClickListener(new View.OnClickListener() {
- public void onClick(View v) {
- v.showContextMenu();
- }
- });
- }
-
- public void setShowArtist(boolean showArtist) {
- this.showArtist = showArtist;
- }
-
- protected void setObjectImpl(Object obj1, Object obj2) {
- this.album = (MusicDirectory.Entry) obj1;
- titleView.setText(album.getAlbumDisplay());
- String artist = "";
- if(showArtist) {
- artist = album.getArtist();
- if (artist == null) {
- artist = "";
- }
- if (album.getYear() != null) {
- artist += " - " + album.getYear();
- }
- } else if(album.getYear() != null) {
- artist += album.getYear();
- }
- artistView.setText(album.getArtist() == null ? "" : artist);
- imageTask = ((ImageLoader)obj2).loadImage(coverArtView, album, false, true);
- file = null;
- }
-
- @Override
- protected void updateBackground() {
- if(file == null) {
- file = FileUtil.getAlbumDirectory(context, album);
- }
-
- exists = file.exists();
- isStarred = album.isStarred();
- isRated = album.getRating();
- }
-
- public MusicDirectory.Entry getEntry() {
- return album;
- }
-
- public File getFile() {
- return file;
- }
-}
diff --git a/app/src/main/java/github/daneren2005/dsub/view/AlbumListCountView.java b/app/src/main/java/github/daneren2005/dsub/view/AlbumListCountView.java
new file mode 100644
index 00000000..1ae9ef8e
--- /dev/null
+++ b/app/src/main/java/github/daneren2005/dsub/view/AlbumListCountView.java
@@ -0,0 +1,130 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+ Copyright 2015 (C) Scott Jackson
+*/
+
+package github.daneren2005.dsub.view;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.domain.MusicDirectory;
+import github.daneren2005.dsub.service.MusicService;
+import github.daneren2005.dsub.service.MusicServiceFactory;
+import github.daneren2005.dsub.util.Constants;
+import github.daneren2005.dsub.util.FileUtil;
+import github.daneren2005.dsub.util.Util;
+
+public class AlbumListCountView extends UpdateView2<Integer, Void> {
+ private final String TAG = AlbumListCountView.class.getSimpleName();
+
+ private TextView titleView;
+ private TextView countView;
+ private int startCount;
+ private int count = 0;
+
+ public AlbumListCountView(Context context) {
+ super(context, false);
+ this.context = context;
+ LayoutInflater.from(context).inflate(R.layout.basic_count_item, this, true);
+
+ titleView = (TextView) findViewById(R.id.basic_count_name);
+ countView = (TextView) findViewById(R.id.basic_count_count);
+ }
+
+ protected void setObjectImpl(Integer albumListString, Void dummy) {
+ titleView.setText(albumListString);
+
+ SharedPreferences prefs = Util.getPreferences(context);
+ startCount = prefs.getInt(Constants.PREFERENCES_KEY_RECENT_COUNT + Util.getActiveServer(context), 0);
+ count = startCount;
+ update();
+ }
+
+ @Override
+ protected void updateBackground() {
+ try {
+ String recentAddedFile = Util.getCacheName(context, "recent_count");
+ ArrayList<String> recents = FileUtil.deserialize(context, recentAddedFile, ArrayList.class);
+ if (recents == null) {
+ recents = new ArrayList<String>();
+ }
+
+ MusicService musicService = MusicServiceFactory.getMusicService(context);
+ MusicDirectory recentlyAdded = musicService.getAlbumList("newest", 20, 0, false, context, null);
+
+ // If first run, just put everything in it and return 0
+ boolean firstRun = recents.isEmpty();
+
+ // Count how many new albums are in the list
+ count = 0;
+ for (MusicDirectory.Entry album : recentlyAdded.getChildren()) {
+ if (!recents.contains(album.getId())) {
+ recents.add(album.getId());
+ count++;
+ }
+ }
+
+ // Keep recents list from growing infinitely
+ while (recents.size() > 40) {
+ recents.remove(0);
+ }
+ FileUtil.serialize(context, recents, recentAddedFile);
+
+ if (!firstRun) {
+ // Add the old count which will get cleared out after viewing recents
+ count += startCount;
+ SharedPreferences.Editor editor = Util.getPreferences(context).edit();
+ editor.putInt(Constants.PREFERENCES_KEY_RECENT_COUNT + Util.getActiveServer(context), count);
+ editor.commit();
+ }
+ } catch(Exception e) {
+ Log.w(TAG, "Failed to refresh most recent count", e);
+ }
+ }
+
+ @Override
+ protected void update() {
+ // Update count display with appropriate information
+ if(count <= 0) {
+ countView.setVisibility(View.GONE);
+ } else {
+ String displayName;
+ if(count < 10) {
+ displayName = "0" + count;
+ } else {
+ displayName = "" + count;
+ }
+
+ countView.setText(displayName);
+ countView.setVisibility(View.VISIBLE);
+ }
+ }
+
+ @Override
+ public void onClick() {
+ SharedPreferences.Editor editor = Util.getPreferences(context).edit();
+ editor.putInt(Constants.PREFERENCES_KEY_RECENT_COUNT + Util.getActiveServer(context), 0);
+ editor.commit();
+
+ count = 0;
+ update();
+ }
+}
diff --git a/app/src/main/java/github/daneren2005/dsub/view/AlbumView.java b/app/src/main/java/github/daneren2005/dsub/view/AlbumView.java
index bd54ea1e..e521babf 100644
--- a/app/src/main/java/github/daneren2005/dsub/view/AlbumView.java
+++ b/app/src/main/java/github/daneren2005/dsub/view/AlbumView.java
@@ -16,10 +16,10 @@
Copyright 2009 (C) Sindre Mehus
*/
+
package github.daneren2005.dsub.view;
import android.content.Context;
-import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageButton;
@@ -30,75 +30,76 @@ import github.daneren2005.dsub.R;
import github.daneren2005.dsub.domain.MusicDirectory;
import github.daneren2005.dsub.util.FileUtil;
import github.daneren2005.dsub.util.ImageLoader;
-import github.daneren2005.dsub.util.Util;
+
import java.io.File;
-import java.util.List;
-/**
- * Used to display albums in a {@code ListView}.
- *
- * @author Sindre Mehus
- */
-public class AlbumView extends UpdateView {
+public class AlbumView extends UpdateView2<MusicDirectory.Entry, ImageLoader> {
private static final String TAG = AlbumView.class.getSimpleName();
- private Context context;
- private MusicDirectory.Entry album;
private File file;
-
+ private View coverArtView;
private TextView titleView;
private TextView artistView;
- private View coverArtView;
+ private boolean showArtist = true;
- public AlbumView(Context context) {
+ public AlbumView(Context context, boolean cell) {
super(context);
- this.context = context;
- LayoutInflater.from(context).inflate(R.layout.album_list_item, this, true);
+ if(cell) {
+ LayoutInflater.from(context).inflate(R.layout.album_cell_item, this, true);
+ } else {
+ LayoutInflater.from(context).inflate(R.layout.album_list_item, this, true);
+ }
+
+ coverArtView = findViewById(R.id.album_coverart);
titleView = (TextView) findViewById(R.id.album_title);
artistView = (TextView) findViewById(R.id.album_artist);
- coverArtView = findViewById(R.id.album_coverart);
+
ratingBar = (RatingBar) findViewById(R.id.album_rating);
+ ratingBar.setFocusable(false);
starButton = (ImageButton) findViewById(R.id.album_star);
starButton.setFocusable(false);
+ moreButton = (ImageView) findViewById(R.id.more_button);
- moreButton = (ImageView) findViewById(R.id.album_more);
- moreButton.setOnClickListener(new View.OnClickListener() {
- public void onClick(View v) {
- v.showContextMenu();
- }
- });
+ checkable = true;
+ }
+
+ public void setShowArtist(boolean showArtist) {
+ this.showArtist = showArtist;
}
- protected void setObjectImpl(Object obj1, Object obj2) {
- this.album = (MusicDirectory.Entry) obj1;
+ protected void setObjectImpl(MusicDirectory.Entry album, ImageLoader imageLoader) {
titleView.setText(album.getAlbumDisplay());
- String artist = album.getArtist();
- if(artist == null) {
- artist = "";
- }
- if(album.getYear() != null) {
- artist += " - " + album.getYear();
+ String artist = "";
+ if(showArtist) {
+ artist = album.getArtist();
+ if (artist == null) {
+ artist = "";
+ }
+ if (album.getYear() != null) {
+ artist += " - " + album.getYear();
+ }
+ } else if(album.getYear() != null) {
+ artist += album.getYear();
}
- artistView.setText(artist);
- artistView.setVisibility(album.getArtist() == null ? View.GONE : View.VISIBLE);
- imageTask = ((ImageLoader)obj2).loadImage(coverArtView, album, false, true);
+ artistView.setText(album.getArtist() == null ? "" : artist);
+ imageTask = imageLoader.loadImage(coverArtView, album, false, true);
file = null;
}
@Override
protected void updateBackground() {
if(file == null) {
- file = FileUtil.getAlbumDirectory(context, album);
+ file = FileUtil.getAlbumDirectory(context, item);
}
exists = file.exists();
- isStarred = album.isStarred();
- isRated = album.getRating();
+ isStarred = item.isStarred();
+ isRated = item.getRating();
}
public MusicDirectory.Entry getEntry() {
- return album;
+ return item;
}
public File getFile() {
diff --git a/app/src/main/java/github/daneren2005/dsub/view/ArtistEntryView.java b/app/src/main/java/github/daneren2005/dsub/view/ArtistEntryView.java
index 71bdeb78..f6a6adb3 100644
--- a/app/src/main/java/github/daneren2005/dsub/view/ArtistEntryView.java
+++ b/app/src/main/java/github/daneren2005/dsub/view/ArtistEntryView.java
@@ -19,7 +19,6 @@
package github.daneren2005.dsub.view;
import android.content.Context;
-import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageButton;
@@ -28,26 +27,21 @@ import android.widget.TextView;
import github.daneren2005.dsub.R;
import github.daneren2005.dsub.domain.MusicDirectory;
import github.daneren2005.dsub.util.FileUtil;
-import github.daneren2005.dsub.util.ImageLoader;
-import github.daneren2005.dsub.util.Util;
+
import java.io.File;
/**
* Used to display albums in a {@code ListView}.
*
* @author Sindre Mehus
*/
-public class ArtistEntryView extends UpdateView {
+public class ArtistEntryView extends UpdateView<MusicDirectory.Entry> {
private static final String TAG = ArtistEntryView.class.getSimpleName();
- private Context context;
- private MusicDirectory.Entry artist;
private File file;
-
private TextView titleView;
public ArtistEntryView(Context context) {
super(context);
- this.context = context;
LayoutInflater.from(context).inflate(R.layout.basic_list_item, this, true);
titleView = (TextView) findViewById(R.id.item_name);
@@ -61,8 +55,7 @@ public class ArtistEntryView extends UpdateView {
});
}
- protected void setObjectImpl(Object obj) {
- this.artist = (MusicDirectory.Entry) obj;
+ protected void setObjectImpl(MusicDirectory.Entry artist) {
titleView.setText(artist.getTitle());
file = FileUtil.getArtistDirectory(context, artist);
}
@@ -70,7 +63,7 @@ public class ArtistEntryView extends UpdateView {
@Override
protected void updateBackground() {
exists = file.exists();
- isStarred = artist.isStarred();
+ isStarred = item.isStarred();
}
public File getFile() {
diff --git a/app/src/main/java/github/daneren2005/dsub/view/ArtistView.java b/app/src/main/java/github/daneren2005/dsub/view/ArtistView.java
index c255be69..d86c5d26 100644
--- a/app/src/main/java/github/daneren2005/dsub/view/ArtistView.java
+++ b/app/src/main/java/github/daneren2005/dsub/view/ArtistView.java
@@ -35,18 +35,14 @@ import java.io.File;
*
* @author Sindre Mehus
*/
-public class ArtistView extends UpdateView {
+public class ArtistView extends UpdateView<Artist> {
private static final String TAG = ArtistView.class.getSimpleName();
-
- private Context context;
- private Artist artist;
- private File file;
+ private File file;
private TextView titleView;
public ArtistView(Context context) {
super(context);
- this.context = context;
LayoutInflater.from(context).inflate(R.layout.basic_list_item, this, true);
titleView = (TextView) findViewById(R.id.item_name);
@@ -60,8 +56,7 @@ public class ArtistView extends UpdateView {
});
}
- protected void setObjectImpl(Object obj) {
- this.artist = (Artist) obj;
+ protected void setObjectImpl(Artist artist) {
titleView.setText(artist.getName());
file = FileUtil.getArtistDirectory(context, artist);
}
@@ -69,7 +64,7 @@ public class ArtistView extends UpdateView {
@Override
protected void updateBackground() {
exists = file.exists();
- isStarred = artist.isStarred();
+ isStarred = item.isStarred();
}
public File getFile() {
diff --git a/app/src/main/java/github/daneren2005/dsub/view/BasicHeaderView.java b/app/src/main/java/github/daneren2005/dsub/view/BasicHeaderView.java
new file mode 100644
index 00000000..01aa42e1
--- /dev/null
+++ b/app/src/main/java/github/daneren2005/dsub/view/BasicHeaderView.java
@@ -0,0 +1,40 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+ Copyright 2015 (C) Scott Jackson
+*/
+
+package github.daneren2005.dsub.view;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.widget.TextView;
+
+import github.daneren2005.dsub.R;
+
+public class BasicHeaderView extends UpdateView<String> {
+ TextView nameView;
+
+ public BasicHeaderView(Context context) {
+ this(context, R.layout.basic_header);
+ }
+ public BasicHeaderView(Context context, int layout) {
+ super(context, false);
+
+ LayoutInflater.from(context).inflate(layout, this, true);
+ nameView = (TextView) findViewById(R.id.item_name);
+ }
+
+ protected void setObjectImpl(String string) {
+ nameView.setText(string);
+ }
+}
diff --git a/app/src/main/java/github/daneren2005/dsub/view/BasicListView.java b/app/src/main/java/github/daneren2005/dsub/view/BasicListView.java
new file mode 100644
index 00000000..ca7e8993
--- /dev/null
+++ b/app/src/main/java/github/daneren2005/dsub/view/BasicListView.java
@@ -0,0 +1,44 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+ Copyright 2015 (C) Scott Jackson
+*/
+
+package github.daneren2005.dsub.view;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import github.daneren2005.dsub.R;
+
+public class BasicListView extends UpdateView<String> {
+ private TextView titleView;
+
+ public BasicListView(Context context) {
+ super(context, false);
+ LayoutInflater.from(context).inflate(R.layout.basic_list_item, this, true);
+
+ titleView = (TextView) findViewById(R.id.item_name);
+ starButton = (ImageButton) findViewById(R.id.item_star);
+ starButton.setFocusable(false);
+ moreButton = (ImageView) findViewById(R.id.item_more);
+ moreButton.setVisibility(View.GONE);
+ }
+
+ protected void setObjectImpl(String string) {
+ titleView.setText(string);
+ }
+}
diff --git a/app/src/main/java/github/daneren2005/dsub/view/ChangeLog.java b/app/src/main/java/github/daneren2005/dsub/view/ChangeLog.java
index 096583c7..e3d24485 100644
--- a/app/src/main/java/github/daneren2005/dsub/view/ChangeLog.java
+++ b/app/src/main/java/github/daneren2005/dsub/view/ChangeLog.java
@@ -42,7 +42,7 @@ import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
-import android.app.AlertDialog;
+import android.support.v7.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
diff --git a/app/src/main/java/github/daneren2005/dsub/view/ErrorDialog.java b/app/src/main/java/github/daneren2005/dsub/view/ErrorDialog.java
index 0b9d05a0..3d6eaa52 100644
--- a/app/src/main/java/github/daneren2005/dsub/view/ErrorDialog.java
+++ b/app/src/main/java/github/daneren2005/dsub/view/ErrorDialog.java
@@ -19,7 +19,7 @@
package github.daneren2005.dsub.view;
import android.app.Activity;
-import android.app.AlertDialog;
+import android.support.v7.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
diff --git a/app/src/main/java/github/daneren2005/dsub/view/GenreView.java b/app/src/main/java/github/daneren2005/dsub/view/GenreView.java
index 8dbcf89d..b29aefba 100644
--- a/app/src/main/java/github/daneren2005/dsub/view/GenreView.java
+++ b/app/src/main/java/github/daneren2005/dsub/view/GenreView.java
@@ -25,7 +25,7 @@ import android.widget.TextView;
import github.daneren2005.dsub.R;
import github.daneren2005.dsub.domain.Genre;
-public class GenreView extends UpdateView {
+public class GenreView extends UpdateView<Genre> {
private static final String TAG = GenreView.class.getSimpleName();
private TextView titleView;
@@ -41,8 +41,7 @@ public class GenreView extends UpdateView {
albumsView = (TextView) findViewById(R.id.genre_albums);
}
- public void setObjectImpl(Object obj) {
- Genre genre = (Genre) obj;
+ public void setObjectImpl(Genre genre) {
titleView.setText(genre.getName());
if(genre.getAlbumCount() != null) {
diff --git a/app/src/main/java/github/daneren2005/dsub/view/GridSpacingDecoration.java b/app/src/main/java/github/daneren2005/dsub/view/GridSpacingDecoration.java
new file mode 100644
index 00000000..3bb3e8a1
--- /dev/null
+++ b/app/src/main/java/github/daneren2005/dsub/view/GridSpacingDecoration.java
@@ -0,0 +1,99 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+ Copyright 2015 (C) Scott Jackson
+*/
+
+package github.daneren2005.dsub.view;
+
+import android.graphics.Rect;
+import android.support.v7.widget.GridLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.util.TypedValue;
+import android.view.View;
+
+public class GridSpacingDecoration extends RecyclerView.ItemDecoration {
+
+ @Override
+ public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
+ super.getItemOffsets(outRect, view, parent, state);
+
+ int spacing = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10, view.getResources().getDisplayMetrics());
+ int halfSpacing = spacing / 2;
+
+ int childCount = parent.getChildCount();
+ int childIndex = parent.getChildPosition(view);
+ int spanCount = getTotalSpan(view, parent);
+ int spanIndex = childIndex % spanCount;
+ int spanSize = getSpanSize(parent, childIndex);
+
+ /* INVALID SPAN */
+ if (spanCount < 1 || spanSize > 1) return;
+
+ outRect.top = halfSpacing;
+ outRect.bottom = halfSpacing;
+ outRect.left = halfSpacing;
+ outRect.right = halfSpacing;
+
+ if (isTopEdge(childIndex, spanCount)) {
+ outRect.top = spacing;
+ }
+
+ if (isLeftEdge(spanIndex, spanCount)) {
+ outRect.left = spacing;
+ }
+
+ if (isRightEdge(spanIndex, spanCount)) {
+ outRect.right = spacing;
+ }
+
+ if (isBottomEdge(childIndex, childCount, spanCount)) {
+ outRect.bottom = spacing;
+ }
+ }
+
+ protected int getTotalSpan(View view, RecyclerView parent) {
+ RecyclerView.LayoutManager mgr = parent.getLayoutManager();
+ if (mgr instanceof GridLayoutManager) {
+ return ((GridLayoutManager) mgr).getSpanCount();
+ }
+
+ return -1;
+ }
+ protected int getSpanSize(RecyclerView parent, int childIndex) {
+ RecyclerView.LayoutManager mgr = parent.getLayoutManager();
+ if (mgr instanceof GridLayoutManager) {
+ GridLayoutManager.SpanSizeLookup lookup = ((GridLayoutManager) mgr).getSpanSizeLookup();
+ if(lookup != null) {
+ return lookup.getSpanSize(childIndex);
+ }
+ }
+
+ return 1;
+ }
+
+ protected boolean isLeftEdge(int spanIndex, int spanCount) {
+ return spanIndex == 0;
+ }
+
+ protected boolean isRightEdge(int spanIndex, int spanCount) {
+ return spanIndex == spanCount - 1;
+ }
+
+ protected boolean isTopEdge(int childIndex, int spanCount) {
+ return childIndex < spanCount;
+ }
+
+ protected boolean isBottomEdge(int childIndex, int childCount, int spanCount) {
+ return childIndex >= childCount - spanCount;
+ }
+}
diff --git a/app/src/main/java/github/daneren2005/dsub/view/HeaderGridView.java b/app/src/main/java/github/daneren2005/dsub/view/HeaderGridView.java
deleted file mode 100644
index 8a82f353..00000000
--- a/app/src/main/java/github/daneren2005/dsub/view/HeaderGridView.java
+++ /dev/null
@@ -1,836 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package github.daneren2005.dsub.view;
-
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.database.DataSetObservable;
-import android.database.DataSetObserver;
-import android.os.Build;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.*;
-
-import java.lang.reflect.Field;
-import java.util.ArrayList;
-
-/**
- * A {@link GridView} that supports adding header rows in a
- * very similar way to {@link android.widget.ListView}.
- * See {@link HeaderGridView#addHeaderView(View, Object, boolean)}
- * See {@link HeaderGridView#addFooterView(View, Object, boolean)}
- */
-public class HeaderGridView extends GridView {
- private static final String TAG = HeaderGridView.class.getSimpleName();
- public static boolean DEBUG = false;
-
- /**
- * A class that represents a fixed view in a list, for example a header at the top
- * or a footer at the bottom.
- */
- private static class FixedViewInfo {
- /**
- * The view to add to the grid
- */
- public View view;
- public ViewGroup viewContainer;
- /**
- * The data backing the view. This is returned from {@link ListAdapter#getItem(int)}.
- */
- public Object data;
- /**
- * <code>true</code> if the fixed view should be selectable in the grid
- */
- public boolean isSelectable;
- }
-
- private int mNumColumns = AUTO_FIT;
- private View mViewForMeasureRowHeight = null;
- private int mRowHeight = -1;
- private static final String LOG_TAG = HeaderGridView.class.getSimpleName();
-
- private ArrayList<FixedViewInfo> mHeaderViewInfos = new ArrayList<FixedViewInfo>();
- private ArrayList<FixedViewInfo> mFooterViewInfos = new ArrayList<FixedViewInfo>();
-
- private void initHeaderGridView() {
- }
-
- public HeaderGridView(Context context) {
- super(context);
- initHeaderGridView();
- }
-
- public HeaderGridView(Context context, AttributeSet attrs) {
- super(context, attrs);
- initHeaderGridView();
- }
-
- public HeaderGridView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- initHeaderGridView();
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- ListAdapter adapter = getAdapter();
- if (adapter != null && adapter instanceof HeaderViewGridAdapter) {
- ((HeaderViewGridAdapter) adapter).setNumColumns(getNumColumnsCompatible());
- ((HeaderViewGridAdapter) adapter).setRowHeight(getRowHeight());
- }
- }
-
- @Override
- public void setClipChildren(boolean clipChildren) {
- // Ignore, since the header rows depend on not being clipped
- }
-
- /**
- * Do not call this method unless you know how it works.
- *
- * @param clipChildren
- */
- public void setClipChildrenSupper(boolean clipChildren) {
- super.setClipChildren(false);
- }
-
- /**
- * Add a fixed view to appear at the top of the grid. If addHeaderView is
- * called more than once, the views will appear in the order they were
- * added. Views added using this call can take focus if they want.
- * <p/>
- * NOTE: Call this before calling setAdapter. This is so HeaderGridView can wrap
- * the supplied cursor with one that will also account for header views.
- *
- * @param v The view to add.
- */
- public void addHeaderView(View v) {
- addHeaderView(v, null, true);
- }
-
- /**
- * Add a fixed view to appear at the top of the grid. If addHeaderView is
- * called more than once, the views will appear in the order they were
- * added. Views added using this call can take focus if they want.
- * <p/>
- * NOTE: Call this before calling setAdapter. This is so HeaderGridView can wrap
- * the supplied cursor with one that will also account for header views.
- *
- * @param v The view to add.
- * @param data Data to associate with this view
- * @param isSelectable whether the item is selectable
- */
- public void addHeaderView(View v, Object data, boolean isSelectable) {
- ListAdapter adapter = getAdapter();
- if (adapter != null && !(adapter instanceof HeaderViewGridAdapter)) {
- throw new IllegalStateException(
- "Cannot add header view to grid -- setAdapter has already been called.");
- }
-
- ViewGroup.LayoutParams lyp = v.getLayoutParams();
-
- FixedViewInfo info = new FixedViewInfo();
- FrameLayout fl = new FullWidthFixedViewLayout(getContext());
-
- if (lyp != null) {
- v.setLayoutParams(new FrameLayout.LayoutParams(lyp.width, lyp.height));
- fl.setLayoutParams(new AbsListView.LayoutParams(lyp.width, lyp.height));
- }
- fl.addView(v);
- info.view = v;
- info.viewContainer = fl;
- info.data = data;
- info.isSelectable = isSelectable;
- mHeaderViewInfos.add(info);
- // in the case of re-adding a header view, or adding one later on,
- // we need to notify the observer
- if (adapter != null) {
- ((HeaderViewGridAdapter) adapter).notifyDataSetChanged();
- }
- }
-
- public void addFooterView(View v) {
- addFooterView(v, null, true);
- }
-
- public void addFooterView(View v, Object data, boolean isSelectable) {
- ListAdapter mAdapter = getAdapter();
- if (mAdapter != null && !(mAdapter instanceof HeaderViewGridAdapter)) {
- throw new IllegalStateException(
- "Cannot add header view to grid -- setAdapter has already been called.");
- }
-
- ViewGroup.LayoutParams lyp = v.getLayoutParams();
-
- FixedViewInfo info = new FixedViewInfo();
- FrameLayout fl = new FullWidthFixedViewLayout(getContext());
-
- if (lyp != null) {
- v.setLayoutParams(new FrameLayout.LayoutParams(lyp.width, lyp.height));
- fl.setLayoutParams(new AbsListView.LayoutParams(lyp.width, lyp.height));
- }
- fl.addView(v);
- info.view = v;
- info.viewContainer = fl;
- info.data = data;
- info.isSelectable = isSelectable;
- mFooterViewInfos.add(info);
-
- if (mAdapter != null) {
- ((HeaderViewGridAdapter) mAdapter).notifyDataSetChanged();
- }
- }
-
- public int getHeaderViewCount() {
- return mHeaderViewInfos.size();
- }
-
- public int getFooterViewCount() {
- return mFooterViewInfos.size();
- }
-
- /**
- * Removes a previously-added header view.
- *
- * @param v The view to remove
- * @return true if the view was removed, false if the view was not a header
- * view
- */
- public boolean removeHeaderView(View v) {
- if (mHeaderViewInfos.size() > 0) {
- boolean result = false;
- ListAdapter adapter = getAdapter();
- if (adapter != null && ((HeaderViewGridAdapter) adapter).removeHeader(v)) {
- result = true;
- }
- removeFixedViewInfo(v, mHeaderViewInfos);
- return result;
- }
- return false;
- }
-
- /**
- * Removes a previously-added footer view.
- *
- * @param v The view to remove
- * @return true if the view was removed, false if the view was not a header
- * view
- */
- public boolean removeFooterView(View v) {
- if (mFooterViewInfos.size() > 0) {
- boolean result = false;
- ListAdapter adapter = getAdapter();
- if (adapter != null && ((HeaderViewGridAdapter) adapter).removeFooter(v)) {
- result = true;
- }
- removeFixedViewInfo(v, mFooterViewInfos);
- return result;
- }
- return false;
- }
-
- private void removeFixedViewInfo(View v, ArrayList<FixedViewInfo> where) {
- int len = where.size();
- for (int i = 0; i < len; ++i) {
- FixedViewInfo info = where.get(i);
- if (info.view == v) {
- where.remove(i);
- break;
- }
- }
- }
-
- @TargetApi(11)
- private int getNumColumnsCompatible() {
- if (Build.VERSION.SDK_INT >= 11) {
- return super.getNumColumns();
- } else {
- try {
- Field numColumns = GridView.class.getSuperclass().getDeclaredField("mNumColumns");
- numColumns.setAccessible(true);
- return numColumns.getInt(this);
- } catch (Exception e) {
- if (mNumColumns != -1) {
- return mNumColumns;
- } else {
- return 2;
- }
- }
- }
- }
-
- @TargetApi(16)
- private int getColumnWidthCompatible() {
- if (Build.VERSION.SDK_INT >= 16) {
- return super.getColumnWidth();
- } else {
- try {
- Field numColumns = getClass().getSuperclass().getDeclaredField("mColumnWidth");
- numColumns.setAccessible(true);
- return numColumns.getInt(this);
- } catch (NoSuchFieldException e) {
- throw new RuntimeException(e);
- } catch (IllegalAccessException e) {
- throw new RuntimeException(e);
- }
- }
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- mViewForMeasureRowHeight = null;
- }
-
- public void invalidateRowHeight() {
- mRowHeight = -1;
- }
-
- public int getHeaderHeight(int row) {
- if (row >= 0) {
- return mHeaderViewInfos.get(row).view.getMeasuredHeight();
- }
-
- return 0;
- }
-
- @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
- public int getVerticalSpacing(){
- int value = 0;
-
- try {
- int currentapiVersion = android.os.Build.VERSION.SDK_INT;
- if (currentapiVersion < Build.VERSION_CODES.JELLY_BEAN){
- Field field = this.getClass().getSuperclass().getDeclaredField("mVerticalSpacing");
- field.setAccessible(true);
- value = field.getInt(this);
- } else{
- value = super.getVerticalSpacing();
- }
-
- }catch (Exception ex){
-
- }
-
- return value;
- }
-
- @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
- public int getHorizontalSpacing(){
- int value = 0;
-
- try {
- int currentapiVersion = android.os.Build.VERSION.SDK_INT;
- if (currentapiVersion < Build.VERSION_CODES.JELLY_BEAN){
- Field field = this.getClass().getSuperclass().getDeclaredField("mHorizontalSpacing");
- field.setAccessible(true);
- value = field.getInt(this);
- } else{
- value = super.getHorizontalSpacing();
- }
-
- }catch (Exception ex){
-
- }
-
- return value;
- }
-
- public int getRowHeight() {
- if (mRowHeight > 0) {
- // return mRowHeight;
- }
- ListAdapter adapter = getAdapter();
- int numColumns = getNumColumnsCompatible();
-
- // adapter has not been set or has no views in it;
- if (adapter == null || adapter.getCount() <= numColumns * (mHeaderViewInfos.size() + mFooterViewInfos.size()) || numColumns == -1) {
- return -1;
- }
- int mColumnWidth = getColumnWidthCompatible();
- View view = getAdapter().getView(numColumns * mHeaderViewInfos.size(), mViewForMeasureRowHeight, this);
- AbsListView.LayoutParams p = (AbsListView.LayoutParams) view.getLayoutParams();
- if (p == null) {
- p = new AbsListView.LayoutParams(-1, -2, 0);
- view.setLayoutParams(p);
- }
- int childHeightSpec = getChildMeasureSpec(
- MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), 0, p.height);
- int childWidthSpec = getChildMeasureSpec(
- MeasureSpec.makeMeasureSpec(mColumnWidth, MeasureSpec.EXACTLY), 0, p.width);
- view.measure(childWidthSpec, childHeightSpec);
- mViewForMeasureRowHeight = view;
- mRowHeight = view.getMeasuredHeight();
- return mRowHeight;
- }
-
- @TargetApi(11)
- public void tryToScrollToBottomSmoothly() {
- int lastPos = getAdapter().getCount() - 1;
- if (Build.VERSION.SDK_INT >= 11) {
- smoothScrollToPositionFromTop(lastPos, 0);
- } else {
- setSelection(lastPos);
- }
- }
-
- @TargetApi(11)
- public void tryToScrollToBottomSmoothly(int duration) {
- int lastPos = getAdapter().getCount() - 1;
- if (Build.VERSION.SDK_INT >= 11) {
- smoothScrollToPositionFromTop(lastPos, 0, duration);
- } else {
- setSelection(lastPos);
- }
- }
-
- @Override
- public void setAdapter(ListAdapter adapter) {
- if (mHeaderViewInfos.size() > 0 || mFooterViewInfos.size() > 0) {
- HeaderViewGridAdapter headerViewGridAdapter = new HeaderViewGridAdapter(mHeaderViewInfos, mFooterViewInfos, adapter);
- int numColumns = getNumColumnsCompatible();
- if (numColumns > 1) {
- headerViewGridAdapter.setNumColumns(numColumns);
- }
- headerViewGridAdapter.setRowHeight(getRowHeight());
- super.setAdapter(headerViewGridAdapter);
- } else {
- super.setAdapter(adapter);
- }
- }
-
- /**
- * full width
- */
- private class FullWidthFixedViewLayout extends FrameLayout {
-
- public FullWidthFixedViewLayout(Context context) {
- super(context);
- }
-
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- int realLeft = HeaderGridView.this.getPaddingLeft() + getPaddingLeft();
- // Try to make where it should be, from left, full width
- if (realLeft != left) {
- offsetLeftAndRight(realLeft - left);
- }
- super.onLayout(changed, left, top, right, bottom);
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- int targetWidth = HeaderGridView.this.getMeasuredWidth()
- - HeaderGridView.this.getPaddingLeft()
- - HeaderGridView.this.getPaddingRight();
- widthMeasureSpec = MeasureSpec.makeMeasureSpec(targetWidth,
- MeasureSpec.getMode(widthMeasureSpec));
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- }
- }
-
- @Override
- public void setNumColumns(int numColumns) {
- super.setNumColumns(numColumns);
- mNumColumns = numColumns;
- ListAdapter adapter = getAdapter();
- if (adapter != null && adapter instanceof HeaderViewGridAdapter) {
- ((HeaderViewGridAdapter) adapter).setNumColumns(numColumns);
- }
- }
-
- /**
- * ListAdapter used when a HeaderGridView has header views. This ListAdapter
- * wraps another one and also keeps track of the header views and their
- * associated data objects.
- * <p>This is intended as a base class; you will probably not need to
- * use this class directly in your own code.
- */
- private static class HeaderViewGridAdapter extends BaseAdapter implements WrapperListAdapter, Filterable {
- // This is used to notify the container of updates relating to number of columns
- // or headers changing, which changes the number of placeholders needed
- private final DataSetObservable mDataSetObservable = new DataSetObservable();
- private final ListAdapter mAdapter;
- static final ArrayList<FixedViewInfo> EMPTY_INFO_LIST =
- new ArrayList<FixedViewInfo>();
-
- // This ArrayList is assumed to NOT be null.
- ArrayList<FixedViewInfo> mHeaderViewInfos;
- ArrayList<FixedViewInfo> mFooterViewInfos;
- private int mNumColumns = 1;
- private int mRowHeight = -1;
- boolean mAreAllFixedViewsSelectable;
- private final boolean mIsFilterable;
- private boolean mCachePlaceHoldView = true;
- // From Recycle Bin or calling getView, this a question...
- private boolean mCacheFirstHeaderView = false;
-
- public HeaderViewGridAdapter(ArrayList<FixedViewInfo> headerViewInfos, ArrayList<FixedViewInfo> footViewInfos, ListAdapter adapter) {
- mAdapter = adapter;
- mIsFilterable = adapter instanceof Filterable;
- if (headerViewInfos == null) {
- mHeaderViewInfos = EMPTY_INFO_LIST;
- } else {
- mHeaderViewInfos = headerViewInfos;
- }
-
- if (footViewInfos == null) {
- mFooterViewInfos = EMPTY_INFO_LIST;
- } else {
- mFooterViewInfos = footViewInfos;
- }
- mAreAllFixedViewsSelectable = areAllListInfosSelectable(mHeaderViewInfos)
- && areAllListInfosSelectable(mFooterViewInfos);
- }
-
- public void setNumColumns(int numColumns) {
- if (numColumns < 1) {
- return;
- }
- if (mNumColumns != numColumns) {
- mNumColumns = numColumns;
- notifyDataSetChanged();
- }
- }
-
- public void setRowHeight(int height) {
- mRowHeight = height;
- }
-
- public int getHeadersCount() {
- return mHeaderViewInfos.size();
- }
-
- public int getFootersCount() {
- return mFooterViewInfos.size();
- }
-
- @Override
- public boolean isEmpty() {
- return (mAdapter == null || mAdapter.isEmpty()) && getHeadersCount() == 0 && getFootersCount() == 0;
- }
-
- private boolean areAllListInfosSelectable(ArrayList<FixedViewInfo> infos) {
- if (infos != null) {
- for (FixedViewInfo info : infos) {
- if (!info.isSelectable) {
- return false;
- }
- }
- }
- return true;
- }
-
- public boolean removeHeader(View v) {
- for (int i = 0; i < mHeaderViewInfos.size(); i++) {
- FixedViewInfo info = mHeaderViewInfos.get(i);
- if (info.view == v) {
- mHeaderViewInfos.remove(i);
- mAreAllFixedViewsSelectable =
- areAllListInfosSelectable(mHeaderViewInfos) && areAllListInfosSelectable(mFooterViewInfos);
- mDataSetObservable.notifyChanged();
- return true;
- }
- }
- return false;
- }
-
- public boolean removeFooter(View v) {
- for (int i = 0; i < mFooterViewInfos.size(); i++) {
- FixedViewInfo info = mFooterViewInfos.get(i);
- if (info.view == v) {
- mFooterViewInfos.remove(i);
- mAreAllFixedViewsSelectable =
- areAllListInfosSelectable(mHeaderViewInfos) && areAllListInfosSelectable(mFooterViewInfos);
- mDataSetObservable.notifyChanged();
- return true;
- }
- }
- return false;
- }
-
- @Override
- public int getCount() {
- if (mAdapter != null) {
- return (getFootersCount() + getHeadersCount()) * mNumColumns + getAdapterAndPlaceHolderCount();
- } else {
- return (getFootersCount() + getHeadersCount()) * mNumColumns;
- }
- }
-
- @Override
- public boolean areAllItemsEnabled() {
- if (mAdapter != null) {
- return mAreAllFixedViewsSelectable && mAdapter.areAllItemsEnabled();
- } else {
- return true;
- }
- }
-
- private int getAdapterAndPlaceHolderCount() {
- final int adapterCount = (int) (Math.ceil(1f * mAdapter.getCount() / mNumColumns) * mNumColumns);
- return adapterCount;
- }
-
- @Override
- public boolean isEnabled(int position) {
- // Header (negative positions will throw an IndexOutOfBoundsException)
- int numHeadersAndPlaceholders = getHeadersCount() * mNumColumns;
- if (position < numHeadersAndPlaceholders) {
- return position % mNumColumns == 0
- && mHeaderViewInfos.get(position / mNumColumns).isSelectable;
- }
-
- // Adapter
- final int adjPosition = position - numHeadersAndPlaceholders;
- int adapterCount = 0;
- if (mAdapter != null) {
- adapterCount = getAdapterAndPlaceHolderCount();
- if (adjPosition < adapterCount) {
- return adjPosition < mAdapter.getCount() && mAdapter.isEnabled(adjPosition);
- }
- }
-
- // Footer (off-limits positions will throw an IndexOutOfBoundsException)
- final int footerPosition = adjPosition - adapterCount;
- return footerPosition % mNumColumns == 0
- && mFooterViewInfos.get(footerPosition / mNumColumns).isSelectable;
- }
-
- @Override
- public Object getItem(int position) {
- // Header (negative positions will throw an ArrayIndexOutOfBoundsException)
- int numHeadersAndPlaceholders = getHeadersCount() * mNumColumns;
- if (position < numHeadersAndPlaceholders) {
- if (position % mNumColumns == 0) {
- return mHeaderViewInfos.get(position / mNumColumns).data;
- }
- return null;
- }
-
- // Adapter
- final int adjPosition = position - numHeadersAndPlaceholders;
- int adapterCount = 0;
- if (mAdapter != null) {
- adapterCount = getAdapterAndPlaceHolderCount();
- if (adjPosition < adapterCount) {
- if (adjPosition < mAdapter.getCount()) {
- return mAdapter.getItem(adjPosition);
- } else {
- return null;
- }
- }
- }
-
- // Footer (off-limits positions will throw an IndexOutOfBoundsException)
- final int footerPosition = adjPosition - adapterCount;
- if (footerPosition % mNumColumns == 0) {
- return mFooterViewInfos.get(footerPosition).data;
- } else {
- return null;
- }
- }
-
- @Override
- public long getItemId(int position) {
- int numHeadersAndPlaceholders = getHeadersCount() * mNumColumns;
- if (mAdapter != null && position >= numHeadersAndPlaceholders) {
- int adjPosition = position - numHeadersAndPlaceholders;
- int adapterCount = mAdapter.getCount();
- if (adjPosition < adapterCount) {
- return mAdapter.getItemId(adjPosition);
- }
- }
- return -1;
- }
-
- @Override
- public boolean hasStableIds() {
- if (mAdapter != null) {
- return mAdapter.hasStableIds();
- }
- return false;
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- if (DEBUG) {
- Log.d(LOG_TAG, String.format("getView: %s, reused: %s", position, convertView == null));
- }
- // Header (negative positions will throw an ArrayIndexOutOfBoundsException)
- int numHeadersAndPlaceholders = getHeadersCount() * mNumColumns;
- if (position < numHeadersAndPlaceholders) {
- View headerViewContainer = mHeaderViewInfos
- .get(position / mNumColumns).viewContainer;
- if (position % mNumColumns == 0) {
- return headerViewContainer;
- } else {
- if (convertView == null) {
- convertView = new View(parent.getContext());
- }
- // We need to do this because GridView uses the height of the last item
- // in a row to determine the height for the entire row.
- convertView.setVisibility(View.INVISIBLE);
- convertView.setMinimumHeight(headerViewContainer.getHeight());
- return convertView;
- }
- }
- // Adapter
- final int adjPosition = position - numHeadersAndPlaceholders;
- int adapterCount = 0;
- if (mAdapter != null) {
- adapterCount = getAdapterAndPlaceHolderCount();
- if (adjPosition < adapterCount) {
- if (adjPosition < mAdapter.getCount()) {
- View view = mAdapter.getView(adjPosition, convertView, parent);
- return view;
- } else {
- if (convertView == null) {
- convertView = new View(parent.getContext());
- }
- convertView.setVisibility(View.INVISIBLE);
- convertView.setMinimumHeight(mRowHeight);
- return convertView;
- }
- }
- }
- // Footer
- final int footerPosition = adjPosition - adapterCount;
- if (footerPosition < getCount()) {
- View footViewContainer = mFooterViewInfos
- .get(footerPosition / mNumColumns).viewContainer;
- if (position % mNumColumns == 0) {
- return footViewContainer;
- } else {
- if (convertView == null) {
- convertView = new View(parent.getContext());
- }
- // We need to do this because GridView uses the height of the last item
- // in a row to determine the height for the entire row.
- convertView.setVisibility(View.INVISIBLE);
- convertView.setMinimumHeight(footViewContainer.getHeight());
- return convertView;
- }
- }
- throw new ArrayIndexOutOfBoundsException(position);
- }
-
- @Override
- public int getItemViewType(int position) {
-
- final int numHeadersAndPlaceholders = getHeadersCount() * mNumColumns;
- final int adapterViewTypeStart = mAdapter == null ? 0 : mAdapter.getViewTypeCount() - 1;
- int type = AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER;
- if (mCachePlaceHoldView) {
- // Header
- if (position < numHeadersAndPlaceholders) {
- if (position == 0) {
- if (mCacheFirstHeaderView) {
- type = adapterViewTypeStart + mHeaderViewInfos.size() + mFooterViewInfos.size() + 1 + 1;
- }
- }
- if (position % mNumColumns != 0) {
- type = adapterViewTypeStart + (position / mNumColumns + 1);
- }
- }
- }
-
- // Adapter
- final int adjPosition = position - numHeadersAndPlaceholders;
- int adapterCount = 0;
- if (mAdapter != null) {
- adapterCount = getAdapterAndPlaceHolderCount();
- if (adjPosition >= 0 && adjPosition < adapterCount) {
- if (adjPosition < mAdapter.getCount()) {
- type = mAdapter.getItemViewType(adjPosition);
- } else {
- if (mCachePlaceHoldView) {
- type = adapterViewTypeStart + mHeaderViewInfos.size() + 1;
- }
- }
- }
- }
-
- if (mCachePlaceHoldView) {
- // Footer
- final int footerPosition = adjPosition - adapterCount;
- if (footerPosition >= 0 && footerPosition < getCount() && (footerPosition % mNumColumns) != 0) {
- type = adapterViewTypeStart + mHeaderViewInfos.size() + 1 + (footerPosition / mNumColumns + 1);
- }
- }
- if (DEBUG) {
- Log.d(LOG_TAG, String.format("getItemViewType: pos: %s, result: %s", position, type, mCachePlaceHoldView, mCacheFirstHeaderView));
- }
- return type;
- }
-
- /**
- * content view, content view holder, header[0], header and footer placeholder(s)
- *
- * @return
- */
- @Override
- public int getViewTypeCount() {
- int count = mAdapter == null ? 1 : mAdapter.getViewTypeCount();
- if (mCachePlaceHoldView) {
- int offset = mHeaderViewInfos.size() + 1 + mFooterViewInfos.size();
- if (mCacheFirstHeaderView) {
- offset += 1;
- }
- count += offset;
- }
- if (DEBUG) {
- Log.d(LOG_TAG, String.format("getViewTypeCount: %s", count));
- }
- return count;
- }
-
- @Override
- public void registerDataSetObserver(DataSetObserver observer) {
- mDataSetObservable.registerObserver(observer);
- if (mAdapter != null) {
- mAdapter.registerDataSetObserver(observer);
- }
- }
-
- @Override
- public void unregisterDataSetObserver(DataSetObserver observer) {
- mDataSetObservable.unregisterObserver(observer);
- if (mAdapter != null) {
- mAdapter.unregisterDataSetObserver(observer);
- }
- }
-
- @Override
- public Filter getFilter() {
- if (mIsFilterable) {
- return ((Filterable) mAdapter).getFilter();
- }
- return null;
- }
-
- @Override
- public ListAdapter getWrappedAdapter() {
- return mAdapter;
- }
-
- public void notifyDataSetChanged() {
- mDataSetObservable.notifyChanged();
- }
- }
-}
diff --git a/app/src/main/java/github/daneren2005/dsub/view/PlaylistSongView.java b/app/src/main/java/github/daneren2005/dsub/view/PlaylistSongView.java
index 0264a785..581b92e4 100644
--- a/app/src/main/java/github/daneren2005/dsub/view/PlaylistSongView.java
+++ b/app/src/main/java/github/daneren2005/dsub/view/PlaylistSongView.java
@@ -34,16 +34,12 @@ import github.daneren2005.dsub.util.FileUtil;
import github.daneren2005.dsub.util.SyncUtil;
import github.daneren2005.dsub.util.Util;
-public class PlaylistSongView extends UpdateView {
+public class PlaylistSongView extends UpdateView2<Playlist, List<MusicDirectory.Entry>> {
private static final String TAG = PlaylistSongView.class.getSimpleName();
- private Context context;
- private Playlist playlist;
-
private TextView titleView;
private TextView countView;
private int count = 0;
- private List<MusicDirectory.Entry> songs;
public PlaylistSongView(Context context) {
super(context, false);
@@ -54,9 +50,7 @@ public class PlaylistSongView extends UpdateView {
countView = (TextView) findViewById(R.id.basic_count_count);
}
- protected void setObjectImpl(Object obj1, Object obj2) {
- this.playlist = (Playlist) obj1;
- this.songs = (List<MusicDirectory.Entry>) obj2;
+ protected void setObjectImpl(Playlist playlist, List<MusicDirectory.Entry> songs) {
count = 0;
titleView.setText(playlist.getName());
// Make sure to hide initially so it's not present briefly before update
@@ -69,11 +63,11 @@ public class PlaylistSongView extends UpdateView {
count = 0;
// Don't try to lookup playlist for Create New
- if(!"-1".equals(playlist.getId())) {
- MusicDirectory cache = FileUtil.deserialize(context, Util.getCacheName(context, "playlist", playlist.getId()), MusicDirectory.class);
+ if(!"-1".equals(item.getId())) {
+ MusicDirectory cache = FileUtil.deserialize(context, Util.getCacheName(context, "playlist", item.getId()), MusicDirectory.class);
if(cache != null) {
// Try to find song instances in the given playlists
- for(MusicDirectory.Entry song: songs) {
+ for(MusicDirectory.Entry song: item2) {
if(cache.getChildren().contains(song)) {
count++;
}
diff --git a/app/src/main/java/github/daneren2005/dsub/view/PlaylistView.java b/app/src/main/java/github/daneren2005/dsub/view/PlaylistView.java
index 25613984..2cd27b19 100644
--- a/app/src/main/java/github/daneren2005/dsub/view/PlaylistView.java
+++ b/app/src/main/java/github/daneren2005/dsub/view/PlaylistView.java
@@ -21,11 +21,11 @@ package github.daneren2005.dsub.view;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
-import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
import github.daneren2005.dsub.R;
import github.daneren2005.dsub.domain.Playlist;
+import github.daneren2005.dsub.util.ImageLoader;
import github.daneren2005.dsub.util.SyncUtil;
/**
@@ -33,37 +33,31 @@ import github.daneren2005.dsub.util.SyncUtil;
*
* @author Sindre Mehus
*/
-public class PlaylistView extends UpdateView {
+public class PlaylistView extends UpdateView<Playlist> {
private static final String TAG = PlaylistView.class.getSimpleName();
- private Context context;
- private Playlist playlist;
-
+ private View coverArtView;
private TextView titleView;
+ private ImageLoader imageLoader;
- public PlaylistView(Context context) {
+ public PlaylistView(Context context, ImageLoader imageLoader, boolean largeCell) {
super(context);
- this.context = context;
- LayoutInflater.from(context).inflate(R.layout.basic_list_item, this, true);
+ LayoutInflater.from(context).inflate(largeCell ? R.layout.playlist_cell_item : R.layout.playlist_list_item, this, true);
- titleView = (TextView) findViewById(R.id.item_name);
- starButton = (ImageButton) findViewById(R.id.item_star);
- starButton.setFocusable(false);
+ coverArtView = findViewById(R.id.playlist_coverart);
+ titleView = (TextView) findViewById(R.id.playlist_title);
moreButton = (ImageView) findViewById(R.id.item_more);
- moreButton.setOnClickListener(new View.OnClickListener() {
- public void onClick(View v) {
- v.showContextMenu();
- }
- });
+
+ this.imageLoader = imageLoader;
}
- protected void setObjectImpl(Object obj) {
- this.playlist = (Playlist) obj;
+ protected void setObjectImpl(Playlist playlist) {
titleView.setText(playlist.getName());
+ imageTask = imageLoader.loadImage(coverArtView, playlist, false, true);
}
@Override
protected void updateBackground() {
- pinned = SyncUtil.isSyncedPlaylist(context, playlist.getId());
+ pinned = SyncUtil.isSyncedPlaylist(context, item.getId());
}
}
diff --git a/app/src/main/java/github/daneren2005/dsub/view/PodcastChannelView.java b/app/src/main/java/github/daneren2005/dsub/view/PodcastChannelView.java
index ada8019e..4878ad67 100644
--- a/app/src/main/java/github/daneren2005/dsub/view/PodcastChannelView.java
+++ b/app/src/main/java/github/daneren2005/dsub/view/PodcastChannelView.java
@@ -30,18 +30,14 @@ import github.daneren2005.dsub.util.SyncUtil;
import github.daneren2005.dsub.util.FileUtil;
import java.io.File;
-public class PodcastChannelView extends UpdateView {
+public class PodcastChannelView extends UpdateView<PodcastChannel> {
private static final String TAG = PodcastChannelView.class.getSimpleName();
- private Context context;
- private PodcastChannel channel;
private File file;
-
private TextView titleView;
public PodcastChannelView(Context context) {
super(context);
- this.context = context;
LayoutInflater.from(context).inflate(R.layout.basic_list_item, this, true);
titleView = (TextView) findViewById(R.id.item_name);
@@ -55,8 +51,7 @@ public class PodcastChannelView extends UpdateView {
});
}
- protected void setObjectImpl(Object obj) {
- channel = (PodcastChannel) obj;
+ protected void setObjectImpl(PodcastChannel channel) {
if(channel.getName() != null) {
titleView.setText(channel.getName());
} else {
@@ -67,7 +62,7 @@ public class PodcastChannelView extends UpdateView {
@Override
protected void updateBackground() {
- if(SyncUtil.isSyncedPodcast(context, channel.getId())) {
+ if(SyncUtil.isSyncedPodcast(context, item.getId())) {
if(exists) {
shaded = false;
exists = false;
diff --git a/app/src/main/java/github/daneren2005/dsub/view/SettingView.java b/app/src/main/java/github/daneren2005/dsub/view/SettingView.java
index 1c78706e..d46dc5d2 100644
--- a/app/src/main/java/github/daneren2005/dsub/view/SettingView.java
+++ b/app/src/main/java/github/daneren2005/dsub/view/SettingView.java
@@ -17,34 +17,42 @@ package github.daneren2005.dsub.view;
import android.content.Context;
import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.CheckedTextView;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.TextView;
import github.daneren2005.dsub.R;
import github.daneren2005.dsub.domain.User;
import static github.daneren2005.dsub.domain.User.Setting;
-public class SettingView extends UpdateView {
- Setting setting;
-
- CheckedTextView view;
+public class SettingView extends UpdateView2<Setting, Boolean> {
+ private final TextView titleView;
+ private final CheckBox checkBox;
public SettingView(Context context) {
super(context, false);
this.context = context;
- LayoutInflater.from(context).inflate(android.R.layout.simple_list_item_multiple_choice, this, true);
+ LayoutInflater.from(context).inflate(R.layout.basic_choice_item, this, true);
- view = (CheckedTextView) findViewById(android.R.id.text1);
+ titleView = (TextView) findViewById(R.id.item_name);
+ checkBox = (CheckBox) findViewById(R.id.item_checkbox);
+ checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ if(item != null) {
+ item.setValue(isChecked);
+ }
+ }
+ });
+ checkBox.setClickable(false);
}
- protected void setObjectImpl(Object obj, Object editable) {
- this.setting = (Setting) obj;
-
+ protected void setObjectImpl(Setting setting, Boolean isEditable) {
// Can't edit non-role parts
String name = setting.getName();
if(name.indexOf("Role") == -1) {
- editable = false;
+ item2 = false;
}
int res = -1;
@@ -74,29 +82,28 @@ public class SettingView extends UpdateView {
res = R.string.admin_role_lastfm;
} else {
// Last resort to display the raw value
- view.setText(name);
+ titleView.setText(name);
}
if(res != -1) {
- view.setText(res);
+ titleView.setText(res);
}
if(setting.getValue()) {
- view.setChecked(setting.getValue());
+ checkBox.setChecked(setting.getValue());
} else {
- view.setChecked(false);
+ checkBox.setChecked(false);
}
- if((Boolean) editable) {
- view.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- view.toggle();
- setting.setValue(view.isChecked());
- }
- });
- } else {
- view.setOnClickListener(null);
- }
+ checkBox.setEnabled(item2);
+ }
+
+ @Override
+ public boolean isCheckable() {
+ return item2;
+ }
+
+ public void setChecked(boolean checked) {
+ checkBox.setChecked(checked);
}
}
diff --git a/app/src/main/java/github/daneren2005/dsub/view/ShareView.java b/app/src/main/java/github/daneren2005/dsub/view/ShareView.java
index bfb5b198..12294369 100644
--- a/app/src/main/java/github/daneren2005/dsub/view/ShareView.java
+++ b/app/src/main/java/github/daneren2005/dsub/view/ShareView.java
@@ -31,7 +31,7 @@ import java.util.Locale;
import github.daneren2005.dsub.R;
import github.daneren2005.dsub.domain.Share;
-public class ShareView extends UpdateView {
+public class ShareView extends UpdateView<Share> {
private static final String TAG = ShareView.class.getSimpleName();
private TextView titleView;
@@ -53,8 +53,7 @@ public class ShareView extends UpdateView {
});
}
- public void setObjectImpl(Object obj) {
- Share share = (Share) obj;
+ public void setObjectImpl(Share share) {
titleView.setText(share.getName());
if(share.getExpires() != null) {
descriptionView.setText(context.getResources().getString(R.string.share_expires, new SimpleDateFormat("E MMM d, yyyy", Locale.ENGLISH).format(share.getExpires())));
diff --git a/app/src/main/java/github/daneren2005/dsub/view/SongView.java b/app/src/main/java/github/daneren2005/dsub/view/SongView.java
index 2fbaedc3..b9c5fa50 100644
--- a/app/src/main/java/github/daneren2005/dsub/view/SongView.java
+++ b/app/src/main/java/github/daneren2005/dsub/view/SongView.java
@@ -29,6 +29,7 @@ import github.daneren2005.dsub.domain.MusicDirectory;
import github.daneren2005.dsub.domain.PodcastEpisode;
import github.daneren2005.dsub.service.DownloadService;
import github.daneren2005.dsub.service.DownloadFile;
+import github.daneren2005.dsub.util.DrawableTint;
import github.daneren2005.dsub.util.Util;
import java.io.File;
@@ -38,20 +39,17 @@ import java.io.File;
*
* @author Sindre Mehus
*/
-public class SongView extends UpdateView implements Checkable {
- private static final String TAG = SongView.class.getSimpleName();
+public class SongView extends UpdateView2<MusicDirectory.Entry, Boolean> {
+ private static final String TAG = SongView.class.getSimpleName();
- private MusicDirectory.Entry song;
-
- private CheckedTextView checkedTextView;
- private TextView titleTextView;
- private TextView artistTextView;
- private TextView durationTextView;
- private TextView statusTextView;
+ private TextView titleTextView;
+ private TextView artistTextView;
+ private TextView durationTextView;
+ private TextView statusTextView;
private ImageView statusImageView;
private ImageView bookmarkButton;
private View bottomRowView;
-
+
private DownloadService downloadService;
private long revision = -1;
private DownloadFile downloadFile;
@@ -68,35 +66,28 @@ public class SongView extends UpdateView implements Checkable {
private boolean isBookmarked = false;
private boolean bookmarked = false;
- public SongView(Context context) {
- super(context);
- LayoutInflater.from(context).inflate(R.layout.song_list_item, this, true);
+ public SongView(Context context) {
+ super(context);
+ LayoutInflater.from(context).inflate(R.layout.song_list_item, this, true);
- checkedTextView = (CheckedTextView) findViewById(R.id.song_check);
- titleTextView = (TextView) findViewById(R.id.song_title);
- artistTextView = (TextView) findViewById(R.id.song_artist);
- durationTextView = (TextView) findViewById(R.id.song_duration);
- statusTextView = (TextView) findViewById(R.id.song_status);
+ titleTextView = (TextView) findViewById(R.id.song_title);
+ artistTextView = (TextView) findViewById(R.id.song_artist);
+ durationTextView = (TextView) findViewById(R.id.song_duration);
+ statusTextView = (TextView) findViewById(R.id.song_status);
statusImageView = (ImageView) findViewById(R.id.song_status_icon);
ratingBar = (RatingBar) findViewById(R.id.song_rating);
- starButton = (ImageButton) findViewById(R.id.song_star);
- starButton.setFocusable(false);
+ starButton = (ImageButton) findViewById(R.id.song_star);
+ starButton.setFocusable(false);
bookmarkButton = (ImageButton) findViewById(R.id.song_bookmark);
bookmarkButton.setFocusable(false);
- moreButton = (ImageView) findViewById(R.id.artist_more);
- moreButton.setOnClickListener(new View.OnClickListener() {
- public void onClick(View v) {
- v.showContextMenu();
- }
- });
+ moreButton = (ImageView) findViewById(R.id.more_button);
bottomRowView = findViewById(R.id.song_bottom);
- }
+ }
+
+ public void setObjectImpl(MusicDirectory.Entry song, Boolean checkable) {
+ this.checkable = checkable;
- public void setObjectImpl(Object obj1, Object obj2) {
- this.song = (MusicDirectory.Entry) obj1;
- boolean checkable = (Boolean) obj2;
-
- StringBuilder artist = new StringBuilder(40);
+ StringBuilder artist = new StringBuilder(40);
boolean isPodcast = song instanceof PodcastEpisode;
if(!song.isVideo() || isPodcast) {
@@ -110,11 +101,11 @@ public class SongView extends UpdateView implements Checkable {
else if(song.getArtist() != null) {
artist.append(song.getArtist());
}
-
+
if(isPodcast) {
String status = ((PodcastEpisode) song).getStatus();
int statusRes = -1;
-
+
if("error".equals(status)) {
statusRes = R.string.song_details_error;
} else if("skipped".equals(status)) {
@@ -122,7 +113,7 @@ public class SongView extends UpdateView implements Checkable {
} else if("downloading".equals(status)) {
statusRes = R.string.song_details_downloading;
}
-
+
if(statusRes != -1) {
artist.append(" (");
artist.append(getContext().getString(statusRes));
@@ -136,16 +127,15 @@ public class SongView extends UpdateView implements Checkable {
bottomRowView.setVisibility(View.GONE);
statusTextView.setText(Util.formatDuration(song.getDuration()));
}
-
+
String title = song.getTitle();
Integer track = song.getTrack();
if(track != null && Util.getDisplayTrack(context)) {
title = String.format("%02d", track) + " " + title;
}
- titleTextView.setText(title);
+ titleTextView.setText(title);
artistTextView.setText(artist);
- checkedTextView.setVisibility(checkable && !song.isVideo() ? View.VISIBLE : View.GONE);
this.setBackgroundColor(0x00000000);
ratingBar.setVisibility(View.GONE);
@@ -155,28 +145,28 @@ public class SongView extends UpdateView implements Checkable {
loaded = false;
dontChangeDownloadFile = false;
}
-
+
public void setDownloadFile(DownloadFile downloadFile) {
this.downloadFile = downloadFile;
dontChangeDownloadFile = true;
}
-
+
public DownloadFile getDownloadFile() {
return downloadFile;
}
-
+
@Override
protected void updateBackground() {
- if (downloadService == null) {
+ if (downloadService == null) {
downloadService = DownloadService.getInstance();
if(downloadService == null) {
return;
}
- }
+ }
long newRevision = downloadService.getDownloadListUpdateRevision();
if((revision != newRevision && dontChangeDownloadFile == false) || downloadFile == null) {
- downloadFile = downloadService.forSong(song);
+ downloadFile = downloadService.forSong(item);
revision = newRevision;
}
@@ -184,28 +174,31 @@ public class SongView extends UpdateView implements Checkable {
isSaved = downloadFile.isSaved();
partialFile = downloadFile.getPartialFile();
partialFileExists = partialFile.exists();
- isStarred = song.isStarred();
- isBookmarked = song.getBookmark() != null;
- isRated = song.getRating();
-
+ isStarred = item.isStarred();
+ isBookmarked = item.getBookmark() != null;
+ isRated = item.getRating();
+
// Check if needs to load metadata: check against all fields that we know are null in offline mode
- if(song.getBitRate() == null && song.getDuration() == null && song.getDiscNumber() == null && isWorkDone) {
- song.loadMetadata(downloadFile.getCompleteFile());
+ if(item.getBitRate() == null && item.getDuration() == null && item.getDiscNumber() == null && isWorkDone) {
+ item.loadMetadata(downloadFile.getCompleteFile());
loaded = true;
}
}
@Override
- protected void update() {
+ protected void update() {
if(loaded) {
- setObjectImpl(song, checkedTextView.getVisibility() == View.VISIBLE);
+ setObjectImpl(item, item2);
+ }
+ if (downloadService == null || downloadFile == null) {
+ return;
}
- if (downloadService == null || downloadFile == null) {
- return;
- }
- if(song.isStarred()) {
+ if(item.isStarred()) {
if(!starred) {
+ if(starButton.getDrawable() == null) {
+ starButton.setImageDrawable(DrawableTint.getTintedDrawable(context, R.drawable.ic_toggle_star));
+ }
starButton.setVisibility(View.VISIBLE);
starred = true;
}
@@ -216,13 +209,13 @@ public class SongView extends UpdateView implements Checkable {
}
}
- if (isWorkDone) {
+ if (isWorkDone) {
int moreImage = isSaved ? R.drawable.download_pinned : R.drawable.download_cached;
if(moreImage != this.moreImage) {
moreButton.setImageResource(moreImage);
this.moreImage = moreImage;
}
- } else if(this.moreImage != R.drawable.download_none_light) {
+ } else if(this.moreImage != R.drawable.download_none_light) {
int[] attrs = new int[] {R.attr.download_none};
TypedArray typedArray = context.obtainStyledAttributes(attrs);
moreButton.setImageResource(typedArray.getResourceId(0, 0));
@@ -230,7 +223,7 @@ public class SongView extends UpdateView implements Checkable {
this.moreImage = R.drawable.download_none_light;
}
- if (downloadFile.isDownloading() && !downloadFile.isDownloadCancelled() && partialFileExists) {
+ if (downloadFile.isDownloading() && !downloadFile.isDownloadCancelled() && partialFileExists) {
double percentage = (partialFile.length() * 100.0) / downloadFile.getEstimatedSize();
percentage = Math.min(percentage, 100);
statusTextView.setText((int)percentage + " %");
@@ -238,29 +231,33 @@ public class SongView extends UpdateView implements Checkable {
statusImageView.setVisibility(View.VISIBLE);
rightImage = true;
}
- } else if(rightImage) {
- statusTextView.setText(null);
+ } else if(rightImage) {
+ statusTextView.setText(null);
statusImageView.setVisibility(View.GONE);
rightImage = false;
- }
+ }
- boolean playing = downloadService.getCurrentPlaying() == downloadFile;
- if (playing) {
+ boolean playing = downloadService.getCurrentPlaying() == downloadFile;
+ if (playing) {
if(!this.playing) {
this.playing = playing;
int[] attrs = new int[] {R.attr.media_button_start};
TypedArray typedArray = context.obtainStyledAttributes(attrs);
- titleTextView.setCompoundDrawablesWithIntrinsicBounds(typedArray.getResourceId(0, 0), 0, 0, 0);
+ titleTextView.setCompoundDrawablesWithIntrinsicBounds(typedArray.getResourceId(0, 0), 0, 0, 0);
}
- } else {
+ } else {
if(this.playing) {
this.playing = playing;
- titleTextView.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
+ titleTextView.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
}
}
if(isBookmarked) {
if(!bookmarked) {
+ if(bookmarkButton.getDrawable() == null) {
+ bookmarkButton.setImageDrawable(DrawableTint.getTintedDrawable(context, R.drawable.ic_menu_bookmark_selected));
+ }
+
bookmarkButton.setVisibility(View.VISIBLE);
bookmarked = true;
}
@@ -295,24 +292,9 @@ public class SongView extends UpdateView implements Checkable {
rating = isRated;
}
- }
-
- @Override
- public void setChecked(boolean b) {
- checkedTextView.setChecked(b);
- }
-
- @Override
- public boolean isChecked() {
- return checkedTextView.isChecked();
- }
-
- @Override
- public void toggle() {
- checkedTextView.toggle();
- }
+ }
public MusicDirectory.Entry getEntry() {
- return song;
+ return item;
}
}
diff --git a/app/src/main/java/github/daneren2005/dsub/view/UnscrollableGridView.java b/app/src/main/java/github/daneren2005/dsub/view/UnscrollableGridView.java
deleted file mode 100644
index 3047d5d7..00000000
--- a/app/src/main/java/github/daneren2005/dsub/view/UnscrollableGridView.java
+++ /dev/null
@@ -1,128 +0,0 @@
-package github.daneren2005.dsub.view;
-
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.os.Build;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.View;
-import android.widget.AbsListView;
-import android.widget.GridView;
-import android.widget.ListAdapter;
-
-import java.lang.reflect.Field;
-
-/**
- * Created by Scott on 4/26/2014.
- */
-public class UnscrollableGridView extends GridView {
- private static final String TAG = UnscrollableGridView.class.getSimpleName();
-
- public UnscrollableGridView(Context context) {
- super(context);
- }
-
- public UnscrollableGridView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public UnscrollableGridView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- }
-
- @Override
- public int getColumnWidth() {
- // This method will be called from onMeasure() too.
- // It's better to use getMeasuredWidth(), as it is safe in this case.
-
- int hSpacing = 20;
- try {
- Field field = GridView.class.getDeclaredField("mHorizontalSpacing");
- field.setAccessible(true);
- hSpacing = field.getInt(this);
- } catch(Exception e) {
-
- }
-
- final int totalHorizontalSpacing = getNumColumnsCompat() > 0 ? (getNumColumnsCompat() - 1) * hSpacing : 0;
- return (getMeasuredWidth() - getPaddingLeft() - getPaddingRight() - totalHorizontalSpacing) / getNumColumnsCompat();
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- // Sets the padding for this view.
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-
- final int measuredWidth = getMeasuredWidth();
- final int childWidth = getColumnWidth();
- int childHeight = 0;
-
- // If there's an adapter, use it to calculate the height of this view.
- final ListAdapter adapter = getAdapter();
- final int count;
-
- // There shouldn't be any inherent size (due to padding) if there are no child views.
- if (adapter == null || (count = adapter.getCount()) == 0) {
- setMeasuredDimension(0, 0);
- return;
- }
-
- // Get the first child from the adapter.
- final View child = adapter.getView(0, null, this);
- if (child != null) {
- // Set a default LayoutParams on the child, if it doesn't have one on its own.
- AbsListView.LayoutParams params = (AbsListView.LayoutParams) child.getLayoutParams();
- if (params == null) {
- params = new AbsListView.LayoutParams(AbsListView.LayoutParams.WRAP_CONTENT,
- AbsListView.LayoutParams.WRAP_CONTENT);
- child.setLayoutParams(params);
- }
-
- // Measure the exact width of the child, and the height based on the width.
- // Note: the child takes care of calculating its height.
- int childWidthSpec = MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.EXACTLY);
- int childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
- child.measure(childWidthSpec, childHeightSpec);
- childHeight = child.getMeasuredHeight();
- }
-
- int vSpacing = 10;
- try {
- Field field = GridView.class.getDeclaredField("mVerticalSpacing");
- field.setAccessible(true);
- vSpacing = field.getInt(this);
- } catch(Exception e) {
-
- }
-
- // Number of rows required to 'mTotal' items.
- final int rows = (int) Math.ceil((double) getCount() / getNumColumnsCompat());
- final int childrenHeight = childHeight * rows;
- final int totalVerticalSpacing = rows > 0 ? (rows - 1) * vSpacing : 0;
-
- // Total height of this view.
- final int measuredHeight = Math.abs(childrenHeight + getPaddingTop() + getPaddingBottom() + totalVerticalSpacing);
- setMeasuredDimension(measuredWidth, measuredHeight);
- }
-
- private int getNumColumnsCompat() {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
- return getNumColumnsCompat11();
- } else {
- int columns = 0;
- int children = getChildCount();
- if (children > 0) {
- int width = getChildAt(0).getMeasuredWidth();
- if (width > 0) {
- columns = getWidth() / width;
- }
- }
- return columns > 0 ? columns : AUTO_FIT;
- }
- }
-
- @TargetApi(Build.VERSION_CODES.HONEYCOMB)
- private int getNumColumnsCompat11() {
- return getNumColumns();
- }
-}
diff --git a/app/src/main/java/github/daneren2005/dsub/view/UpdateView.java b/app/src/main/java/github/daneren2005/dsub/view/UpdateView.java
index f9c62121..fd74c872 100644
--- a/app/src/main/java/github/daneren2005/dsub/view/UpdateView.java
+++ b/app/src/main/java/github/daneren2005/dsub/view/UpdateView.java
@@ -20,9 +20,10 @@ package github.daneren2005.dsub.view;
import android.content.Context;
import android.content.res.TypedArray;
-import android.graphics.Color;
+import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Looper;
+import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
@@ -37,20 +38,21 @@ import java.util.List;
import java.util.WeakHashMap;
import github.daneren2005.dsub.domain.MusicDirectory;
-import github.daneren2005.dsub.util.ImageLoader;
import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.util.DrawableTint;
import github.daneren2005.dsub.util.SilentBackgroundTask;
-public class UpdateView extends LinearLayout {
+public abstract class UpdateView<T> extends LinearLayout {
private static final String TAG = UpdateView.class.getSimpleName();
private static final WeakHashMap<UpdateView, ?> INSTANCES = new WeakHashMap<UpdateView, Object>();
- private static Handler backgroundHandler;
- private static Handler uiHandler;
+ protected static Handler backgroundHandler;
+ protected static Handler uiHandler;
private static Runnable updateRunnable;
private static int activeActivities = 0;
protected Context context;
+ protected T item;
protected RatingBar ratingBar;
protected ImageButton starButton;
protected ImageView moreButton;
@@ -63,8 +65,10 @@ public class UpdateView extends LinearLayout {
protected int isRated = 0;
protected int rating = 0;
protected SilentBackgroundTask<Void> imageTask = null;
+ protected Drawable startBackgroundDrawable;
protected final boolean autoUpdate;
+ protected boolean checkable;
public UpdateView(Context context) {
this(context, true);
@@ -75,8 +79,8 @@ public class UpdateView extends LinearLayout {
this.autoUpdate = autoUpdate;
setLayoutParams(new AbsListView.LayoutParams(
- ViewGroup.LayoutParams.FILL_PARENT,
- ViewGroup.LayoutParams.WRAP_CONTENT));
+ ViewGroup.LayoutParams.FILL_PARENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT));
if(autoUpdate) {
INSTANCES.put(this, null);
@@ -89,37 +93,16 @@ public class UpdateView extends LinearLayout {
}
- public void setObject(Object obj) {
+ public void setObject(T obj) {
+ item = obj;
setObjectImpl(obj);
updateBackground();
update();
}
- public void setObject(Object obj1, Object obj2) {
- if(imageTask != null) {
- imageTask.cancel();
- imageTask = null;
- }
-
- setObjectImpl(obj1, obj2);
- backgroundHandler.post(new Runnable() {
- @Override
- public void run() {
- updateBackground();
- uiHandler.post(new Runnable() {
- @Override
- public void run() {
- update();
- }
- });
- }
- });
- }
- protected void setObjectImpl(Object obj) {
-
- }
- protected void setObjectImpl(Object obj1, Object obj2) {
-
+ public void setObject(T obj1, Object obj2) {
+ setObject(obj1, null);
}
+ protected abstract void setObjectImpl(T obj);
private static synchronized void startUpdater() {
if(uiHandler != null) {
@@ -224,8 +207,6 @@ public class UpdateView extends LinearLayout {
MusicDirectory.Entry check = null;
if(view instanceof SongView) {
check = ((SongView) view).getEntry();
- } else if(view instanceof AlbumCell) {
- check = ((AlbumCell) view).getEntry();
} else if(view instanceof AlbumView) {
check = ((AlbumView) view).getEntry();
}
@@ -261,6 +242,9 @@ public class UpdateView extends LinearLayout {
if(starButton != null) {
if(isStarred) {
if(!starred) {
+ if(starButton.getDrawable() == null) {
+ starButton.setImageDrawable(DrawableTint.getTintedDrawable(context, R.drawable.ic_toggle_star));
+ }
starButton.setVisibility(View.VISIBLE);
starred = true;
}
@@ -283,4 +267,55 @@ public class UpdateView extends LinearLayout {
rating = isRated;
}
}
+
+ public boolean isCheckable() {
+ return checkable;
+ }
+ public void setChecked(boolean checked) {
+ View child = getChildAt(0);
+ if (checked && startBackgroundDrawable == null) {
+ startBackgroundDrawable = child.getBackground();
+ child.setBackgroundColor(DrawableTint.getColorRes(context, R.attr.colorPrimary));
+ } else if (!checked && startBackgroundDrawable != null) {
+ child.setBackgroundDrawable(startBackgroundDrawable);
+ startBackgroundDrawable = null;
+ }
+ }
+
+ public void onClick() {
+
+ }
+
+ public static class UpdateViewHolder<T> extends RecyclerView.ViewHolder {
+ private UpdateView updateView;
+ private View view;
+ private T item;
+
+ public UpdateViewHolder(UpdateView itemView) {
+ super(itemView);
+
+ this.updateView = itemView;
+ this.view = itemView;
+ }
+
+ // Different is so that call is not ambiguous
+ public UpdateViewHolder(View view, boolean different) {
+ super(view);
+ this.view = view;
+ }
+
+ public UpdateView<T> getUpdateView() {
+ return updateView;
+ }
+ public View getView() {
+ return view;
+ }
+ public void setItem(T item) {
+ this.item = item;
+ }
+ public T getItem() {
+ return item;
+ }
+ }
}
+
diff --git a/app/src/main/java/github/daneren2005/dsub/view/UpdateView2.java b/app/src/main/java/github/daneren2005/dsub/view/UpdateView2.java
new file mode 100644
index 00000000..0f0b5455
--- /dev/null
+++ b/app/src/main/java/github/daneren2005/dsub/view/UpdateView2.java
@@ -0,0 +1,47 @@
+package github.daneren2005.dsub.view;
+
+import android.content.Context;
+
+public abstract class UpdateView2<T1, T2> extends UpdateView<T1> {
+ protected T2 item2;
+
+ public UpdateView2(Context context) {
+ super(context);
+ }
+
+ public UpdateView2(Context context, boolean autoUpdate) {
+ super(context, autoUpdate);
+ }
+
+ public final void setObject(T1 obj1) {
+ setObject(obj1, null);
+ }
+ @Override
+ public void setObject(T1 obj1, Object obj2) {
+ item = obj1;
+ item2 = (T2) obj2;
+ if(imageTask != null) {
+ imageTask.cancel();
+ imageTask = null;
+ }
+
+ setObjectImpl(item, item2);
+ backgroundHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ updateBackground();
+ uiHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ update();
+ }
+ });
+ }
+ });
+ }
+
+ protected final void setObjectImpl(T1 obj1) {
+ setObjectImpl(obj1, null);
+ }
+ protected abstract void setObjectImpl(T1 obj1, T2 obj2);
+}
diff --git a/app/src/main/java/github/daneren2005/dsub/view/UserView.java b/app/src/main/java/github/daneren2005/dsub/view/UserView.java
index dec8dbef..a97d755b 100644
--- a/app/src/main/java/github/daneren2005/dsub/view/UserView.java
+++ b/app/src/main/java/github/daneren2005/dsub/view/UserView.java
@@ -25,15 +25,12 @@ 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;
-
+public class UserView extends UpdateView2<User, ImageLoader> {
private TextView usernameView;
private ImageView avatarView;
public UserView(Context context) {
super(context, false);
- this.context = context;
LayoutInflater.from(context).inflate(R.layout.user_list_item, this, true);
usernameView = (TextView) findViewById(R.id.item_name);
@@ -46,9 +43,8 @@ public class UserView extends UpdateView {
});
}
- protected void setObjectImpl(Object obj, Object obj2) {
- this.user = (User) obj;
+ protected void setObjectImpl(User user, ImageLoader imageLoader) {
usernameView.setText(user.getUsername());
- imageTask = ((ImageLoader)obj2).loadAvatar(context, avatarView, user.getUsername());
+ imageTask = imageLoader.loadAvatar(context, avatarView, user.getUsername());
}
}