diff options
Diffstat (limited to 'app')
147 files changed, 4257 insertions, 5041 deletions
diff --git a/app/app.iml b/app/app.iml index 28b7ee49..dce1b16c 100644 --- a/app/app.iml +++ b/app/app.iml @@ -69,11 +69,15 @@ <excludeFolder url="file://$MODULE_DIR$/build/intermediates/dependency-cache" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex-cache" /> - <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/appcompat-v7/22.1.1/jars" /> - <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/mediarouter-v7/22.1.1/jars" /> - <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-v4/22.1.1/jars" /> + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/appcompat-v7/22.2.0/jars" /> + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/design/22.2.0/jars" /> + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/mediarouter-v7/22.2.0/jars" /> + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/recyclerview-v7/22.2.0/jars" /> + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-v4/22.2.0/jars" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.google.android.gms/play-services-base/7.0.0/jars" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.google.android.gms/play-services-cast/7.0.0/jars" /> + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.sothree.slidinguppanel/library/3.0.0/jars" /> + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/de.hdodenhof/circleimageview/1.2.1/jars" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/jacoco" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/javaResources" /> @@ -91,31 +95,35 @@ </content> <orderEntry type="jdk" jdkName="Android API 22 Platform" jdkType="Android SDK" /> <orderEntry type="sourceFolder" forTests="false" /> - <orderEntry type="library" exported="" name="mediarouter-v7-22.1.1" level="project" /> + <orderEntry type="library" exported="" name="mediarouter-v7-22.2.0" level="project" /> <orderEntry type="library" exported="" name="seamless-util-1.1.0" level="project" /> <orderEntry type="library" exported="" name="cling-core-2.0.1" level="project" /> + <orderEntry type="library" exported="" name="recyclerview-v7-22.2.0" level="project" /> <orderEntry type="library" exported="" name="jetty-continuation-8.1.16.v20140903" level="project" /> - <orderEntry type="library" exported="" name="javax.servlet-3.0.0.v201112011016" level="project" /> - <orderEntry type="library" exported="" name="jetty-server-8.1.16.v20140903" level="project" /> - <orderEntry type="library" exported="" name="support-annotations-22.1.1" level="project" /> - <orderEntry type="library" exported="" name="support-v4-22.1.1" level="project" /> + <orderEntry type="library" exported="" name="support-annotations-22.2.0" level="project" /> <orderEntry type="library" exported="" name="play-services-cast-7.0.0" level="project" /> - <orderEntry type="library" exported="" name="cling-support-2.0.1" level="project" /> <orderEntry type="library" exported="" name="seamless-http-1.1.0" level="project" /> <orderEntry type="library" exported="" name="jetty-util-8.1.16.v20140903" level="project" /> - <orderEntry type="library" exported="" name="appcompat-v7-22.1.1" level="project" /> + <orderEntry type="library" exported="" name="appcompat-v7-22.2.0" level="project" /> <orderEntry type="library" exported="" name="seamless-swing-1.1.0" level="project" /> <orderEntry type="library" exported="" name="seamless-xml-1.1.0" level="project" /> + <orderEntry type="library" exported="" name="kryo-2.21-all" level="project" /> + <orderEntry type="library" exported="" name="play-services-base-7.0.0" level="project" /> + <orderEntry type="library" exported="" name="jetty-http-8.1.16.v20140903" level="project" /> + <orderEntry type="library" exported="" name="library-2.4.0" level="project" /> + <orderEntry type="library" exported="" name="javax.servlet-3.0.0.v201112011016" level="project" /> + <orderEntry type="library" exported="" name="jetty-server-8.1.16.v20140903" level="project" /> + <orderEntry type="library" exported="" name="support-v4-22.2.0" level="project" /> + <orderEntry type="library" exported="" name="cling-support-2.0.1" level="project" /> <orderEntry type="library" exported="" name="jetty-io-8.1.16.v20140903" level="project" /> <orderEntry type="library" exported="" name="CWAC-EndlessAdapter" level="project" /> + <orderEntry type="library" exported="" name="circleimageview-1.2.1" level="project" /> <orderEntry type="library" exported="" name="jetty-security-8.1.16.v20140903" level="project" /> - <orderEntry type="library" exported="" name="kryo-2.21-all" level="project" /> - <orderEntry type="library" exported="" name="play-services-base-7.0.0" level="project" /> <orderEntry type="library" exported="" name="jetty-servlet-8.1.16.v20140903" level="project" /> <orderEntry type="library" exported="" name="CWAC-AdapterWrapper" level="project" /> + <orderEntry type="library" exported="" name="library-3.0.0" level="project" /> + <orderEntry type="library" exported="" name="design-22.2.0" level="project" /> <orderEntry type="library" exported="" name="jetty-client-8.1.16.v20140903" level="project" /> - <orderEntry type="library" exported="" name="jetty-http-8.1.16.v20140903" level="project" /> - <orderEntry type="module" module-name="DragSort ListView" exported="" /> <orderEntry type="module" module-name="Server Proxy" exported="" /> </component> </module>
\ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 0eeaec1d..c09f0f91 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -6,7 +6,7 @@ android { defaultConfig { applicationId "github.daneren2005.dsub" - minSdkVersion 9 + minSdkVersion 14 targetSdkVersion 19 } buildTypes { @@ -32,13 +32,16 @@ android { } dependencies { - compile project(':DragSort ListView') compile project(':Server Proxy') compile fileTree(include: ['*.jar'], dir: 'libs') - compile 'com.android.support:support-v4:22.1.1' - compile 'com.android.support:appcompat-v7:22.1.1' - compile 'com.android.support:mediarouter-v7:22.1.1' + compile 'com.android.support:support-v4:22.2.+' + compile 'com.android.support:appcompat-v7:22.2.+' + compile 'com.android.support:mediarouter-v7:22.2.+' + compile 'com.android.support:recyclerview-v7:22.2.+' + compile 'com.android.support:design:22.2.+' compile 'com.google.android.gms:play-services-cast:7.0.0' + compile 'com.sothree.slidinguppanel:library:3.0.0' + compile 'de.hdodenhof:circleimageview:1.2.1' compile group: 'org.fourthline.cling', name: 'cling-core', version:'2.0.1' compile group: 'org.fourthline.cling', name: 'cling-support', version:'2.0.1' compile group: 'org.eclipse.jetty', name: 'jetty-server', version:'8.1.16.v20140903' diff --git a/app/src/androidTest/java/github/daneren2005/dsub/activity/DownloadActivityTest.java b/app/src/androidTest/java/github/daneren2005/dsub/activity/DownloadActivityTest.java deleted file mode 100644 index ce859181..00000000 --- a/app/src/androidTest/java/github/daneren2005/dsub/activity/DownloadActivityTest.java +++ /dev/null @@ -1,32 +0,0 @@ -package github.daneren2005.dsub.activity; - -import github.daneren2005.dsub.R; -import android.test.*; -import android.view.View; - -public class DownloadActivityTest extends - ActivityInstrumentationTestCase2<DownloadActivity> { - - private DownloadActivity activity; - - public DownloadActivityTest() { - super(DownloadActivity.class); - } - - @Override - protected void setUp() throws Exception { - super.setUp(); - activity = getActivity(); - } - - /** - * Test the main layout. - */ - public void testLayout() { - View view = activity.findViewById(R.layout.download_activity); - assertNotNull(view); - assertNotNull(view.findViewById(R.layout.download_activity)); - assertNotNull(activity.findViewById(R.id.fragment_container)); - } - -} diff --git a/app/src/androidTest/java/github/daneren2005/dsub/domain/BookmarkTest.java b/app/src/androidTest/java/github/daneren2005/dsub/domain/BookmarkTest.java index 814f658a..30663543 100644 --- a/app/src/androidTest/java/github/daneren2005/dsub/domain/BookmarkTest.java +++ b/app/src/androidTest/java/github/daneren2005/dsub/domain/BookmarkTest.java @@ -15,7 +15,7 @@ public class BookmarkTest extends TestCase { */ public void testSetCreated() throws ParseException { Bookmark bookmark = new Bookmark(); - bookmark.setCreated(null); + bookmark.setCreated((String) null); assertEquals(null, bookmark.getCreated()); bookmark.setCreated(""); diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 38d80ae8..2b3a5fbc 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -31,7 +31,7 @@ <uses-feature android:name="android.hardware.microphone" android:required="false" /> <uses-feature android:name="android.hardware.wifi" android:required="false" /> - <uses-sdk android:minSdkVersion="9" android:targetSdkVersion="19"/> + <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="19"/> <supports-screens android:anyDensity="true" android:xlargeScreens="true" android:largeScreens="true" android:normalScreens="true" android:smallScreens="true"/> 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..f7e7251c 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,18 @@ 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.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 +52,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; @@ -68,20 +68,20 @@ import github.daneren2005.dsub.service.DownloadService; import github.daneren2005.dsub.service.HeadphoneListenerService; import github.daneren2005.dsub.util.Constants; 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 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>(); @@ -95,11 +95,15 @@ public class SubsonicActivity extends ActionBarActivity implements OnItemSelecte ViewGroup rootView; DrawerLayout drawer; ActionBarDrawerToggle drawerToggle; - DrawerAdapter drawerAdapter; - ListView drawerList; - TextView lastSelectedView = null; + NavigationView drawerList; + View drawerHeader; + ImageView drawerUserAvatar; + TextView drawerServerName; + TextView drawerUserName; int lastSelectedPosition = 0; + boolean showingTabs = true; boolean drawerOpen = false; + SharedPreferences.OnSharedPreferenceChangeListener preferencesListener; @Override protected void onCreate(Bundle bundle) { @@ -118,26 +122,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 +177,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(); @@ -160,8 +198,8 @@ public class SubsonicActivity extends ActionBarActivity implements OnItemSelecte restart(); overridePendingTransition(R.anim.fade_in, R.anim.fade_out); } - - populateDrawer(); + + populateTabs(); UpdateView.addActiveActivity(); } @@ -176,6 +214,7 @@ public class SubsonicActivity extends ActionBarActivity implements OnItemSelecte protected void onDestroy() { super.onDestroy(); destroyed = true; + Util.getPreferences(this).unregisterOnSharedPreferenceChangeListener(preferencesListener); } @Override @@ -185,19 +224,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 +236,100 @@ 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(); } } }); + 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 +338,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 +387,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 +451,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 +464,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 +494,7 @@ public class SubsonicActivity extends ActionBarActivity implements OnItemSelecte return true; } - return currentFragment.onOptionsItemSelected(item); + return getCurrentFragment().onOptionsItemSelected(item); } @Override @@ -415,7 +510,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 +521,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,97 +534,68 @@ 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) { + startFragmentActivity(fragmentType); } public void startFragmentActivity(String fragmentType) { @@ -539,6 +605,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 +644,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 +701,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 +729,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 +755,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 +768,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 +803,7 @@ public class SubsonicActivity extends ActionBarActivity implements OnItemSelecte getSupportActionBar().setDisplayShowCustomEnabled(true); } } else if(!isTv()) { + getSupportActionBar().setTitle(currentFragment.getTitle()); getSupportActionBar().setDisplayShowCustomEnabled(false); } } @@ -735,6 +812,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 +823,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 +832,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,7 +864,7 @@ 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(); @@ -799,7 +877,7 @@ public class SubsonicActivity extends ActionBarActivity implements OnItemSelecte } return DownloadService.getInstance(); } - + public static String getThemeName() { return theme; } @@ -811,6 +889,38 @@ 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(); + } + } + private void updateDrawerHeader() { + drawerServerName.setText(Util.getServerName(this)); + drawerUserName.setText(UserUtil.getCurrentUsername(this)); + getImageLoader().loadAvatar(this, drawerUserAvatar, UserUtil.getCurrentUsername(this)); + } + 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..c7661080 100644 --- a/app/src/main/java/github/daneren2005/dsub/activity/SubsonicFragmentActivity.java +++ b/app/src/main/java/github/daneren2005/dsub/activity/SubsonicFragmentActivity.java @@ -30,15 +30,20 @@ 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.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; @@ -53,6 +58,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 +72,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; @@ -83,9 +88,16 @@ public class SubsonicFragmentActivity extends SubsonicActivity { private static boolean sessionInitialized = false; private static long ALLOWED_SKEW = 30000L; + private Handler handler = new Handler(); + private SlidingUpPanelLayout slideUpPanel; + private NowPlayingFragment nowPlayingFragment; + private Toolbar mainToolbar; + private Toolbar nowPlayingToolbar; + private ScheduledExecutorService executorService; + private View slideUpFrame; 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,151 @@ 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); + slideUpPanel.setPanelSlideListener(new SlidingUpPanelLayout.PanelSlideListener() { + @Override + public void onPanelSlide(View panel, float slideOffset) { + + } + + @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) { + // 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) { + } }); - coverArtView = bottomBar.findViewById(R.id.album_art); + + 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); + } + + slideUpFrame = findViewById(R.id.slide_up_frame); + 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 @@ -262,7 +368,7 @@ public class SubsonicFragmentActivity extends SubsonicActivity { } } } - + @Override public void onNewIntent(Intent intent) { super.onNewIntent(intent); @@ -276,7 +382,6 @@ public class SubsonicFragmentActivity extends SubsonicActivity { if (query != null) { ((SearchFragment)currentFragment).search(query, autoplay); } else { - ((SearchFragment)currentFragment).populateList(); if (requestsearch) { onSearchRequested(); } @@ -300,7 +405,6 @@ public class SubsonicFragmentActivity extends SubsonicActivity { public void onResume() { super.onResume(); - final Handler handler = new Handler(); Runnable runnable = new Runnable() { @Override public void run() { @@ -329,9 +433,6 @@ 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(); @@ -347,8 +448,17 @@ public class SubsonicFragmentActivity extends SubsonicActivity { } @Override + public void onSaveInstanceState(Bundle savedInstanceState) { + super.onSaveInstanceState(savedInstanceState); + savedInstanceState.putString(Constants.MAIN_NOW_PLAYING, nowPlayingFragment.getTag()); + } + @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); } @@ -369,7 +479,9 @@ public class SubsonicFragmentActivity extends SubsonicActivity { @Override public void onBackPressed() { - if(onBackPressedSupport()) { + if(slideUpPanel.getPanelState() == SlidingUpPanelLayout.PanelState.EXPANDED) { + 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,6 +492,15 @@ public class SubsonicFragmentActivity extends SubsonicActivity { } @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) { @@ -393,24 +514,33 @@ public class SubsonicFragmentActivity extends SubsonicActivity { drawerToggle.setDrawerIndicatorEnabled(true); } } - + + @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 +556,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,7 +586,7 @@ public class SubsonicFragmentActivity extends SubsonicActivity { } else { return new MainFragment(); } - } + } private void update() { DownloadService downloadService = getDownloadService(); @@ -465,20 +604,33 @@ public class SubsonicFragmentActivity extends SubsonicActivity { } 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"); - } + if(current != null || slideUpFrame.getVisibility() != View.GONE) { + slideUpFrame.setVisibility(View.VISIBLE); + if (current != null) { + song = current.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); + } - 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(); + int[] attrs = new int[]{(state == PlayerState.STARTED) ? R.attr.actionbar_pause : R.attr.actionbar_start}; + TypedArray typedArray = this.obtainStyledAttributes(attrs); + startButton.setImageResource(typedArray.getResourceId(0, 0)); + typedArray.recycle(); + } } public void checkUpdates() { @@ -502,7 +654,7 @@ public class SubsonicFragmentActivity extends SubsonicActivity { if(ServerInfo.canSavePlayQueue(this) && !Util.isOffline(this)) { loadRemotePlayQueue(); } - + sessionInitialized = true; } private void loadSettings() { @@ -551,7 +703,7 @@ public class SubsonicFragmentActivity extends SubsonicActivity { return true; } } - + private void loadBookmarks() { final Context context = this; new SilentBackgroundTask<Void>(context) { @@ -562,7 +714,7 @@ public class SubsonicFragmentActivity extends SubsonicActivity { return null; } - + @Override public void error(Throwable error) { Log.e(TAG, "Failed to get bookmarks", error); 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/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..8335966d 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,7 @@ 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 android.widget.TextView; import github.daneren2005.dsub.R; @@ -32,33 +26,36 @@ 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; } - - 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, false); + // 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; } } 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..48b278ec --- /dev/null +++ b/app/src/main/java/github/daneren2005/dsub/adapter/EntryGridAdapter.java @@ -0,0 +1,133 @@ +/* + 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.View; +import android.view.ViewGroup; + +import java.util.ArrayList; +import java.util.List; + +import github.daneren2005.dsub.domain.MusicDirectory; +import github.daneren2005.dsub.domain.MusicDirectory.Entry; +import github.daneren2005.dsub.util.ImageLoader; +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 checkable = true; + 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; + } + } + } + + @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()); + } + } + + @Override + public void setChecked(UpdateView updateView, boolean checked) { + if(updateView instanceof SongView) { + ((SongView) updateView).setChecked(checked); + } + } + + 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 setCheckable(boolean checkable) { + this.checkable = checkable; + } + + public void removeAt(int index) { + sections.get(0).remove(index); + notifyItemRemoved(index); + } +} 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..7b7dc6fc --- /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, 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); + } + 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..fa00c1dd 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,54 @@ /* - 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.ArrayList; +import java.util.Arrays; 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.view.PlaylistView; +import github.daneren2005.dsub.view.UpdateView; -import java.util.Collections; -import java.util.Comparator; - -/** - * @author Sindre Mehus - */ -public class PlaylistAdapter extends ArrayAdapter<Playlist> { +public class PlaylistAdapter extends SectionAdapter<Playlist> { + public static int VIEW_TYPE_PLAYLIST = 1; - 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, OnItemClickedListener listener) { + super(context, playlists); + this.onItemClickedListener = listener; + } + public PlaylistAdapter(Context context, List<String> headers, List<List<Playlist>> sections, OnItemClickedListener listener) { + super(context, headers, sections); + 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)); } - 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..c1185702 --- /dev/null +++ b/app/src/main/java/github/daneren2005/dsub/adapter/SearchAdapter.java @@ -0,0 +1,114 @@ +/* + 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.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.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; + } + + @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, false); + } 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; + } + } +} 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..f57012b5 --- /dev/null +++ b/app/src/main/java/github/daneren2005/dsub/adapter/SectionAdapter.java @@ -0,0 +1,328 @@ +/* + 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.support.v7.widget.RecyclerView; +import android.util.Log; +import android.view.View; +import android.view.ViewGroup; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import github.daneren2005.dsub.R; +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 UpdateView contextView; + protected T contextItem; + private List<T> selected = new ArrayList<>(); + + 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; + } + + @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 (updateView.isCheckable()) { + if (selected.contains(item)) { + selected.remove(item); + setChecked(updateView, false); + } else { + selected.add(item); + setChecked(updateView, true); + } + } 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) { + T item = holder.getItem(); + setContextItem(updateView, item); + v.showContextMenu(); + } + }); + + /*updateView.getChildAt(0).setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + T item = holder.getItem(); + setContextItem(updateView, item); + v.showContextMenu(); + return false; + } + });*/ + } + } + + 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)); + if(updateView.isCheckable()) { + setChecked(updateView, selected.contains(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)); + + if(updateView.isCheckable()) { + setChecked(updateView, selected.contains(item)); + } + holder.setItem(item); + return; + } + + subPosition += section.size(); + if(validHeader) { + subPosition += 1; + } + subHeader++; + } + } + + @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 setContextItem(UpdateView updateView, T item) { + contextView = updateView; + contextItem = item; + } + public UpdateView getContextView() { + return contextView; + } + public T getContextItem() { + return contextItem; + } + + 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++; + } + + this.notifyItemChanged(index); + } + selected.clear(); + } + + 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 setChecked(UpdateView updateView, boolean checked) {} + + public interface OnItemClickedListener<T> { + void onItemClicked(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..f3f9eb64 100644 --- a/app/src/main/java/github/daneren2005/dsub/fragments/AdminFragment.java +++ b/app/src/main/java/github/daneren2005/dsub/fragments/AdminFragment.java @@ -28,6 +28,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; @@ -37,7 +38,7 @@ import github.daneren2005.dsub.util.UserUtil; import github.daneren2005.dsub.util.Util; import github.daneren2005.dsub.adapter.UserAdapter; -public class AdminFragment extends SelectListFragment<User> { +public class AdminFragment extends SelectRecyclerFragment<User> { private static String TAG = AdminFragment.class.getSimpleName(); @Override @@ -69,8 +70,7 @@ public class AdminFragment extends SelectListFragment<User> { @Override public boolean onContextItemSelected(MenuItem menuItem) { - AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo(); - User user = objects.get(info.position); + User user = adapter.getContextItem(); switch(menuItem.getItemId()) { case R.id.admin_change_email: @@ -97,8 +97,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 +134,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..5a3d63ad 100644 --- a/app/src/main/java/github/daneren2005/dsub/fragments/DownloadFragment.java +++ b/app/src/main/java/github/daneren2005/dsub/fragments/DownloadFragment.java @@ -30,6 +30,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 +39,15 @@ 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.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 @@ -80,7 +83,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 +94,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,7 +106,7 @@ public class DownloadFragment extends SelectListFragment<DownloadFile> { } @Override - public void onItemClick(AdapterView<?> parent, View view, int position, long id) { + public void onItemClicked(DownloadFile item) { } @@ -144,11 +144,13 @@ public class DownloadFragment extends SelectListFragment<DownloadFile> { @Override public void onCreateContextMenu(android.view.ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) { super.onCreateContextMenu(menu, view, menuInfo); + UpdateView targetView = adapter.getContextView(); + menuInfo = new AdapterView.AdapterContextMenuInfo(targetView, 0, 0); - AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo; - Object selectedItem = ((DownloadFile) listView.getItemAtPosition(info.position)).getSong(); + DownloadFile downloadFile = adapter.getContextItem(); + MusicDirectory.Entry selectedItem = downloadFile.getSong(); onCreateContextMenu(menu, view, menuInfo, selectedItem); - if(selectedItem instanceof MusicDirectory.Entry && !((MusicDirectory.Entry) selectedItem).isVideo() && !Util.isOffline(context)) { + if(!selectedItem.isVideo() && !Util.isOffline(context)) { menu.removeItem(R.id.song_menu_remove_playlist); } @@ -161,9 +163,8 @@ public class DownloadFragment extends SelectListFragment<DownloadFile> { return false; } - AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo(); - Object selectedItem = ((DownloadFile) listView.getItemAtPosition(info.position)).getSong(); - + DownloadFile downloadFile = adapter.getContextItem(); + MusicDirectory.Entry selectedItem = downloadFile.getSong(); if(onContextItemSelected(menuItem, selectedItem)) { return true; } 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..96e1d326 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,6 @@ package github.daneren2005.dsub.fragments; -import android.app.AlertDialog; +import android.support.v7.app.AlertDialog; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; @@ -9,28 +9,23 @@ 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; @@ -41,37 +36,13 @@ 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; } @Override @@ -116,159 +87,56 @@ 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; - } - - @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); - } + public SectionAdapter getAdapter(List objs) { + List<List<Integer>> sections = new ArrayList<>(); + List<String> headers = new ArrayList<>(); - int instance = Util.getActiveServer(context); - String name = Util.getServerName(context, instance); - serverTextView.setText(name); + List<Integer> offline = Arrays.asList(R.string.main_offline); + sections.add(offline); + headers.add(null); - 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> 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"); } } - 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() { @@ -306,8 +174,8 @@ public class MainFragment extends SubsonicFragment { 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); + // TODO: Clear immediately so doesn't still show when pressing back + // setMostRecentCount(0); } SubsonicFragment fragment = new SelectDirectoryFragment(); @@ -506,81 +374,31 @@ 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) { + 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(); } } } 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..0f9d9f68 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; @@ -69,6 +74,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,10 +84,9 @@ 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> { 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; @@ -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; @@ -141,6 +146,7 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis startFlipped = true; } } + primaryFragment = false; } @Override @@ -153,11 +159,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 +176,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 +187,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() { @@ -397,7 +438,7 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis } 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); @@ -500,56 +541,8 @@ 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); slider.setPadding(0, 0, 0, 0); @@ -604,7 +597,7 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis if(menuItemSelected(menuItem.getItemId(), null)) { return true; } - + return super.onOptionsItemSelected(menuItem); } @@ -614,10 +607,11 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis if(!primaryFragment) { return; } + UpdateView targetView = songListAdapter.getContextView(); + menuInfo = new AdapterView.AdapterContextMenuInfo(targetView, 0, 0); if (view == playlistView) { - AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo; - DownloadFile downloadFile = (DownloadFile) playlistView.getItemAtPosition(info.position); + DownloadFile downloadFile = songListAdapter.getContextItem(); android.view.MenuInflater inflater = context.getMenuInflater(); if(Util.isOffline(context)) { @@ -641,9 +635,8 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis if(!primaryFragment) { return false; } - - AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo(); - DownloadFile downloadFile = (DownloadFile) playlistView.getItemAtPosition(info.position); + + DownloadFile downloadFile = songListAdapter.getContextItem(); return menuItemSelected(menuItem.getItemId(), downloadFile) || super.onContextItemSelected(menuItem); } @@ -745,7 +738,7 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis @Override protected void done(Void result) { - onDownloadListChanged(); + context.closeNowPlaying(); } }.execute(); } @@ -841,7 +834,13 @@ 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 @@ -898,25 +897,46 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis @Override public void onPause() { super.onPause(); - executorService.shutdown(); - if(getDownloadService() != null) { - getDownloadService().stopRemoteScan(); + onPauseHandlers(); + } + private void onPauseHandlers() { + if(executorService != null) { + executorService.shutdown(); + if (getDownloadService() != null) { + getDownloadService().stopRemoteScan(); + } + executorService = null; + 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 +974,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,24 +991,25 @@ 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); } } @@ -1043,26 +1064,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 +1108,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(); } } @@ -1125,7 +1146,7 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis protected Void doInBackground() throws Throwable { currentPlayingIndex = downloadService.getCurrentPlayingIndex() + 1; size = downloadService.size(); - + return null; } @@ -1133,24 +1154,24 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis 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 = new ArrayList<>(); songList.addAll(list); - playlistView.setAdapter(songListAdapter = new DownloadFileAdapter(context, songList)); + playlistView.setAdapter(songListAdapter = new DownloadFileAdapter(context, songList, NowPlayingFragment.this)); } else { songList.clear(); songList.addAll(list); songListAdapter.notifyDataSetChanged(); } - + emptyTextView.setVisibility(list.isEmpty() ? View.VISIBLE : View.GONE); currentRevision = downloadService.getDownloadListUpdateRevision(); @@ -1171,7 +1192,7 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis default: break; } - + if(scrollWhenLoaded) { scrollToCurrent(); scrollWhenLoaded = false; @@ -1194,7 +1215,7 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis if (downloadService == null || onCurrentChangedTask != null) { return; } - + onCurrentChangedTask = new SilentBackgroundTask<Void>(context) { int currentPlayingIndex; int currentPlayingSize; @@ -1255,7 +1276,7 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis } } }; - + if(onDownloadListChangedTask == null) { onCurrentChangedTask.execute(); } @@ -1395,33 +1416,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 +1451,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); - + new SilentBackgroundTask<Void>(context) { @Override protected Void doInBackground() throws Throwable { @@ -1458,12 +1479,12 @@ 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; @@ -1474,14 +1495,14 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis } 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(); @@ -1570,4 +1591,19 @@ 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); + + onCurrentChanged(); + onProgressChanged(); + return null; + } + }.execute(); + } } 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..20a87e7d 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,14 @@ package github.daneren2005.dsub.fragments; -import java.util.ArrayList; -import java.util.List; +import java.io.Serializable; import java.util.Arrays; 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 +16,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 +30,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 +58,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 +69,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); } @@ -156,11 +125,12 @@ public class SearchFragment extends SubsonicFragment { @Override public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) { super.onCreateContextMenu(menu, view, menuInfo); + UpdateView targetView = adapter.getContextView(); + menuInfo = new AdapterView.AdapterContextMenuInfo(targetView, 0, 0); - 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)) { + Serializable item = adapter.getContextItem(); + onCreateContextMenu(menu, view, menuInfo, item); + if(item instanceof MusicDirectory.Entry && !((MusicDirectory.Entry) item).isVideo() && !Util.isOffline(context)) { menu.removeItem(R.id.song_menu_remove_playlist); } @@ -172,27 +142,37 @@ public class SearchFragment extends SubsonicFragment { if(menuItem.getGroupId() != getSupportTag()) { return false; } - - AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo(); - Object selectedItem = list.getItemAtPosition(info.position); - - if(onContextItemSelected(menuItem, selectedItem)) { + + Serializable item = adapter.getContextItem(); + if(onContextItemSelected(menuItem, item)) { return true; } return true; } - - @Override - public void setPrimaryFragment(boolean primary) { - super.setPrimaryFragment(primary); - } @Override public void refresh(boolean refresh) { context.onNewIntent(context.getIntent()); } + @Override + 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); + } + } + } + public void search(final String query, final boolean autoplay) { if(skipSearch) { skipSearch = false; @@ -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..8a2b7153 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,19 +73,7 @@ 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; } @@ -95,30 +81,12 @@ public class SelectArtistFragment extends SelectListFragment<Artist> { @Override public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) { super.onCreateContextMenu(menu, view, menuInfo); + UpdateView targetView = adapter.getContextView(); + menuInfo = new AdapterView.AdapterContextMenuInfo(targetView, 0, 0); - 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); - } + Artist artist = adapter.getContextItem(); + onCreateContextMenu(menu, view, menuInfo, artist); recreateContextMenu(menu); } @@ -129,56 +97,33 @@ public class SelectArtistFragment extends SelectListFragment<Artist> { } 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(); - } + Artist artist = adapter.getContextItem(); - return true; + return onContextItemSelected(menuItem, artist); } @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 +160,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 +180,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 +195,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 +214,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 +231,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..a774a287 100644 --- a/app/src/main/java/github/daneren2005/dsub/fragments/SelectBookmarkFragment.java +++ b/app/src/main/java/github/daneren2005/dsub/fragments/SelectBookmarkFragment.java @@ -25,7 +25,7 @@ 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; @@ -34,16 +34,19 @@ 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.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); + UpdateView targetView = adapter.getContextView(); + menuInfo = new AdapterView.AdapterContextMenuInfo(targetView, 0, 0); MenuInflater inflater = context.getMenuInflater(); inflater.inflate(R.menu.select_bookmark_context, menu); @@ -53,8 +56,7 @@ public class SelectBookmarkFragment extends SelectListFragment<MusicDirectory.En @Override public boolean onContextItemSelected(MenuItem menuItem) { - AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo(); - MusicDirectory.Entry bookmark = objects.get(info.position); + MusicDirectory.Entry bookmark = adapter.getContextItem(); switch(menuItem.getItemId()) { case R.id.bookmark_menu_info: @@ -78,8 +80,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 +95,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,7 +111,7 @@ public class SelectBookmarkFragment extends SelectListFragment<MusicDirectory.En @Override protected void done(Void result) { - Util.startActivityWithoutTransition(context, DownloadActivity.class); + context.openNowPlaying(); } }.execute(); } 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..08a051cf 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,6 +9,9 @@ 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; @@ -23,30 +26,26 @@ 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.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 +58,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 +69,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 +110,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 +167,37 @@ 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(); + + registerForContextMenu(recyclerView); if(entries == null) { if(primaryFragment || secondaryFragment) { @@ -287,21 +291,22 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter return true; case R.id.menu_download: downloadBackground(false); - selectAll(false, false); + entryGridAdapter.clearSelected(); return true; case R.id.menu_cache: downloadBackground(true); - selectAll(false, false); + entryGridAdapter.clearSelected(); return true; case R.id.menu_delete: delete(); - selectAll(false, false); + entryGridAdapter.clearSelected(); return true; case R.id.menu_add_playlist: - if(getSelectedSongs().isEmpty()) { - selectAll(true, false); + List<Entry> songs = getSelectedSongs(); + if(songs.isEmpty()) { + songs = entries; } - addToPlaylist(getSelectedSongs()); + addToPlaylist(songs); return true; case R.id.menu_remove_playlist: removeFromPlaylist(playlistId, playlistName, getSelectedIndexes()); @@ -333,23 +338,9 @@ 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; - } + Entry entry = entryGridAdapter.getContextItem(); + UpdateView targetView = entryGridAdapter.getContextView(); + menuInfo = new AdapterView.AdapterContextMenuInfo(targetView, 0, 0); // Don't try to display a context menu if error here if(entry == null) { @@ -384,22 +375,12 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter 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); - } + Entry entry = entryGridAdapter.getContextItem(); 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); + songs.add(entry); + Iterator it = entries.listIterator(entries.indexOf(entry)); while(it.hasNext()) { songs.add((Entry) it.next()); } @@ -408,19 +389,19 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter return true; } - if(onContextItemSelected(menuItem, selectedItem)) { + 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; } @@ -428,38 +409,35 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter } @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); - - 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)); + 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; } + + playNow(Arrays.asList(entry)); } } @@ -478,8 +456,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) { @@ -686,11 +664,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) { @@ -728,120 +702,120 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter } 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); + } 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,122 +823,51 @@ 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); + List<Entry> songs = getSelectedSongs(); + 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; + return entryGridAdapter.getSelected(); } 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) { + private void download(final List<Entry> songs, 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 @@ -993,7 +896,7 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter @Override protected void done(Void result) { if (autoplay) { - Util.startActivityWithoutTransition(context, DownloadActivity.class); + context.openNowPlaying(); } else if (save) { Util.toast(context, context.getResources().getQuantityString(R.plurals.select_album_n_songs_downloading, songs.size(), songs.size())); @@ -1007,11 +910,11 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter checkLicenseAndTrialPeriod(onValid); } private void downloadBackground(final boolean save) { + List<Entry> songs = getSelectedSongs(); 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); @@ -1044,12 +947,12 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter private void delete() { List<Entry> songs = getSelectedSongs(); if(songs.isEmpty()) { - selectAll(true, false); - songs = getSelectedSongs(); - - // Also delete all directories - for(Entry album: albums) { - deleteRecursively(album); + for(Entry entry: entries) { + if(entry.isDirectory()) { + deleteRecursively(entry); + } else { + songs.add(entry); + } } } if (getDownloadService() != null) { @@ -1068,11 +971,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)); } @@ -1157,8 +1058,7 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter @Override protected void done(Void result) { - entries.remove(episode); - entryAdapter.notifyDataSetChanged(); + entryGridAdapter.removeItem(episode); } @Override @@ -1219,10 +1119,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 +1233,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 +1394,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()); 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..fe012f62 100644 --- a/app/src/main/java/github/daneren2005/dsub/fragments/SelectGenreFragment.java +++ b/app/src/main/java/github/daneren2005/dsub/fragments/SelectGenreFragment.java @@ -1,21 +1,18 @@ /* - 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; @@ -23,6 +20,7 @@ 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; @@ -31,7 +29,7 @@ import github.daneren2005.dsub.adapter.GenreAdapter; 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 +38,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 +53,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"); 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..49589971 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,20 @@ 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.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; @@ -31,9 +32,11 @@ import github.daneren2005.dsub.util.UserUtil; import github.daneren2005.dsub.util.Util; import github.daneren2005.dsub.adapter.PlaylistAdapter; +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 @@ -47,8 +50,7 @@ public class SelectPlaylistFragment extends SelectListFragment<Playlist> { else { inflater.inflate(R.menu.select_playlist_context, menu); - AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo; - Playlist playlist = (Playlist) listView.getItemAtPosition(info.position); + Playlist playlist = adapter.getContextItem(); if(SyncUtil.isSyncedPlaylist(context, playlist.getId())) { menu.removeItem(R.id.playlist_menu_sync); } else { @@ -71,9 +73,8 @@ public class SelectPlaylistFragment extends SelectListFragment<Playlist> { if(menuItem.getGroupId() != getSupportTag()) { return false; } - - AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo(); - Playlist playlist = (Playlist) listView.getItemAtPosition(info.position); + + Playlist playlist = adapter.getContextItem(); SubsonicFragment fragment; Bundle args; @@ -130,8 +131,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, 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, this); + } } @Override @@ -149,9 +173,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 +201,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())); } 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..520d0414 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,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 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.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; @@ -46,11 +41,7 @@ import github.daneren2005.dsub.adapter.PodcastChannelAdapter; 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 @@ -79,8 +70,7 @@ public class SelectPodcastsFragment extends SelectListFragment<PodcastChannel> { if(!Util.isOffline(context) && UserUtil.canPodcast()) { inflater.inflate(R.menu.select_podcasts_context, menu); - AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo; - PodcastChannel podcast = (PodcastChannel) listView.getItemAtPosition(info.position); + PodcastChannel podcast = adapter.getContextItem(); if(SyncUtil.isSyncedPodcast(context, podcast.getId())) { menu.removeItem(R.id.podcast_menu_sync); } else { @@ -98,10 +88,8 @@ public class SelectPodcastsFragment extends SelectListFragment<PodcastChannel> { if(menuItem.getGroupId() != getSupportTag()) { return false; } - - AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo(); - PodcastChannel channel = (PodcastChannel) listView.getItemAtPosition(info.position); + PodcastChannel channel = adapter.getContextItem(); switch (menuItem.getItemId()) { case R.id.podcast_menu_sync: syncPodcast(channel); @@ -126,8 +114,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 +129,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())) { @@ -258,8 +244,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/SelectListFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SelectRecyclerFragment.java index 6f73f6e8..526a4312 100644 --- a/app/src/main/java/github/daneren2005/dsub/fragments/SelectListFragment.java +++ b/app/src/main/java/github/daneren2005/dsub/fragments/SelectRecyclerFragment.java @@ -1,25 +1,23 @@ /* - 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.support.v4.widget.SwipeRefreshLayout; +import android.support.v7.widget.RecyclerView; import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; @@ -27,15 +25,13 @@ 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.adapter.SectionAdapter; import github.daneren2005.dsub.service.MusicService; import github.daneren2005.dsub.service.MusicServiceFactory; import github.daneren2005.dsub.util.BackgroundTask; @@ -43,13 +39,16 @@ 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; +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 BackgroundTask<List<T>> currentTask; protected List<T> objects; protected boolean serialize = true; + protected boolean largeAlbums = false; + protected int columns; + protected boolean pullToRefresh = true; @Override public void onCreate(Bundle bundle) { @@ -58,6 +57,7 @@ public abstract class SelectListFragment<T> extends SubsonicFragment implements if(bundle != null && serialize) { objects = (List<T>) bundle.getSerializable(Constants.FRAGMENT_LIST); } + columns = context.getResources().getInteger(R.integer.Grid_Columns); } @Override @@ -70,21 +70,26 @@ public abstract class SelectListFragment<T> extends SubsonicFragment implements @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); 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); + recyclerView = (RecyclerView) rootView.findViewById(R.id.fragment_recycler); + setupLayoutManager(); + + if(pullToRefresh) { + setupScrollList(recyclerView); + } else { + refreshLayout.setEnabled(false); + } if(objects == null) { refresh(false); } else { - listView.setAdapter(adapter = getAdapter(objects)); + recyclerView.setAdapter(adapter = getAdapter(objects)); } + registerForContextMenu(recyclerView); return rootView; } @@ -109,7 +114,7 @@ public abstract class SelectListFragment<T> extends SubsonicFragment implements if(titleRes != 0) { setTitle(getTitleResource()); } - listView.setVisibility(View.GONE); + recyclerView.setVisibility(View.GONE); // Cancel current running task before starting another one if(currentTask != null) { @@ -135,13 +140,10 @@ public abstract class SelectListFragment<T> extends SubsonicFragment implements @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); + recyclerView.setAdapter(adapter = getAdapter(result)); onFinishRefresh(); - listView.setVisibility(View.VISIBLE); + recyclerView.setVisibility(View.VISIBLE); } else { setEmpty(true); } @@ -152,8 +154,12 @@ public abstract class SelectListFragment<T> extends SubsonicFragment implements currentTask.execute(); } + private void setupLayoutManager() { + setupLayoutManager(recyclerView, largeAlbums); + } + public abstract int getOptionsMenu(); - public abstract ArrayAdapter getAdapter(List<T> objs); + 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(); 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..3d79b7b9 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,26 @@ +/* + 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.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 +30,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; @@ -28,10 +42,7 @@ import github.daneren2005.dsub.util.ProgressListener; import github.daneren2005.dsub.util.Util; import github.daneren2005.dsub.adapter.ShareAdapter; -/** - * 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 @@ -48,9 +59,7 @@ public class SelectShareFragment extends SelectListFragment<Share> { return false; } - AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo(); - Share share = (Share) listView.getItemAtPosition(info.position); - + Share share = adapter.getContextItem(); switch (menuItem.getItemId()) { case R.id.share_menu_share: shareExternal(share); @@ -75,8 +84,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 +99,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 +200,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..bf25db77 100644 --- a/app/src/main/java/github/daneren2005/dsub/fragments/SelectVideoFragment.java +++ b/app/src/main/java/github/daneren2005/dsub/fragments/SelectVideoFragment.java @@ -19,25 +19,28 @@ import android.view.ContextMenu; 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,18 +55,17 @@ 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); + UpdateView targetView = adapter.getContextView(); + menuInfo = new AdapterView.AdapterContextMenuInfo(targetView, 0, 0); - AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo; - Object entry = listView.getItemAtPosition(info.position); - + MusicDirectory.Entry entry = adapter.getContextItem(); onCreateContextMenu(menu, view, menuInfo, entry); recreateContextMenu(menu); } @@ -74,9 +76,7 @@ public class SelectVideoFragment extends SelectListFragment<MusicDirectory.Entry return false; } - AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo(); - Object entry = listView.getItemAtPosition(info.position); - + MusicDirectory.Entry entry = adapter.getContextItem(); 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..a89bc280 100644 --- a/app/src/main/java/github/daneren2005/dsub/fragments/SelectYearFragment.java +++ b/app/src/main/java/github/daneren2005/dsub/fragments/SelectYearFragment.java @@ -1,21 +1,18 @@ /* - 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; @@ -27,14 +24,13 @@ 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; -/** - * Created by Scott on 12/23/13. - */ -public class SelectYearFragment extends SelectListFragment<Integer> { +public class SelectYearFragment extends SelectRecyclerFragment<String> { @Override public int getOptionsMenu() { @@ -42,15 +38,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,15 +58,13 @@ 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); 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..088b6d00 100644 --- a/app/src/main/java/github/daneren2005/dsub/fragments/SimilarArtistFragment.java +++ b/app/src/main/java/github/daneren2005/dsub/fragments/SimilarArtistFragment.java @@ -17,12 +17,15 @@ 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 +35,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 +55,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: @@ -71,10 +86,11 @@ public class SimilarArtistFragment extends SelectListFragment<Artist> { @Override public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) { super.onCreateContextMenu(menu, view, menuInfo); + UpdateView targetView = adapter.getContextView(); + menuInfo = new AdapterView.AdapterContextMenuInfo(targetView, 0, 0); - AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo; - Object entry = listView.getItemAtPosition(info.position); - onCreateContextMenu(menu, view, menuInfo, entry); + Artist artist = adapter.getContextItem(); + onCreateContextMenu(menu, view, menuInfo, artist); recreateContextMenu(menu); } @@ -85,14 +101,12 @@ public class SimilarArtistFragment extends SelectListFragment<Artist> { return false; } - AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo(); - Artist artist = (Artist) listView.getItemAtPosition(info.position); + Artist artist = adapter.getContextItem(); 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 +123,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..a155e807 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,13 +29,19 @@ 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.internal.view.ContextThemeWrapper; +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.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; @@ -49,9 +56,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; @@ -75,10 +82,10 @@ 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 +108,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 +123,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++; @@ -187,7 +194,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR return false; } - + public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo, Object selected) { MenuInflater inflater = context.getMenuInflater(); @@ -199,7 +206,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR } else { inflater.inflate(R.menu.select_podcast_episode_context, menu); - + if(entry.getBookmark() == null) { menu.removeItem(R.id.bookmark_menu_delete); } @@ -223,7 +230,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR } else { inflater.inflate(R.menu.select_song_context, menu); - + if(entry.getBookmark() == null) { menu.removeItem(R.id.bookmark_menu_delete); } @@ -286,7 +293,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR if(info.targetView instanceof SongView) { SongView songView = (SongView) info.targetView; DownloadFile downloadFile = songView.getDownloadFile(); - + try { if(downloadFile != null) { if(downloadFile.isWorkDone()) { @@ -294,7 +301,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR 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 { @@ -307,14 +314,10 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR } } // Apply similar logic to album views - else if(info.targetView instanceof AlbumCell || info.targetView instanceof AlbumView - || info.targetView instanceof ArtistView || info.targetView instanceof ArtistEntryView) { + else if(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) { + if(info.targetView instanceof AlbumView) { folder = ((AlbumView) info.targetView).getFile(); id = R.id.album_menu_delete; } else if(info.targetView instanceof ArtistView) { @@ -324,7 +327,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR folder = ((ArtistEntryView) info.targetView).getFile(); id = R.id.artist_menu_delete; } - + try { if(folder != null && !folder.exists()) { menu.removeItem(id); @@ -475,7 +478,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR return true; } - + public void replaceFragment(SubsonicFragment fragment) { replaceFragment(fragment, true); } @@ -492,7 +495,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 +634,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 +715,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 +759,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 +799,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(); } @@ -906,7 +977,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR playNowOverride = true; return false; } - + if (!append && !background) { downloadService.clear(); } @@ -994,7 +1065,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 +1074,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR it.remove(); } } - + return playlists; } @@ -1016,7 +1087,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 +1104,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 +1146,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 +1158,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 +1186,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 +1255,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); @@ -1197,6 +1268,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR }.execute(); } + @TargetApi(Build.VERSION_CODES.GINGERBREAD_MR1) public void displaySongInfo(final Entry song) { Integer duration = null; Integer bitrate = null; @@ -1209,12 +1281,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 +1296,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); @@ -1286,7 +1358,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR Util.info(context, song.getTitle(), msg); } - + protected void playVideo(Entry entry) { if(entryExists(entry)) { playExternalPlayer(entry); @@ -1315,7 +1387,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,7 +1445,7 @@ 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)); @@ -1490,7 +1562,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; } @@ -1515,7 +1587,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 +1600,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,7 +1618,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR AlertDialog dialog = builder.create(); dialog.show(); } - + protected void playNow(List<Entry> entries) { playNow(entries, null, null); } @@ -1558,7 +1630,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR break; } } - + // If no bookmark found, just play from start if(bookmark == null) { playNow(entries, 0, playlistName, playlistId); @@ -1585,22 +1657,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 +1698,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 +1728,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 +1755,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 +1782,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 +1794,16 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR } }.execute(); } - + 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 +1816,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); @@ -1780,11 +1851,6 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR 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 +1864,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 +1882,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..25196d66 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,30 @@ 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; -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 +62,35 @@ 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) { - listView.addHeaderView(header); } } 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/DownloadService.java b/app/src/main/java/github/daneren2005/dsub/service/DownloadService.java index 9e57f5b0..bf68c450 100644 --- a/app/src/main/java/github/daneren2005/dsub/service/DownloadService.java +++ b/app/src/main/java/github/daneren2005/dsub/service/DownloadService.java @@ -886,6 +886,9 @@ 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); } @@ -1818,6 +1821,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(); 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/util/Constants.java b/app/src/main/java/github/daneren2005/dsub/util/Constants.java index 05481bf0..6252d0e4 100644 --- a/app/src/main/java/github/daneren2005/dsub/util/Constants.java +++ b/app/src/main/java/github/daneren2005/dsub/util/Constants.java @@ -172,6 +172,7 @@ 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 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/ImageLoader.java b/app/src/main/java/github/daneren2005/dsub/util/ImageLoader.java index 7ba2064b..a770fbb1 100644 --- a/app/src/main/java/github/daneren2005/dsub/util/ImageLoader.java +++ b/app/src/main/java/github/daneren2005/dsub/util/ImageLoader.java @@ -190,8 +190,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(); @@ -274,12 +277,18 @@ 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(); @@ -582,7 +591,6 @@ public class ImageLoader { } } catch (Throwable x) { Log.e(TAG, "Failed to download album art.", x); - cancelled.set(true); } return null; @@ -592,6 +600,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/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..f44bc672 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,7 @@ package github.daneren2005.dsub.util; import android.annotation.TargetApi; import android.app.Activity; -import android.app.AlertDialog; +import android.support.v7.app.AlertDialog; import android.content.ComponentName; import android.content.Context; import android.content.DialogInterface; @@ -48,6 +48,8 @@ import android.view.Gravity; 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.domain.MusicDirectory; import github.daneren2005.dsub.domain.PlayerState; import github.daneren2005.dsub.domain.RepeatMode; @@ -249,6 +251,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 +284,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)) { 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..a1b14f61 --- /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, 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..3104864a 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,74 @@ 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(); - } - }); + 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..cb7bd186 100644 --- a/app/src/main/java/github/daneren2005/dsub/view/PlaylistView.java +++ b/app/src/main/java/github/daneren2005/dsub/view/PlaylistView.java @@ -33,37 +33,27 @@ 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 TextView titleView; public PlaylistView(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); starButton = (ImageButton) findViewById(R.id.item_star); starButton.setFocusable(false); moreButton = (ImageView) findViewById(R.id.item_more); - moreButton.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - v.showContextMenu(); - } - }); } - protected void setObjectImpl(Object obj) { - this.playlist = (Playlist) obj; + protected void setObjectImpl(Playlist playlist) { titleView.setText(playlist.getName()); } @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..fadf100e 100644 --- a/app/src/main/java/github/daneren2005/dsub/view/SongView.java +++ b/app/src/main/java/github/daneren2005/dsub/view/SongView.java @@ -38,20 +38,18 @@ import java.io.File; * * @author Sindre Mehus */ -public class SongView extends UpdateView implements Checkable { - 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; +public class SongView extends UpdateView2<MusicDirectory.Entry, Boolean> implements Checkable { + private static final String TAG = SongView.class.getSimpleName(); + + private CheckedTextView checkedTextView; + 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,29 @@ 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); + 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); 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 +102,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 +114,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 +128,16 @@ 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); + checkedTextView.setVisibility(checkable ? View.VISIBLE : View.GONE); this.setBackgroundColor(0x00000000); ratingBar.setVisibility(View.GONE); @@ -155,28 +147,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,27 +176,27 @@ 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, checkedTextView.getVisibility() == View.VISIBLE); + } + if (downloadService == null || downloadFile == null) { + return; } - if (downloadService == null || downloadFile == null) { - return; - } - if(song.isStarred()) { + if(item.isStarred()) { if(!starred) { starButton.setVisibility(View.VISIBLE); starred = true; @@ -216,13 +208,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 +222,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,24 +230,24 @@ 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); } } @@ -295,24 +287,24 @@ public class SongView extends UpdateView implements Checkable { rating = isRated; } - } + } - @Override - public void setChecked(boolean b) { - checkedTextView.setChecked(b); - } + @Override + public void setChecked(boolean b) { + checkedTextView.setChecked(b); + } - @Override - public boolean isChecked() { - return checkedTextView.isChecked(); - } + @Override + public boolean isChecked() { + return checkedTextView.isChecked(); + } - @Override - public void toggle() { - checkedTextView.toggle(); - } + @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..8e1029df 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,9 @@ package github.daneren2005.dsub.view; import android.content.Context; import android.content.res.TypedArray; -import android.graphics.Color; 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 +37,20 @@ 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.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; @@ -65,6 +65,7 @@ public class UpdateView extends LinearLayout { protected SilentBackgroundTask<Void> imageTask = null; protected final boolean autoUpdate; + protected boolean checkable; public UpdateView(Context context) { this(context, true); @@ -75,8 +76,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 +90,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 +204,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(); } @@ -283,4 +261,45 @@ public class UpdateView extends LinearLayout { rating = isRated; } } + + public boolean isCheckable() { + return checkable; + } + + 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()); } } diff --git a/app/src/main/res/layout-large-land/abstract_fragment_container.xml b/app/src/main/res/layout-large-land/abstract_fragment_container.xml index 5e3b1561..3901710f 100644 --- a/app/src/main/res/layout-large-land/abstract_fragment_container.xml +++ b/app/src/main/res/layout-large-land/abstract_fragment_container.xml @@ -2,8 +2,8 @@ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="match_parent" - android:layout_height="0px" - android:layout_weight="1"> + android:layout_height="match_parent" + android:layout_marginTop="?attr/actionBarSize"> <FrameLayout android:id="@+id/fragment_container" diff --git a/app/src/main/res/layout/abstract_activity.xml b/app/src/main/res/layout/abstract_activity.xml index be65e437..56db1439 100644 --- a/app/src/main/res/layout/abstract_activity.xml +++ b/app/src/main/res/layout/abstract_activity.xml @@ -1,21 +1,22 @@ <?xml version="1.0" encoding="utf-8"?> <android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/drawer_layout" android:layout_width="match_parent" - android:layout_height="match_parent"> + android:layout_height="match_parent" + android:fitsSystemWindows="true"> + <!-- The main content view --> <FrameLayout android:id="@+id/content_frame" android:layout_width="match_parent" android:layout_height="match_parent"/> + <!-- The navigation drawer --> - <ListView android:id="@+id/left_drawer" - android:layout_width="240dp" - android:layout_height="match_parent" - android:layout_gravity="start" - android:choiceMode="singleChoice" - android:divider="@android:color/transparent" - android:dividerHeight="0dp" - android:background="?android:windowBackground"/> + <android.support.design.widget.NavigationView + android:id="@+id/left_drawer" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:layout_gravity="start"/> </android.support.v4.widget.DrawerLayout> diff --git a/app/src/main/res/layout/abstract_fragment_activity.xml b/app/src/main/res/layout/abstract_fragment_activity.xml index d9c99f2f..44ac35fd 100644 --- a/app/src/main/res/layout/abstract_fragment_activity.xml +++ b/app/src/main/res/layout/abstract_fragment_activity.xml @@ -1,84 +1,137 @@ -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" +<com.sothree.slidinguppanel.SlidingUpPanelLayout + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:sothree="http://schemas.android.com/apk/res-auto" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:id="@+id/slide_up_panel" android:layout_width="match_parent" android:layout_height="match_parent" - android:gravity="center_horizontal" - android:orientation="vertical" > + android:gravity="bottom" + sothree:umanoPanelHeight="?attr/actionBarSize" + sothree:umanoShadowHeight="4dp" + sothree:umanoDragView="@+id/slide_up_swipe_target"> - <include layout="@layout/abstract_fragment_container" /> + <FrameLayout + android:layout_width="match_parent" + android:layout_height="match_parent"> - <View - android:layout_width="fill_parent" - android:layout_height="1px" - android:background="@color/dividerColor"/> + <android.support.v7.widget.Toolbar + android:id="@+id/main_toolbar" + android:layout_height="?attr/actionBarSize" + android:layout_width="match_parent" + android:background="?attr/colorPrimary" + android:elevation="4dp" + android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" + app:popupTheme="?attr/actionbarPopupStyle"/> + + <include layout="@layout/abstract_fragment_container"/> + </FrameLayout> <LinearLayout - android:id="@+id/bottom_bar" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - style="@style/BasicButton" - android:orientation="horizontal"> - - <github.daneren2005.dsub.view.RecyclingImageView - android:id="@+id/album_art" - android:layout_width="50dip" - android:layout_height="50dip" - android:layout_gravity="left|center" - android:scaleType="fitStart"/> - - <LinearLayout - android:layout_width="0dp" + android:id="@+id/slide_up_frame" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical" + android:visibility="gone"> + + <FrameLayout + android:id="@+id/slide_up_swipe_target" + android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_gravity="center_vertical" - android:layout_weight="1" - android:orientation="vertical" - android:paddingLeft="8dip"> + android:background="?attr/colorPrimary"> - <TextView - android:id="@+id/track_name" - android:layout_height="wrap_content" - android:layout_width="wrap_content" - android:textColor="?android:textColorPrimary" - android:singleLine="true" - android:textAppearance="?android:attr/textAppearanceSmall" - android:textSize="13sp" - android:text="@string/search.artists"/> - - <TextView - android:id="@+id/artist_name" + <android.support.v7.widget.Toolbar + android:id="@+id/now_playing_toolbar" + android:layout_height="?attr/actionBarSize" + android:layout_width="match_parent" + android:elevation="4dp" + android:visibility="gone" + app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" + app:popupTheme="?attr/actionbarPopupStyle"/> + + <LinearLayout + android:id="@+id/bottom_bar" + android:layout_width="fill_parent" android:layout_height="wrap_content" - android:layout_width="wrap_content" - android:textColor="?android:textColorSecondary" - android:singleLine="true" - android:textAppearance="?android:attr/textAppearanceSmall" - android:textSize="12sp" - android:text="@string/search.albums"/> - </LinearLayout> - - <LinearLayout - android:layout_height="wrap_content" - android:layout_width="0dp" - android:layout_weight="1"> - - <ImageButton - style="@style/PlaybackControl.Small" - android:id="@+id/download_previous" - android:src="?attr/media_button_backward" - android:layout_width="0dp" - android:layout_weight="1"/> - - <ImageButton - style="@style/PlaybackControl.Small" - android:id="@+id/download_start" - android:src="?attr/media_button_start" - android:layout_width="0dp" - android:layout_weight="1"/> - - <ImageButton - style="@style/PlaybackControl.Small" - android:id="@+id/download_next" - android:src="?attr/media_button_forward" - android:layout_width="0dp" - android:layout_weight="1"/> - </LinearLayout> + style="@style/BasicButton" + android:orientation="horizontal" + android:elevation="4dp"> + + <github.daneren2005.dsub.view.RecyclingImageView + android:id="@+id/album_art" + android:layout_width="?attr/actionBarSize" + android:layout_height="?attr/actionBarSize" + android:layout_gravity="left|center" + android:scaleType="fitStart"/> + + <LinearLayout + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_gravity="center_vertical" + android:layout_weight="1" + android:orientation="vertical" + android:paddingLeft="8dip"> + + <TextView + android:id="@+id/track_name" + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:singleLine="true" + android:text="@string/main.title" + android:ellipsize="marquee" + android:marqueeRepeatLimit="marquee_forever" + android:scrollHorizontally="true" + android:focusable="true" + android:focusableInTouchMode="true" + style="?attr/actionbarTitleStyle"> + + <requestFocus android:focusable="true" + android:focusableInTouchMode="true" + android:duplicateParentState="true" /> + </TextView> + + <TextView + android:id="@+id/artist_name" + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:singleLine="true" + android:text="@string/main.artist" + style="?attr/actionbarSubtitleStyle"/> + </LinearLayout> + + + <LinearLayout + android:layout_height="fill_parent" + android:layout_width="0dp" + android:layout_weight="1"> + + <ImageButton + style="@style/PlaybackControl.Match" + android:id="@+id/download_previous" + android:src="?attr/actionbar_backward" + android:layout_width="0dp" + android:layout_weight="1"/> + + <ImageButton + style="@style/PlaybackControl.Match" + android:id="@+id/download_start" + android:src="?attr/actionbar_start" + android:layout_width="0dp" + android:layout_weight="1"/> + + <ImageButton + style="@style/PlaybackControl.Match" + android:id="@+id/download_next" + android:src="?attr/actionbar_forward" + android:layout_width="0dp" + android:layout_weight="1"/> + </LinearLayout> + </LinearLayout> + </FrameLayout> + + <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/now_playing_fragment_container" + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="1"/> </LinearLayout> -</LinearLayout>
\ No newline at end of file +</com.sothree.slidinguppanel.SlidingUpPanelLayout>
\ No newline at end of file diff --git a/app/src/main/res/layout/abstract_fragment_container.xml b/app/src/main/res/layout/abstract_fragment_container.xml index 61e17d1d..f13356c4 100644 --- a/app/src/main/res/layout/abstract_fragment_container.xml +++ b/app/src/main/res/layout/abstract_fragment_container.xml @@ -2,5 +2,5 @@ <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/fragment_container" android:layout_width="match_parent" - android:layout_height="0px" - android:layout_weight="1"/>
\ No newline at end of file + android:layout_height="match_parent" + android:layout_marginTop="?attr/actionBarSize"/>
\ No newline at end of file diff --git a/app/src/main/res/layout/abstract_list_fragment.xml b/app/src/main/res/layout/abstract_recycler_fragment.xml index 618a7341..47141450 100644 --- a/app/src/main/res/layout/abstract_list_fragment.xml +++ b/app/src/main/res/layout/abstract_recycler_fragment.xml @@ -10,17 +10,12 @@ android:layout_height="fill_parent" android:orientation="vertical" > - <View - android:layout_width="fill_parent" - android:layout_height="1px" - android:background="@color/dividerColor"/> - - <ListView - android:id="@+id/fragment_list" + <android.support.v7.widget.RecyclerView + android:id="@+id/fragment_recycler" android:layout_width="fill_parent" android:layout_height="0dip" android:layout_weight="1.0" - android:fastScrollEnabled="true"/> + android:scrollbars="vertical"/> <include layout="@layout/tab_progress" /> </LinearLayout> diff --git a/app/src/main/res/layout/actionbar_spinner.xml b/app/src/main/res/layout/actionbar_spinner.xml index 22055901..4d9f95a4 100644 --- a/app/src/main/res/layout/actionbar_spinner.xml +++ b/app/src/main/res/layout/actionbar_spinner.xml @@ -1,13 +1,14 @@ <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:orientation="vertical" - android:layout_width="fill_parent" - android:layout_height="fill_parent" - android:gravity="fill_horizontal" > - <Spinner - android:id="@+id/spinner" - android:layout_width="fill_parent" - android:layout_height="fill_parent" - android:prompt="@string/common.appname" - /> + android:orientation="vertical" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:gravity="fill_horizontal" > + + <Spinner + android:id="@+id/spinner" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:prompt="@string/common.appname" + android:background="@drawable/abc_spinner_mtrl_am_alpha"/> </RelativeLayout> diff --git a/app/src/main/res/layout/album_cell_item.xml b/app/src/main/res/layout/album_cell_item.xml index 3f708e63..fe634c13 100644 --- a/app/src/main/res/layout/album_cell_item.xml +++ b/app/src/main/res/layout/album_cell_item.xml @@ -2,7 +2,8 @@ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" - android:layout_height="match_parent"> + android:layout_height="match_parent" + android:background="?attr/selectableItemBackground"> <RelativeLayout android:layout_width="match_parent" @@ -77,7 +78,7 @@ </LinearLayout> <ImageView - android:id="@+id/album_more" + android:id="@+id/more_button" android:src="?attr/download_none" android:layout_width="wrap_content" android:layout_height="wrap_content" diff --git a/app/src/main/res/layout/album_list_header.xml b/app/src/main/res/layout/album_list_header.xml new file mode 100644 index 00000000..e78d0ace --- /dev/null +++ b/app/src/main/res/layout/album_list_header.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:orientation="horizontal"> + + <TextView + android:id="@+id/item_name" + android:layout_width="0dp" + android:layout_weight="1" + android:layout_height="wrap_content" + android:textAppearance="?android:attr/textAppearanceMedium" + android:background="@android:color/transparent" + android:textColor="?attr/colorAccent" + android:textStyle="bold" + android:paddingLeft="6dp" + android:paddingRight="6dp" + android:paddingTop="8dp" + android:paddingBottom="8dp"/> + + <CheckBox + android:id="@+id/item_checkbox" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/main.albums_per_folder" + android:layout_marginRight="6dp" + android:layout_gravity="right" + android:textColor="?android:textColorPrimary"/> +</LinearLayout>
\ No newline at end of file diff --git a/app/src/main/res/layout/album_list_item.xml b/app/src/main/res/layout/album_list_item.xml index 0ee92edd..150cbd8f 100644 --- a/app/src/main/res/layout/album_list_item.xml +++ b/app/src/main/res/layout/album_list_item.xml @@ -3,7 +3,8 @@ android:id="@id/drag_handle" android:orientation="horizontal" android:layout_width="fill_parent" - android:layout_height="wrap_content"> + android:layout_height="wrap_content" + android:background="?attr/selectableItemBackground"> <RelativeLayout android:layout_width="@dimen/AlbumArt.Small" @@ -43,14 +44,16 @@ android:textAppearance="?android:attr/textAppearanceMedium" android:singleLine="true" android:ellipsize="marquee" - android:paddingBottom="6dip"/> + android:paddingBottom="6dip" + android:textColor="?android:textColorPrimary"/> <TextView android:id="@+id/album_artist" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceSmall" - android:singleLine="true"/> + android:singleLine="true" + android:textColor="?android:textColorSecondary"/> </LinearLayout> @@ -65,7 +68,7 @@ android:visibility="gone"/> <ImageView - android:id="@+id/album_more" + android:id="@+id/more_button" android:src="?attr/download_none" android:layout_width="wrap_content" android:layout_height="fill_parent" diff --git a/app/src/main/res/layout/basic_choice_item.xml b/app/src/main/res/layout/basic_choice_item.xml new file mode 100644 index 00000000..e2dc2204 --- /dev/null +++ b/app/src/main/res/layout/basic_choice_item.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="horizontal" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:background="?attr/selectableItemBackground"> + + <TextView + android:id="@+id/item_name" + android:layout_width="0dip" + android:layout_height="?android:attr/listPreferredItemHeight" + android:layout_weight="1" + android:textAppearance="?android:attr/textAppearanceMedium" + android:gravity="left|center_vertical" + android:paddingLeft="6dip" + android:paddingRight="6dip" + android:background="@android:color/transparent" + android:textColor="?android:textColorPrimary"/> + + <CheckBox + android:id="@+id/item_checkbox" + android:layout_width="wrap_content" + android:layout_height="fill_parent" + android:layout_gravity="right|center_vertical" + android:checkMark="?android:attr/listChoiceIndicatorMultiple" + style="@style/MoreButton"/> +</LinearLayout>
\ No newline at end of file diff --git a/app/src/main/res/layout/basic_count_item.xml b/app/src/main/res/layout/basic_count_item.xml index 08d276db..ce1aa800 100644 --- a/app/src/main/res/layout/basic_count_item.xml +++ b/app/src/main/res/layout/basic_count_item.xml @@ -2,7 +2,7 @@ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="wrap_content" - android:background="@android:color/transparent" + android:background="?attr/selectableItemBackground" android:minHeight="50dip"> <TextView @@ -14,7 +14,8 @@ android:paddingLeft="6dip" android:paddingRight="6dip" android:background="@android:color/transparent" - android:text="Text"/> + android:text="Text" + android:textColor="?android:textColorPrimary"/> <TextView android:id="@+id/basic_count_count" @@ -31,5 +32,6 @@ android:textSize="11sp" android:gravity="right|center_vertical" android:layout_gravity="center_vertical" + android:textColor="?android:textColorPrimary" android:visibility="gone"/> </LinearLayout>
\ No newline at end of file diff --git a/app/src/main/res/layout/basic_header.xml b/app/src/main/res/layout/basic_header.xml new file mode 100644 index 00000000..b1f94b33 --- /dev/null +++ b/app/src/main/res/layout/basic_header.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8"?> +<TextView xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/item_name" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:textAppearance="?android:attr/textAppearanceMedium" + android:background="@android:color/transparent" + android:textColor="?attr/colorAccent" + android:textStyle="bold" + android:paddingLeft="6dp" + android:paddingRight="6dp" + android:paddingTop="8dp" + android:paddingBottom="8dp"/>
\ No newline at end of file diff --git a/app/src/main/res/layout/basic_list_item.xml b/app/src/main/res/layout/basic_list_item.xml index 2338f7e0..04fdfe06 100644 --- a/app/src/main/res/layout/basic_list_item.xml +++ b/app/src/main/res/layout/basic_list_item.xml @@ -3,7 +3,7 @@ android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="wrap_content" - android:background="@android:color/transparent"> + android:background="?attr/selectableItemBackground"> <TextView android:id="@+id/item_name" @@ -15,7 +15,8 @@ android:paddingLeft="6dip" android:paddingRight="6dip" android:minHeight="50dip" - android:background="@android:color/transparent"/> + android:background="@android:color/transparent" + android:textColor="?android:textColorPrimary"/> <ImageButton android:id="@+id/item_star" diff --git a/app/src/main/res/layout/change_email.xml b/app/src/main/res/layout/change_email.xml index 18ffc765..87d297be 100644 --- a/app/src/main/res/layout/change_email.xml +++ b/app/src/main/res/layout/change_email.xml @@ -15,7 +15,8 @@ android:layout_height="wrap_content" android:layout_marginLeft="4dp" android:textSize="20dp" - android:text="@string/admin.change_email_label" /> + android:text="@string/admin.change_email_label" + android:textColor="?android:textColorPrimary"/> <EditText android:id="@+id/new_email" android:inputType="textEmailAddress" diff --git a/app/src/main/res/layout/change_password.xml b/app/src/main/res/layout/change_password.xml index 1a382a6b..d8043c05 100644 --- a/app/src/main/res/layout/change_password.xml +++ b/app/src/main/res/layout/change_password.xml @@ -15,7 +15,8 @@ android:layout_height="wrap_content" android:layout_marginLeft="4dp" android:textSize="20dp" - android:text="@string/admin.change_password_label" /> + android:text="@string/admin.change_password_label" + android:textColor="?android:textColorPrimary"/> <EditText android:id="@+id/new_password" android:inputType="textPassword" diff --git a/app/src/main/res/layout/chat_item.xml b/app/src/main/res/layout/chat_item.xml index f31f7988..bf5d81ca 100644 --- a/app/src/main/res/layout/chat_item.xml +++ b/app/src/main/res/layout/chat_item.xml @@ -2,7 +2,8 @@ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="fill_parent" - android:layout_height="wrap_content"> + android:layout_height="wrap_content" + android:background="?attr/selectableItemBackground"> <github.daneren2005.dsub.view.RecyclingImageView android:id="@+id/chat_avatar" @@ -34,7 +35,7 @@ android:singleLine="true" android:text="User" android:textAppearance="?android:attr/textAppearanceLarge" - android:textColor="?android:textColorSecondary"/> + android:textColor="?android:textColorPrimary"/> <TextView android:id="@+id/chat_time" @@ -43,7 +44,8 @@ android:layout_marginLeft="6dip" android:singleLine="true" android:text="00:00" - android:textAppearance="?android:attr/textAppearanceSmall" /> + android:textAppearance="?android:attr/textAppearanceSmall" + android:textColor="?android:textColorSecondary"/> </LinearLayout> <TextView @@ -56,6 +58,7 @@ android:linksClickable="true" android:singleLine="false" android:text="Message Text Goes Here" - android:textAppearance="?android:attr/textAppearanceMedium" /> + android:textAppearance="?android:attr/textAppearanceMedium" + android:textColor="?android:textColorSecondary"/> </LinearLayout> </LinearLayout>
\ No newline at end of file diff --git a/app/src/main/res/layout/chat_item_reverse.xml b/app/src/main/res/layout/chat_item_reverse.xml index b8102193..31875208 100644 --- a/app/src/main/res/layout/chat_item_reverse.xml +++ b/app/src/main/res/layout/chat_item_reverse.xml @@ -2,7 +2,8 @@ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="fill_parent" - android:layout_height="wrap_content"> + android:layout_height="wrap_content" + android:background="?attr/selectableItemBackground"> <LinearLayout android:layout_width="0dip" @@ -24,7 +25,8 @@ android:layout_marginRight="6dip" android:singleLine="true" android:text="00:00" - android:textAppearance="?android:attr/textAppearanceSmall" /> + android:textAppearance="?android:attr/textAppearanceSmall" + android:textColor="?android:textColorSecondary"/> <TextView android:id="@+id/chat_username" @@ -35,7 +37,7 @@ android:singleLine="true" android:text="User" android:textAppearance="?android:attr/textAppearanceLarge" - android:textColor="?android:textColorSecondary"/> + android:textColor="?android:textColorPrimary"/> </LinearLayout> <TextView @@ -49,7 +51,8 @@ android:singleLine="false" android:text="Chat message" android:textAppearance="?android:attr/textAppearanceMedium" - android:layout_gravity="right"/> + android:layout_gravity="right" + android:textColor="?android:textColorSecondary"/> </LinearLayout> <github.daneren2005.dsub.view.RecyclingImageView diff --git a/app/src/main/res/layout/complex_list_item.xml b/app/src/main/res/layout/complex_list_item.xml index 67851eca..2aec9451 100644 --- a/app/src/main/res/layout/complex_list_item.xml +++ b/app/src/main/res/layout/complex_list_item.xml @@ -1,9 +1,9 @@ <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:orientation="horizontal" - android:layout_width="fill_parent" - android:layout_height="?android:attr/listPreferredItemHeight" - android:background="@android:color/transparent"> + android:orientation="horizontal" + android:layout_width="fill_parent" + android:layout_height="?android:attr/listPreferredItemHeight" + android:background="?attr/selectableItemBackground"> <LinearLayout android:orientation="vertical" android:layout_width="0dip" @@ -19,14 +19,16 @@ android:layout_width="fill_parent" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceMedium" - android:background="@android:color/transparent"/> + android:background="@android:color/transparent" + android:textColor="?android:textColorPrimary"/> <TextView android:id="@+id/item_description" android:layout_width="fill_parent" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceSmall" - android:background="@android:color/transparent"/> + android:background="@android:color/transparent" + android:textColor="?android:textColorSecondary"/> </LinearLayout> <ImageButton diff --git a/app/src/main/res/layout/confirm_password.xml b/app/src/main/res/layout/confirm_password.xml index 27ee04ea..9ec61c0a 100644 --- a/app/src/main/res/layout/confirm_password.xml +++ b/app/src/main/res/layout/confirm_password.xml @@ -15,7 +15,8 @@ android:layout_height="wrap_content" android:layout_marginLeft="4dp" android:textSize="20dp" - android:text="@string/admin.add_user_password" /> + android:text="@string/admin.add_user_password" + android:textColor="?android:textColorPrimary"/> <EditText android:id="@+id/password" android:inputType="textPassword" diff --git a/app/src/main/res/layout/create_bookmark.xml b/app/src/main/res/layout/create_bookmark.xml index f72b39d8..d6f077c3 100644 --- a/app/src/main/res/layout/create_bookmark.xml +++ b/app/src/main/res/layout/create_bookmark.xml @@ -14,7 +14,8 @@ android:layout_height="wrap_content" android:layout_marginLeft="4dp" android:textSize="20dp" - android:text="@string/common.comment" /> + android:text="@string/common.comment" + android:textColor="?android:textColorPrimary"/> <EditText android:id="@+id/comment_text" android:inputType="text" diff --git a/app/src/main/res/layout/create_podcast.xml b/app/src/main/res/layout/create_podcast.xml index 5a2ec970..04e74ec3 100644 --- a/app/src/main/res/layout/create_podcast.xml +++ b/app/src/main/res/layout/create_podcast.xml @@ -14,7 +14,8 @@ android:layout_height="wrap_content" android:layout_marginLeft="4dp" android:textSize="20dp" - android:text="@string/select_podcasts.add_url"/> + android:text="@string/select_podcasts.add_url" + android:textColor="?android:textColorPrimary"/> <EditText android:id="@+id/create_podcast_url" android:inputType="textUri" diff --git a/app/src/main/res/layout/create_user.xml b/app/src/main/res/layout/create_user.xml index eac6e5cb..b2d8f6e0 100644 --- a/app/src/main/res/layout/create_user.xml +++ b/app/src/main/res/layout/create_user.xml @@ -22,7 +22,8 @@ android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_weight="1" - android:layout_marginLeft="4dp" /> + android:layout_marginLeft="4dp" + android:textColor="?android:textColorPrimary"/> </LinearLayout> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" @@ -43,7 +44,8 @@ android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_weight="1" - android:layout_marginLeft="4dp" /> + android:layout_marginLeft="4dp" + android:textColor="?android:textColorPrimary"/> </LinearLayout> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" @@ -64,14 +66,14 @@ android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_weight="1" - android:layout_marginLeft="4dp" /> + android:layout_marginLeft="4dp" + android:textColor="?android:textColorPrimary"/> </LinearLayout> - <ListView + <android.support.v7.widget.RecyclerView android:id="@+id/settings_list" android:layout_width="fill_parent" android:layout_height="0dip" android:layout_weight="1.0" - android:fastScrollEnabled="true"/> - + android:scrollbars="vertical"/> </LinearLayout>
\ No newline at end of file diff --git a/app/src/main/res/layout/download_activity.xml b/app/src/main/res/layout/download_activity.xml deleted file mode 100644 index 017e4013..00000000 --- a/app/src/main/res/layout/download_activity.xml +++ /dev/null @@ -1,4 +0,0 @@ -<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/fragment_container" - android:layout_width="match_parent" - android:layout_height="match_parent" />
\ No newline at end of file diff --git a/app/src/main/res/layout/download_playlist.xml b/app/src/main/res/layout/download_playlist.xml index 8a73ef3b..7a83330c 100644 --- a/app/src/main/res/layout/download_playlist.xml +++ b/app/src/main/res/layout/download_playlist.xml @@ -1,31 +1,29 @@ <?xml version="1.0" encoding="utf-8"?> - <LinearLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:orientation="vertical" - android:layout_width="fill_parent" - android:layout_height="fill_parent" - android:layout_weight="1"> + xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:layout_weight="1"> <View - android:layout_width="fill_parent" - android:layout_height="1px" - android:background="@color/dividerColor"/> + android:layout_width="fill_parent" + android:layout_height="1px" + android:background="?attr/colorPrimary"/> <TextView - android:id="@+id/download_empty" - android:text="@string/download.empty" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:padding="10dip"/> + android:id="@+id/download_empty" + android:text="@string/download.empty" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:padding="10dip" + android:textColor="?android:textColorPrimary"/> - <com.mobeta.android.dslv.DragSortListView - style="@style/DragDropListView" + <android.support.v7.widget.RecyclerView android:id="@+id/download_list" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_weight="1" - android:cacheColorHint="#00000000" - android:fastScrollEnabled="true"/> + android:scrollbars="vertical"/> </LinearLayout>
\ No newline at end of file diff --git a/app/src/main/res/layout/drawer_header.xml b/app/src/main/res/layout/drawer_header.xml new file mode 100644 index 00000000..fe0a9f2d --- /dev/null +++ b/app/src/main/res/layout/drawer_header.xml @@ -0,0 +1,62 @@ +<?xml version="1.0" encoding="utf-8"?> +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="178dp" + android:orientation="vertical" + android:weightSum="1" + android:background="?attr/selectableItemBackground"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="56dp" + android:orientation="horizontal" + android:layout_alignParentBottom="true" + android:layout_alignParentLeft="true" + android:layout_alignParentStart="true"> + + <LinearLayout + android:orientation="vertical" + android:layout_width="wrap_content" + android:layout_height="wrap_content"> + + <TextView + android:id="@+id/header_server_name" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginLeft="16dp" + android:textColor="?android:textColorPrimary" + android:textSize="14sp" + android:textStyle="bold"/> + + <TextView + android:id="@+id/header_user_name" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginLeft="16dp" + android:layout_marginTop="5dp" + android:textColor="?android:textColorSecondary" + android:textSize="14sp" + android:textStyle="normal"/> + </LinearLayout> + + <View + android:layout_width="0dp" + android:layout_height="0dp" + android:layout_weight="1"/> + + <ImageView + android:id="@+id/header_select_image" + android:src="?attr/select_server" + android:layout_gravity="center_vertical" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingRight="20dp"/> + </LinearLayout> + + <de.hdodenhof.circleimageview.CircleImageView + android:id="@+id/header_user_avatar" + android:layout_width="70dp" + android:layout_height="70dp" + android:layout_marginLeft="16dp" + android:layout_marginTop="38dp"/> +</RelativeLayout>
\ No newline at end of file diff --git a/app/src/main/res/layout/drawer_list_item.xml b/app/src/main/res/layout/drawer_list_item.xml deleted file mode 100644 index 5f17c9e9..00000000 --- a/app/src/main/res/layout/drawer_list_item.xml +++ /dev/null @@ -1,26 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:orientation="horizontal" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:paddingTop="8dip" - android:paddingBottom="9dip"> - - <ImageView - android:id="@+id/drawer_icon" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="left|center_vertical" - android:paddingTop="1dip" - android:paddingBottom="1dip" - android:paddingRight="8dip" - android:paddingLeft="10dip"/> - - <TextView - android:id="@+id/drawer_name" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:textSize="26sp" - android:singleLine="true" - android:textColor="?android:textColorPrimary"/> -</LinearLayout> diff --git a/app/src/main/res/layout/edit_play_action.xml b/app/src/main/res/layout/edit_play_action.xml index a1115da6..95a3d7ff 100644 --- a/app/src/main/res/layout/edit_play_action.xml +++ b/app/src/main/res/layout/edit_play_action.xml @@ -14,7 +14,8 @@ android:layout_height="wrap_content" android:layout_marginLeft="4dp" android:textSize="20dp" - android:text="@string/tasker.edit_shuffle_mode" /> + android:text="@string/tasker.edit_shuffle_mode" + android:textColor="?android:textColorPrimary"/> <CheckBox android:id="@+id/edit_shuffle_checkbox" android:layout_width="wrap_content" @@ -33,7 +34,8 @@ android:layout_height="wrap_content" android:layout_marginLeft="4dp" android:textSize="20dp" - android:text="@string/tasker.edit_shuffle_start_year" /> + android:text="@string/tasker.edit_shuffle_start_year" + android:textColor="?android:textColorPrimary"/> <CheckBox android:id="@+id/edit_start_year_checkbox" @@ -61,7 +63,8 @@ android:layout_height="wrap_content" android:layout_marginLeft="4dp" android:textSize="20dp" - android:text="@string/tasker.edit_shuffle_end_year" /> + android:text="@string/tasker.edit_shuffle_end_year" + android:textColor="?android:textColorPrimary"/> <CheckBox android:id="@+id/edit_end_year_checkbox" @@ -89,7 +92,8 @@ android:layout_height="wrap_content" android:layout_marginLeft="4dp" android:textSize="20dp" - android:text="@string/tasker.edit_shuffle_genre"/> + android:text="@string/tasker.edit_shuffle_genre" + android:textColor="?android:textColorPrimary"/> <Button android:id="@+id/edit_genre_spinner" @@ -109,7 +113,8 @@ android:layout_height="wrap_content" android:layout_marginLeft="4dp" android:textSize="20dp" - android:text="@string/tasker.edit_server_offline"/> + android:text="@string/tasker.edit_server_offline" + android:textColor="?android:textColorPrimary"/> <Spinner android:id="@+id/edit_offline_spinner" diff --git a/app/src/main/res/layout/equalizer_bar.xml b/app/src/main/res/layout/equalizer_bar.xml index 6dc91565..9e3fab5c 100644 --- a/app/src/main/res/layout/equalizer_bar.xml +++ b/app/src/main/res/layout/equalizer_bar.xml @@ -1,36 +1,33 @@ <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:orientation="vertical" - android:layout_width="fill_parent" - android:layout_height="wrap_content"> + android:orientation="vertical" + android:layout_width="fill_parent" + android:layout_height="wrap_content"> - <TextView - android:id="@+id/equalizer.frequency" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginTop="8dp" - android:layout_alignParentLeft="true" - /> - - <TextView - android:id="@+id/equalizer.level" - android:text="0 dB" - android:textSize="12sp" - android:gravity="right" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginTop="8dp" - android:layout_alignParentRight="true" - android:layout_toRightOf="@+id/equalizer.frequency" - /> - - <SeekBar - android:id="@+id/equalizer.bar" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:layout_below="@+id/equalizer.frequency" - /> + <TextView + android:id="@+id/equalizer.frequency" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="8dp" + android:layout_alignParentLeft="true" + android:textColor="?android:textColorPrimary"/> + <TextView + android:id="@+id/equalizer.level" + android:text="0 dB" + android:textSize="12sp" + android:gravity="right" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="8dp" + android:layout_alignParentRight="true" + android:layout_toRightOf="@+id/equalizer.frequency" + android:textColor="?android:textColorSecondary"/> + <SeekBar + android:id="@+id/equalizer.bar" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_below="@+id/equalizer.frequency"/> </RelativeLayout> diff --git a/app/src/main/res/layout/genre_list_item.xml b/app/src/main/res/layout/genre_list_item.xml index 6affa24c..fb30e167 100644 --- a/app/src/main/res/layout/genre_list_item.xml +++ b/app/src/main/res/layout/genre_list_item.xml @@ -3,7 +3,7 @@ android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="wrap_content" - android:background="@android:color/transparent"> + android:background="?attr/selectableItemBackground"> <TextView android:id="@+id/genre_name" @@ -17,7 +17,8 @@ android:minHeight="50dip" android:singleLine="true" android:ellipsize="marquee" - android:background="@android:color/transparent"/> + android:background="@android:color/transparent" + android:textColor="?android:textColorPrimary"/> <LinearLayout android:layout_width="wrap_content" @@ -31,12 +32,14 @@ android:id="@+id/genre_songs" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:textAppearance="?android:attr/textAppearanceSmall"/> + android:textAppearance="?android:attr/textAppearanceSmall" + android:textColor="?android:textColorSecondary"/> <TextView android:id="@+id/genre_albums" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:textAppearance="?android:attr/textAppearanceSmall"/> + android:textAppearance="?android:attr/textAppearanceSmall" + android:textColor="?android:textColorSecondary"/> </LinearLayout> </LinearLayout>
\ No newline at end of file diff --git a/app/src/main/res/layout/grid_view.xml b/app/src/main/res/layout/grid_view.xml deleted file mode 100644 index 599cf92c..00000000 --- a/app/src/main/res/layout/grid_view.xml +++ /dev/null @@ -1,14 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<github.daneren2005.dsub.view.HeaderGridView xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/gridview" - android:layout_width="fill_parent" - android:layout_height="0dip" - android:layout_weight="1.0" - android:numColumns="@integer/Grid.Columns" - android:horizontalSpacing="10dp" - android:verticalSpacing="10dp" - android:gravity="center" - android:stretchMode="columnWidth" - android:padding="24px" - android:fastScrollEnabled="true" - android:scrollbarStyle="outsideOverlay"/>
\ No newline at end of file diff --git a/app/src/main/res/layout/home.xml b/app/src/main/res/layout/home.xml index e5bf5a70..043886a2 100644 --- a/app/src/main/res/layout/home.xml +++ b/app/src/main/res/layout/home.xml @@ -8,7 +8,7 @@ <View android:layout_width="fill_parent" android:layout_height="1px" - android:background="@color/dividerColor"/> + android:background="?attr/colorPrimary"/> <ListView android:id="@+id/main_list" diff --git a/app/src/main/res/layout/jukebox_volume.xml b/app/src/main/res/layout/jukebox_volume.xml deleted file mode 100644 index 0c49f634..00000000 --- a/app/src/main/res/layout/jukebox_volume.xml +++ /dev/null @@ -1,46 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<RelativeLayout - android:background="@drawable/toast_frame" - android:padding="20dip" - android:layout_height="fill_parent" - android:layout_width="fill_parent" - android:orientation="vertical" - android:id="@+id/toast_layout_root" - xmlns:android="http://schemas.android.com/apk/res/android"> - - <TextView - android:layout_height="wrap_content" - android:layout_width="fill_parent" - android:id="@+id/jukebox_volume_title" - android:paddingBottom="12dp" - android:paddingRight="32dp" - android:paddingLeft="32dp" - android:shadowRadius="2.75" - android:shadowColor="#bb000000" - android:textColor="#ffffffff" - android:textAppearance="?android:attr/textAppearanceMedium" - android:text="@string/download.jukebox_volume" - android:gravity="center_horizontal" - android:layout_alignParentTop="true" - android:layout_alignParentRight="true" - android:layout_alignParentLeft="true"/> - - <ImageView - android:layout_height="wrap_content" - android:layout_width="wrap_content" - android:id="@+id/jukebox_volume_image" - android:paddingRight="12dip" - android:layout_alignParentLeft="true" - android:layout_below="@+id/jukebox_volume_title" - android:src="@drawable/ic_action_volume_dark"/> - - <SeekBar - android:layout_height="wrap_content" - android:layout_width="fill_parent" - android:id="@+id/jukebox_volume_progress_bar" - android:paddingBottom="3dp" - android:layout_alignParentRight="true" - android:layout_below="@+id/jukebox_volume_title" - android:layout_toRightOf="@+id/jukebox_volume_image" - android:indeterminate="false"/> -</RelativeLayout>
\ No newline at end of file diff --git a/app/src/main/res/layout/lyrics.xml b/app/src/main/res/layout/lyrics.xml index 747727bd..f2aa7c7f 100644 --- a/app/src/main/res/layout/lyrics.xml +++ b/app/src/main/res/layout/lyrics.xml @@ -1,55 +1,54 @@ <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:orientation="vertical" - android:layout_width="fill_parent" - android:layout_height="fill_parent"> - - <include layout="@layout/tab_progress"/> - - <ScrollView - android:layout_width="fill_parent" - android:layout_height="0dip" - android:layout_weight="1.0"> - - <LinearLayout - android:orientation="vertical" - android:layout_width="fill_parent" - android:layout_height="fill_parent"> - <TextView - android:id="@+id/lyrics_artist" - android:textAppearance="?android:attr/textAppearanceMedium" - android:gravity="center_horizontal" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:paddingLeft="10dip" - android:paddingRight="10dip" - android:paddingTop="10dip" - android:paddingBottom="4dip" - /> - - <TextView - android:id="@+id/lyrics_title" - android:textAppearance="?android:attr/textAppearanceMedium" - android:gravity="center_horizontal" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:paddingLeft="10dip" - android:paddingRight="10dip" - /> - - <TextView - android:id="@+id/lyrics_text" - android:textAppearance="?android:attr/textAppearanceSmall" - android:gravity="center_horizontal" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:paddingLeft="10dip" - android:paddingRight="10dip" - /> - - </LinearLayout> - - </ScrollView> - + android:orientation="vertical" + android:layout_width="fill_parent" + android:layout_height="fill_parent"> + + <include layout="@layout/tab_progress"/> + + <ScrollView + android:layout_width="fill_parent" + android:layout_height="0dip" + android:layout_weight="1.0"> + + <LinearLayout + android:orientation="vertical" + android:layout_width="fill_parent" + android:layout_height="fill_parent"> + + <TextView + android:id="@+id/lyrics_artist" + android:textAppearance="?android:attr/textAppearanceMedium" + android:gravity="center_horizontal" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:paddingLeft="10dip" + android:paddingRight="10dip" + android:paddingTop="10dip" + android:paddingBottom="4dip" + android:textColor="?android:textColorPrimary"/> + + <TextView + android:id="@+id/lyrics_title" + android:textAppearance="?android:attr/textAppearanceSmall" + android:gravity="center_horizontal" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:paddingLeft="10dip" + android:paddingRight="10dip" + android:paddingBottom="12dip" + android:textColor="?android:textColorPrimary"/> + + <TextView + android:id="@+id/lyrics_text" + android:textAppearance="?android:attr/textAppearanceSmall" + android:gravity="center_horizontal" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:paddingLeft="10dip" + android:paddingRight="10dip" + android:textColor="?android:textColorSecondary"/> + </LinearLayout> + </ScrollView> </LinearLayout> diff --git a/app/src/main/res/layout/main_buttons.xml b/app/src/main/res/layout/main_buttons.xml deleted file mode 100644 index 95a60409..00000000 --- a/app/src/main/res/layout/main_buttons.xml +++ /dev/null @@ -1,157 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:orientation="vertical" - android:layout_width="fill_parent" - android:layout_height="wrap_content"> - - <LinearLayout - android:id="@+id/main_select_server" - android:orientation="horizontal" - android:paddingTop="2dip" - android:paddingBottom="2dip" - android:paddingLeft="6dp" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:minHeight="?android:attr/listPreferredItemHeight"> - - <ImageView - android:src="?attr/select_server" - android:layout_gravity="center_vertical" - android:layout_width="wrap_content" - android:layout_height="wrap_content"/> - - <LinearLayout - android:orientation="vertical" - android:layout_width="wrap_content" - android:layout_height="wrap_content"> - - <TextView android:id="@+id/main.select_server_1" - android:text="@string/main.select_server" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:layout_marginLeft="10dip" - android:layout_marginTop="6dip" - android:textAppearance="?android:attr/textAppearanceLarge"/> - - <TextView android:id="@+id/main.select_server_2" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:layout_marginLeft="10dip" - android:textAppearance="?android:attr/textAppearanceSmall"/> - - </LinearLayout> - </LinearLayout> - - <TextView - android:id="@+id/main_offline" - android:text="@string/main.offline" - android:drawablePadding="12dip" - android:drawableLeft="?attr/offline_icon" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:textAppearance="?android:attr/textAppearanceMedium" - android:gravity="center_vertical" - android:paddingLeft="6dp" - android:paddingBottom="4dp" - android:minHeight="50dip"/> - - <LinearLayout - android:id="@+id/main_albums" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:orientation="horizontal"> - - <TextView - android:text="@string/main.albums_title" - style="@style/MainAlbumButtonLabel" - android:layout_width="0dp" - android:layout_weight="1" - android:layout_height="fill_parent"/> - - <CheckBox - android:id="@+id/main_albums_per_folder" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/main.albums_per_folder" - android:layout_marginRight="6dp" - android:layout_gravity="right"/> - </LinearLayout> - - <TextView - android:id="@+id/main_video_section" - android:text="@string/main.videos" - style="@style/MainAlbumButtonLabel" - android:layout_width="0dp" - android:layout_weight="1" - android:layout_height="fill_parent"/> - - <LinearLayout - android:id="@+id/main_albums_newest" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:minHeight="46dip"> - - <TextView - android:layout_width="wrap_content" - android:layout_height="fill_parent" - android:text="@string/main.albums_newest" - style="@style/MainAlbumButton"/> - - <TextView - android:id="@+id/main_albums_recent_count" - android:layout_width="32dp" - android:layout_height="32dp" - android:layout_marginRight="12dp" - android:background="@drawable/ic_number_border" - android:focusable="false" - android:paddingRight="10dp" - android:layout_marginLeft="20px" - android:layout_marginBottom="4px" - android:text="99" - android:textAppearance="?android:attr/textAppearanceSmallPopupMenu" - android:textSize="11sp" - android:gravity="right|center_vertical" - android:layout_gravity="center_vertical" - android:visibility="gone"/> - </LinearLayout> - - <TextView - android:id="@+id/main_albums_recent" - android:text="@string/main.albums_recent" - style="@style/MainAlbumButton"/> - <TextView - android:id="@+id/main_albums_frequent" - android:text="@string/main.albums_frequent" - style="@style/MainAlbumButton"/> - <TextView - android:id="@+id/main_albums_highest" - android:text="@string/main.albums_highest" - style="@style/MainAlbumButton"/> - <TextView - android:id="@+id/main_albums_starred" - android:text="@string/main.albums_starred" - style="@style/MainAlbumButton"/> - <TextView - android:id="@+id/main_albums_genres" - android:text="@string/main.albums_genres" - style="@style/MainAlbumButton"/> - <TextView - android:id="@+id/main_albums_year" - android:text="@string/main.albums_year" - style="@style/MainAlbumButton"/> - <TextView - android:id="@+id/main_albums_random" - android:text="@string/main.albums_random" - style="@style/MainAlbumButton"/> - <TextView - android:id="@+id/main_albums_alphabetical" - android:text="@string/main.albums_alphabetical" - style="@style/MainAlbumButton"/> - - <TextView - android:id="@+id/main_videos" - android:text="@string/main.videos" - style="@style/MainAlbumButton"/> - -</LinearLayout> - diff --git a/app/src/main/res/layout/progress.xml b/app/src/main/res/layout/progress.xml index a1904c11..8a299d63 100644 --- a/app/src/main/res/layout/progress.xml +++ b/app/src/main/res/layout/progress.xml @@ -1,20 +1,20 @@ <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:orientation="horizontal" - android:layout_weight="1" - android:layout_width="0dip" - android:layout_height="fill_parent" - android:padding="10dp"> + android:orientation="horizontal" + android:layout_weight="1" + android:layout_width="0dip" + android:layout_height="fill_parent" + android:padding="10dp"> - <ProgressBar - android:layout_width="wrap_content" - android:layout_height="fill_parent" - android:layout_marginRight="10dp"/> - - <TextView - android:id="@+id/progress_message" - android:text="@string/progress.wait" - android:layout_width="wrap_content" - android:layout_height="fill_parent"/> + <ProgressBar + android:layout_width="wrap_content" + android:layout_height="fill_parent" + android:layout_marginRight="10dp"/> + <TextView + android:id="@+id/progress_message" + android:text="@string/progress.wait" + android:layout_width="wrap_content" + android:layout_height="fill_parent" + android:textColor="?android:textColorPrimary"/> </LinearLayout>
\ No newline at end of file diff --git a/app/src/main/res/layout/save_playlist.xml b/app/src/main/res/layout/save_playlist.xml index 8bb21748..9a85d24c 100644 --- a/app/src/main/res/layout/save_playlist.xml +++ b/app/src/main/res/layout/save_playlist.xml @@ -1,17 +1,17 @@ <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/save_playlist_root" - android:padding="10dip" - android:orientation="vertical" - android:layout_width="fill_parent" - android:layout_height="fill_parent"> + android:id="@+id/save_playlist_root" + android:padding="10dip" + android:orientation="vertical" + android:layout_width="fill_parent" + android:layout_height="fill_parent"> - <EditText - android:id="@+id/save_playlist_name" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:inputType="text" - android:singleLine="true"/> + <EditText + android:id="@+id/save_playlist_name" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:inputType="text" + android:singleLine="true"/> <CheckBox android:id="@+id/save_playlist_overwrite" @@ -20,7 +20,8 @@ android:text="@string/playlist.overwrite" android:layout_marginLeft="4dp" android:checked="false" - android:visibility="gone"/> + android:visibility="gone" + android:textColor="?android:textColorPrimary"/> </LinearLayout> diff --git a/app/src/main/res/layout/search_buttons.xml b/app/src/main/res/layout/search_buttons.xml deleted file mode 100644 index 699ad341..00000000 --- a/app/src/main/res/layout/search_buttons.xml +++ /dev/null @@ -1,73 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:orientation="vertical" - android:layout_width="fill_parent" - android:layout_height="wrap_content"> - - <TextView - android:id="@+id/search_artists" - android:text="@string/search.artists" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:textAppearance="?android:attr/textAppearanceSmall" - android:textColor="#EFEFEF" - android:textStyle="bold" - android:background="#A5A5A5" - android:gravity="center_vertical" - android:paddingLeft="4dp"/> - - <TextView - android:id="@+id/search_albums" - android:text="@string/search.albums" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:textAppearance="?android:attr/textAppearanceSmall" - android:textColor="#EFEFEF" - android:textStyle="bold" - android:background="#A5A5A5" - android:gravity="center_vertical" - android:paddingLeft="4dp"/> - - <TextView - android:id="@+id/search_songs" - android:text="@string/search.songs" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:textAppearance="?android:attr/textAppearanceSmall" - android:textColor="#EFEFEF" - android:textStyle="bold" - android:background="#A5A5A5" - android:gravity="center_vertical" - android:paddingLeft="4dp"/> - - <TextView - android:id="@+id/search_more_artists" - android:text="@string/search.more" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:textAppearance="?android:attr/textAppearanceSmall" - android:gravity="center" - android:paddingTop="8dp" - android:paddingBottom="8dp"/> - - <TextView - android:id="@+id/search_more_albums" - android:text="@string/search.more" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:textAppearance="?android:attr/textAppearanceSmall" - android:gravity="center" - android:paddingTop="8dp" - android:paddingBottom="8dp"/> - - <TextView - android:id="@+id/search_more_songs" - android:text="@string/search.more" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:textAppearance="?android:attr/textAppearanceSmall" - android:gravity="center" - android:paddingTop="8dp" - android:paddingBottom="8dp"/> - -</LinearLayout> diff --git a/app/src/main/res/layout/seekbar_preference.xml b/app/src/main/res/layout/seekbar_preference.xml index 030b608b..74dad4cd 100644 --- a/app/src/main/res/layout/seekbar_preference.xml +++ b/app/src/main/res/layout/seekbar_preference.xml @@ -9,7 +9,8 @@ android:padding="5dip" android:layout_width="fill_parent" android:layout_height="wrap_content" - android:gravity="right" /> + android:gravity="right" + android:textColor="?android:textColorPrimary"/> <SeekBar android:id="@+id/seek_bar" android:padding="15dip" diff --git a/app/src/main/res/layout/select_album.xml b/app/src/main/res/layout/select_album.xml index bbdf0e54..e72ccffd 100644 --- a/app/src/main/res/layout/select_album.xml +++ b/app/src/main/res/layout/select_album.xml @@ -13,16 +13,15 @@ <View android:layout_width="fill_parent" android:layout_height="1px" - android:background="@color/dividerColor"/> + android:background="?attr/colorPrimary"/> <include layout="@layout/tab_progress"/> - <ListView + <android.support.v7.widget.RecyclerView android:id="@+id/select_album_entries" - android:textFilterEnabled="true" android:layout_width="fill_parent" android:layout_height="0dip" android:layout_weight="1.0" - android:fastScrollEnabled="true"/> + android:scrollbars="vertical"/> </LinearLayout> </android.support.v4.widget.SwipeRefreshLayout>
\ No newline at end of file diff --git a/app/src/main/res/layout/select_album_header.xml b/app/src/main/res/layout/select_album_header.xml index d028a476..5b2294f0 100644 --- a/app/src/main/res/layout/select_album_header.xml +++ b/app/src/main/res/layout/select_album_header.xml @@ -30,7 +30,8 @@ android:marqueeRepeatLimit="marquee_forever" android:scrollHorizontally="true" android:focusable="true" - android:focusableInTouchMode="true"> + android:focusableInTouchMode="true" + android:textColor="?android:textColorPrimary"> <requestFocus android:focusable="true" android:focusableInTouchMode="true" @@ -44,7 +45,8 @@ android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceMedium" android:singleLine="true" - android:ellipsize="end"/> + android:ellipsize="end" + android:textColor="?android:textColorPrimary"/> <TextView android:text="XX SONGS" @@ -54,7 +56,8 @@ android:paddingTop="14dip" android:textAppearance="?android:attr/textAppearanceSmall" android:singleLine="true" - android:ellipsize="none"/> + android:ellipsize="none" + android:textColor="?android:textColorSecondary"/> <TextView android:text="0:00" @@ -63,7 +66,8 @@ android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceSmall" android:singleLine="true" - android:ellipsize="none"/> + android:ellipsize="none" + android:textColor="?android:textColorSecondary"/> </LinearLayout> @@ -145,9 +149,16 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" - android:textAppearance="?android:attr/textAppearanceMedium"/> + android:textAppearance="?android:attr/textAppearanceMedium" + android:textColor="?android:textColorPrimary"/> </LinearLayout> </FrameLayout> + + <View + android:layout_width="fill_parent" + android:layout_height="1px" + android:background="?attr/colorPrimary" + android:layout_gravity="bottom"/> </FrameLayout> diff --git a/app/src/main/res/layout/select_artist_header.xml b/app/src/main/res/layout/select_artist_header.xml index 2821ce43..9ec94ff1 100644 --- a/app/src/main/res/layout/select_artist_header.xml +++ b/app/src/main/res/layout/select_artist_header.xml @@ -2,7 +2,9 @@ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" - android:layout_height="wrap_content"> + android:layout_height="wrap_content" + android:background="?attr/selectableItemBackground"> + <LinearLayout android:id="@+id/select_artist_folder" android:orientation="horizontal" @@ -30,14 +32,21 @@ android:layout_height="wrap_content" android:layout_marginLeft="10dip" android:layout_marginTop="6dip" - android:textAppearance="?android:attr/textAppearanceLarge"/> + android:textAppearance="?android:attr/textAppearanceLarge" + android:textColor="?attr/colorAccent"/> <TextView android:id="@+id/select_artist_folder_2" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_marginLeft="10dip" - android:textAppearance="?android:attr/textAppearanceSmall"/> + android:textAppearance="?android:attr/textAppearanceSmall" + android:textColor="?android:textColorSecondary"/> </LinearLayout> </LinearLayout> + + <View + android:layout_width="fill_parent" + android:layout_height="1px" + android:background="?attr/colorPrimary"/> </LinearLayout>
\ No newline at end of file diff --git a/app/src/main/res/layout/settings_activity.xml b/app/src/main/res/layout/settings_activity.xml new file mode 100644 index 00000000..c77528b2 --- /dev/null +++ b/app/src/main/res/layout/settings_activity.xml @@ -0,0 +1,21 @@ +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + + <android.support.v7.widget.Toolbar + android:id="@+id/main_toolbar" + android:layout_height="?attr/actionBarSize" + android:layout_width="match_parent" + android:background="?attr/colorPrimary" + android:elevation="4dp" + app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" + app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/> + + <FrameLayout + android:id="@+id/fragment_container" + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="1"/> +</LinearLayout>
\ No newline at end of file diff --git a/app/src/main/res/layout/shuffle_dialog.xml b/app/src/main/res/layout/shuffle_dialog.xml index 295f57cb..63778ed7 100644 --- a/app/src/main/res/layout/shuffle_dialog.xml +++ b/app/src/main/res/layout/shuffle_dialog.xml @@ -14,7 +14,8 @@ android:layout_height="wrap_content" android:layout_marginLeft="4dp" android:textSize="20dp" - android:text="@string/shuffle.startYear" /> + android:text="@string/shuffle.startYear" + android:textColor="?android:textColorPrimary"/> <EditText android:id="@+id/start_year" android:inputType="number" @@ -36,7 +37,8 @@ android:layout_height="wrap_content" android:layout_marginLeft="4dp" android:textSize="20dp" - android:text="@string/shuffle.endYear" /> + android:text="@string/shuffle.endYear" + android:textColor="?android:textColorPrimary"/> <EditText android:id="@+id/end_year" android:inputType="number" @@ -66,7 +68,8 @@ android:layout_height="wrap_content" android:layout_weight="1" android:layout_marginLeft="4dp" - android:hint="@string/shuffle.genre"/> + android:hint="@string/shuffle.genre" + android:textColor="?android:textColorPrimary"/> <Button android:id="@+id/genre_combo" diff --git a/app/src/main/res/layout/song_list_item.xml b/app/src/main/res/layout/song_list_item.xml index 86f77869..419f9524 100644 --- a/app/src/main/res/layout/song_list_item.xml +++ b/app/src/main/res/layout/song_list_item.xml @@ -3,7 +3,8 @@ android:id="@id/drag_handle" android:orientation="horizontal" android:layout_width="fill_parent" - android:layout_height="?android:attr/listPreferredItemHeight"> + android:layout_height="?android:attr/listPreferredItemHeight" + android:background="?attr/selectableItemBackground"> <CheckedTextView android:id="@+id/song_check" @@ -25,17 +26,18 @@ android:layout_gravity="center_vertical"> <TextView - android:id="@+id/song_title" - android:layout_width="0dip" - android:layout_height="wrap_content" - android:layout_weight="1" - android:layout_gravity="left|center_vertical" - android:textAppearance="?android:attr/textAppearanceMedium" - android:singleLine="true" - android:ellipsize="marquee" - android:drawablePadding="6dip" - android:paddingLeft="6dip" - android:paddingRight="6dip"/> + android:id="@+id/song_title" + android:layout_width="0dip" + android:layout_height="wrap_content" + android:layout_weight="1" + android:layout_gravity="left|center_vertical" + android:textAppearance="?android:attr/textAppearanceMedium" + android:singleLine="true" + android:ellipsize="marquee" + android:drawablePadding="6dip" + android:paddingLeft="6dip" + android:paddingRight="6dip" + android:textColor="?android:textColorPrimary"/> <ImageButton android:id="@+id/song_bookmark" @@ -91,7 +93,8 @@ android:textAppearance="?android:attr/textAppearanceSmall" android:singleLine="true" android:ellipsize="middle" - android:paddingLeft="6dip"/> + android:paddingLeft="6dip" + android:textColor="?android:textColorSecondary"/> <RatingBar android:id="@+id/song_rating" @@ -111,13 +114,14 @@ android:textAppearance="?android:attr/textAppearanceSmall" android:singleLine="true" android:paddingLeft="3dip" - android:paddingRight="4dip"/> + android:paddingRight="4dip" + android:textColor="?android:textColorSecondary"/> </LinearLayout> </LinearLayout> <ImageView - android:id="@+id/artist_more" + android:id="@+id/more_button" android:src="?attr/download_none" android:layout_width="wrap_content" android:layout_height="fill_parent" diff --git a/app/src/main/res/layout/start_timer.xml b/app/src/main/res/layout/start_timer.xml index 9736a31d..59bd60e3 100644 --- a/app/src/main/res/layout/start_timer.xml +++ b/app/src/main/res/layout/start_timer.xml @@ -11,7 +11,8 @@ android:layout_marginLeft="8dp" android:textSize="20dp" android:paddingRight="10px" - android:layout_gravity="center"/> + android:layout_gravity="center" + android:textColor="?android:textColorPrimary"/> <SeekBar android:id="@+id/timer_length_bar" diff --git a/app/src/main/res/layout/sync_dialog.xml b/app/src/main/res/layout/sync_dialog.xml index 5133b753..fc657186 100644 --- a/app/src/main/res/layout/sync_dialog.xml +++ b/app/src/main/res/layout/sync_dialog.xml @@ -8,5 +8,6 @@ android:layout_height="wrap_content" android:layout_margin="5dp" android:checked="false" - android:text="@string/offline.sync_dialog_default"/> + android:text="@string/offline.sync_dialog_default" + android:textColor="?android:textColorPrimary"/> </FrameLayout>
\ No newline at end of file diff --git a/app/src/main/res/layout/tab_progress.xml b/app/src/main/res/layout/tab_progress.xml index f6d326ca..7423777f 100644 --- a/app/src/main/res/layout/tab_progress.xml +++ b/app/src/main/res/layout/tab_progress.xml @@ -26,6 +26,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" - android:textAppearance="?android:attr/textAppearanceMedium"/> + android:textAppearance="?android:attr/textAppearanceMedium" + android:textColor="?android:textColorPrimary"/> </LinearLayout> </FrameLayout>
\ No newline at end of file diff --git a/app/src/main/res/layout/unscrollable_grid_view.xml b/app/src/main/res/layout/unscrollable_grid_view.xml deleted file mode 100644 index 96bea5ce..00000000 --- a/app/src/main/res/layout/unscrollable_grid_view.xml +++ /dev/null @@ -1,11 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<github.daneren2005.dsub.view.UnscrollableGridView xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/gridview" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:numColumns="@integer/Grid.Columns" - android:horizontalSpacing="10dp" - android:verticalSpacing="10dp" - android:gravity="center" - android:padding="20px" - android:stretchMode="columnWidth"/>
\ No newline at end of file diff --git a/app/src/main/res/layout/update_playlist.xml b/app/src/main/res/layout/update_playlist.xml index 7354ef5c..cc7e5ee6 100644 --- a/app/src/main/res/layout/update_playlist.xml +++ b/app/src/main/res/layout/update_playlist.xml @@ -22,7 +22,8 @@ android:layout_height="wrap_content" android:layout_weight="1" android:layout_marginLeft="4dp" - android:hint="@string/common.name" /> + android:hint="@string/common.name" + android:textColor="?android:textColorPrimary"/> </LinearLayout> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" @@ -36,7 +37,8 @@ android:layout_height="wrap_content" android:layout_marginLeft="4dp" android:textSize="20dp" - android:text="@string/common.comment" /> + android:text="@string/common.comment" + android:textColor="?android:textColorPrimary"/> <EditText android:id="@+id/get_playlist_comment" android:inputType="text" @@ -58,7 +60,8 @@ android:layout_height="wrap_content" android:layout_marginLeft="4dp" android:textSize="20dp" - android:text="@string/common.public" /> + android:text="@string/common.public" + android:textColor="?android:textColorPrimary"/> <CheckBox android:id="@+id/get_playlist_public" android:layout_width="fill_parent" diff --git a/app/src/main/res/layout/update_share.xml b/app/src/main/res/layout/update_share.xml index 92b7137b..ef44e304 100644 --- a/app/src/main/res/layout/update_share.xml +++ b/app/src/main/res/layout/update_share.xml @@ -14,7 +14,8 @@ android:layout_height="wrap_content" android:layout_marginLeft="4dp" android:textSize="20dp" - android:text="@string/common.name" /> + android:text="@string/common.name" + android:textColor="?android:textColorPrimary"/> <EditText android:id="@+id/get_share_name" android:inputType="text" @@ -36,7 +37,8 @@ android:layout_height="wrap_content" android:layout_marginLeft="4dp" android:textSize="20dp" - android:text="@string/share.expiration" /> + android:text="@string/share.expiration" + android:textColor="?android:textColorPrimary"/> <DatePicker android:id="@+id/get_share_expire" android:layout_width="fill_parent" @@ -57,7 +59,8 @@ android:layout_height="wrap_content" android:layout_marginLeft="4dp" android:textSize="20dp" - android:text="@string/share.no_expiration" /> + android:text="@string/share.no_expiration" + android:textColor="?android:textColorPrimary"/> <CheckBox android:id="@+id/get_share_no_expire" android:layout_width="fill_parent" diff --git a/app/src/main/res/layout/user_header.xml b/app/src/main/res/layout/user_header.xml index 0b303afe..c8186372 100644 --- a/app/src/main/res/layout/user_header.xml +++ b/app/src/main/res/layout/user_header.xml @@ -34,7 +34,8 @@ android:marqueeRepeatLimit="marquee_forever" android:scrollHorizontally="true" android:focusable="true" - android:focusableInTouchMode="true"> + android:focusableInTouchMode="true" + android:textColor="?android:textColorPrimary"> <requestFocus android:focusable="true" android:focusableInTouchMode="true" @@ -49,9 +50,16 @@ android:textAppearance="?android:attr/textAppearanceMedium" android:singleLine="true" android:ellipsize="end" - android:autoLink="email"/> + android:autoLink="email" + android:textColor="?android:textColorSecondary"/> </LinearLayout> + + <View + android:layout_width="fill_parent" + android:layout_height="1px" + android:background="?attr/colorPrimary" + android:layout_alignParentBottom="true"/> </RelativeLayout> diff --git a/app/src/main/res/layout/user_list_item.xml b/app/src/main/res/layout/user_list_item.xml index dc2bdab9..aa198578 100644 --- a/app/src/main/res/layout/user_list_item.xml +++ b/app/src/main/res/layout/user_list_item.xml @@ -3,7 +3,7 @@ android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="wrap_content" - android:background="@android:color/transparent"> + android:background="?attr/selectableItemBackground"> <github.daneren2005.dsub.view.RecyclingImageView android:id="@+id/item_avatar" @@ -22,7 +22,8 @@ android:paddingLeft="12dip" android:paddingRight="6dip" android:minHeight="50dip" - android:background="@android:color/transparent"/> + android:background="@android:color/transparent" + android:textColor="?android:textColorPrimary"/> <ImageButton android:id="@+id/item_star" diff --git a/app/src/main/res/menu/drawer_navigation.xml b/app/src/main/res/menu/drawer_navigation.xml new file mode 100644 index 00000000..88ecd920 --- /dev/null +++ b/app/src/main/res/menu/drawer_navigation.xml @@ -0,0 +1,51 @@ +<?xml version="1.0" encoding="utf-8"?> +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + <group android:checkableBehavior="single"> + <item + android:id="@+id/drawer_home" + android:icon="?attr/drawerHome" + android:title="@string/button_bar.home"/> + <item + android:id="@+id/drawer_library" + android:icon="?attr/drawerLibrary" + android:title="@string/button_bar.browse"/> + <item + android:id="@+id/drawer_playlists" + android:icon="?attr/drawerPlaylists" + android:title="@string/button_bar.playlists"/> + <item + android:id="@+id/drawer_podcasts" + android:icon="?attr/drawerPodcasts" + android:title="@string/button_bar.podcasts"/> + <item + android:id="@+id/drawer_bookmarks" + android:icon="?attr/drawerBookmarks" + android:title="@string/button_bar.bookmarks"/> + <item + android:id="@+id/drawer_shares" + android:icon="?attr/drawerShares" + android:title="@string/button_bar.shares"/> + <item + android:id="@+id/drawer_chat" + android:icon="?attr/drawerChat" + android:title="@string/button_bar.chat"/> + <item + android:id="@+id/drawer_admin" + android:icon="?attr/drawerAdmin" + android:title="@string/button_bar.admin"/> + <item + android:id="@+id/drawer_downloading" + android:icon="?attr/drawerDownloading" + android:title="@string/button_bar.downloading" + android:visible="false"/> + </group> + + <group + android:id="@+id/drawer_bottom" + android:checkableBehavior="single"> + + <item + android:id="@+id/drawer_settings" + android:title="@string/menu.settings"/> + </group> +</menu>
\ No newline at end of file diff --git a/app/src/main/res/menu/select_album.xml b/app/src/main/res/menu/select_album.xml index 3d2228e8..c94a4a3f 100644 --- a/app/src/main/res/menu/select_album.xml +++ b/app/src/main/res/menu/select_album.xml @@ -3,7 +3,7 @@ xmlns:compat="http://schemas.android.com/apk/res-auto"> <item android:id="@+id/menu_play_now" - android:icon="?media_button_start" + android:icon="?actionbar_start" android:title="@string/menu.play" compat:showAsAction="always|withText"/> diff --git a/app/src/main/res/menu/select_album_list.xml b/app/src/main/res/menu/select_album_list.xml index a9196d1c..17148a51 100644 --- a/app/src/main/res/menu/select_album_list.xml +++ b/app/src/main/res/menu/select_album_list.xml @@ -3,7 +3,7 @@ xmlns:compat="http://schemas.android.com/apk/res-auto"> <item android:id="@+id/menu_play_now" - android:icon="?media_button_start" + android:icon="?actionbar_start" android:title="@string/menu.play" compat:showAsAction="always|withText"/> diff --git a/app/src/main/res/menu/select_song.xml b/app/src/main/res/menu/select_song.xml index fc4494cb..d47ad933 100644 --- a/app/src/main/res/menu/select_song.xml +++ b/app/src/main/res/menu/select_song.xml @@ -3,7 +3,7 @@ xmlns:compat="http://schemas.android.com/apk/res-auto"> <item android:id="@+id/menu_play_now" - android:icon="?attr/media_button_start" + android:icon="?attr/actionbar_start" android:title="@string/menu.play" compat:showAsAction="always|withText"/> diff --git a/app/src/main/res/menu/select_song_offline.xml b/app/src/main/res/menu/select_song_offline.xml index c45405fb..7a4a5407 100644 --- a/app/src/main/res/menu/select_song_offline.xml +++ b/app/src/main/res/menu/select_song_offline.xml @@ -3,7 +3,7 @@ xmlns:compat="http://schemas.android.com/apk/res-auto"> <item android:id="@+id/menu_play_now" - android:icon="?attr/media_button_start" + android:icon="?attr/actionbar_start" android:title="@string/menu.play" compat:showAsAction="always|withText"/> diff --git a/app/src/main/res/menu/similar_artists.xml b/app/src/main/res/menu/similar_artists.xml index 771555b6..f6c30fb2 100644 --- a/app/src/main/res/menu/similar_artists.xml +++ b/app/src/main/res/menu/similar_artists.xml @@ -4,7 +4,7 @@ <item android:id="@+id/menu_play_now" - android:icon="?media_button_start" + android:icon="?actionbar_start" android:title="@string/menu.play" compat:showAsAction="always|withText"/> diff --git a/app/src/main/res/values-large/dimens.xml b/app/src/main/res/values-large/dimens.xml index b08dda86..fe8f1a6c 100644 --- a/app/src/main/res/values-large/dimens.xml +++ b/app/src/main/res/values-large/dimens.xml @@ -4,4 +4,6 @@ <dimen name="Button.Small">54dip</dimen> <dimen name="AlbumArt.Small">96dip</dimen> <dimen name="AlbumArt.Header">210dip</dimen> + <dimen name="BottomBar.Text.Major">18sp</dimen> + <dimen name="BottomBar.Text.Minor">16sp</dimen> </resources>
\ No newline at end of file diff --git a/app/src/main/res/values-v16/themes.xml b/app/src/main/res/values-v16/themes.xml deleted file mode 100644 index 013ac0aa..00000000 --- a/app/src/main/res/values-v16/themes.xml +++ /dev/null @@ -1,15 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<resources> - <style name="DSub.TextViewStyle" parent="android:Widget.TextView"> - <item name="android:fontFamily">sans-serif-light</item> - </style> - - <style name="DSub.TextViewStyle.Bold" parent="android:Widget.TextView"> - <item name="android:fontFamily">sans-serif</item> - <item name="android:textStyle">bold</item> - </style> - - <style name="DSub.ButtonStyle" parent="android:Widget.Holo.Button"> - <item name="android:fontFamily">sans-serif-light</item> - </style> -</resources>
\ No newline at end of file diff --git a/app/src/main/res/values-v21/themes.xml b/app/src/main/res/values-v21/themes.xml new file mode 100644 index 00000000..acd117b5 --- /dev/null +++ b/app/src/main/res/values-v21/themes.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <style name="Theme.DSub.Light" parent="Theme.DSub.Light.Base"> + <item name="android:windowTranslucentStatus">true</item> + </style> + + <style name="Theme.DSub.Dark" parent="Theme.DSub.Dark.Base"> + <item name="android:windowTranslucentStatus">true</item> + </style> +</resources>
\ No newline at end of file diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index 37b15d12..d876ecdb 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -1,18 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> <resources> - <string-array name="drawerItems"> - <item>@string/button_bar.home</item> - <item>@string/button_bar.browse</item> - <item>@string/button_bar.playlists</item> - <item>@string/button_bar.podcasts</item> - <item>@string/button_bar.bookmarks</item> - <item>@string/button_bar.shares</item> - <item>@string/button_bar.chat</item> - <item>@string/button_bar.admin</item> - <item>@string/button_bar.downloading</item> - <item>@string/menu.settings</item> - </string-array> - <string-array name="drawerItemsDescriptions"> <item>Home</item> <item>Artist</item> @@ -46,32 +33,6 @@ <item>Chat</item> </string-array> - <array name="drawerItemIconsLight"> - <item>@drawable/main_offline_light</item> - <item>@drawable/ic_menu_library_light</item> - <item>@drawable/ic_menu_playlist_light</item> - <item>@drawable/ic_menu_podcast_light</item> - <item>@drawable/ic_menu_bookmark_light</item> - <item>@drawable/ic_menu_share_light</item> - <item>@drawable/ic_menu_chat_light</item> - <item>@drawable/ic_menu_admin_light</item> - <item>@drawable/ic_menu_download_light</item> - <item>@drawable/ic_menu_settings_light</item> - </array> - - <array name="drawerItemIconsDark"> - <item>@drawable/main_offline_dark</item> - <item>@drawable/ic_menu_library_dark</item> - <item>@drawable/ic_menu_playlist_dark</item> - <item>@drawable/ic_menu_podcast_dark</item> - <item>@drawable/ic_menu_bookmark_dark</item> - <item>@drawable/ic_menu_share_dark</item> - <item>@drawable/ic_menu_chat_dark</item> - <item>@drawable/ic_menu_admin_dark</item> - <item>@drawable/ic_menu_download_dark</item> - <item>@drawable/ic_menu_settings_dark</item> - </array> - <string-array name="themeValues"> <item>light</item> <item>dark</item> diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml index 9667117c..2d2ebd7d 100644 --- a/app/src/main/res/values/attrs.xml +++ b/app/src/main/res/values/attrs.xml @@ -7,10 +7,16 @@ <attr name="media_button_repeat_off" format="reference"/> <attr name="media_button_start" format="reference"/> <attr name="media_button_stop" format="reference"/> + <attr name="actionbar_backward" format="reference"/> + <attr name="actionbar_forward" format="reference"/> + <attr name="actionbar_pause" format="reference"/> + <attr name="actionbar_start" format="reference"/> + <attr name="actionbar_stop" format="reference"/> <attr name="chat_send" format="reference"/> <attr name="add" format="reference"/> <attr name="download_none" format="reference"/> <attr name="shuffle" format="reference"/> + <attr name="shuffle_button" format="reference"/> <attr name="refresh" format="reference"/> <attr name="search" format="reference"/> <attr name="remove" format="reference"/> @@ -27,6 +33,19 @@ <attr name="rating_good" format="reference"/> <attr name="radio" format="reference"/> <attr name="drawerItemsIcons" format="reference"/> + <attr name="drawerHome" format="reference"/> + <attr name="drawerLibrary" format="reference"/> + <attr name="drawerPlaylists" format="reference"/> + <attr name="drawerPodcasts" format="reference"/> + <attr name="drawerBookmarks" format="reference"/> + <attr name="drawerShares" format="reference"/> + <attr name="drawerChat" format="reference"/> + <attr name="drawerAdmin" format="reference"/> + <attr name="drawerDownloading" format="reference"/> + <attr name="drawerSettings" format="reference"/> + <attr name="actionbarTitleStyle" format="reference"/> + <attr name="actionbarSubtitleStyle" format="reference"/> + <attr name="actionbarPopupStyle" format="reference"/> <declare-styleable name="SeekBarPreference"> <attr name="min" format="integer"/> diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index b1422ad6..e35d0311 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -1,17 +1,24 @@ <?xml version="1.0" encoding="utf-8"?> <resources> - <color name="lightBackground">#F1F0E6</color> - <color name="dividerColor">#FF33B5E5</color> <color name="appwidget_text">#FFFFFF</color> <color name="notificationArtist">#434343</color> <color name="notificationTitle">#000000</color> - <color name="background_holo_light">#ff33b5e5</color> <color name="overlayColor">#80000000</color> - <color name="ics_opaque">#8033b5e5</color> - <color name="cyan">#ff0099cc</color> <color name="holo_blue_light">#ff33b5e5</color> <color name="holo_orange_light">#ffffbb33</color> <color name="holo_green_light">#ff99cc00</color> <color name="holo_red_light">#ffff4444</color> + + <color name="lightPrimary">#2196f3</color> + <color name="lightPrimaryDark">#1e88e5</color> + <color name="lightAccent">#448aff</color> + + <color name="darkPrimary">#f44336</color> + <color name="darkPrimaryDark">#e53935</color> + <color name="darkAccent">#ff5252</color> + + <color name="holoPrimary">#009688</color> + <color name="holoPrimaryDark">#00897b</color> + <color name="holoAccent">#64ffda</color> </resources>
\ No newline at end of file diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index be3e843d..9759e1c2 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -4,4 +4,6 @@ <dimen name="Button.Small">46dip</dimen> <dimen name="AlbumArt.Small">78dip</dimen> <dimen name="AlbumArt.Header">120dip</dimen> + <dimen name="BottomBar.Text.Major">13sp</dimen> + <dimen name="BottomBar.Text.Minor">12sp</dimen> </resources>
\ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8544c6e3..bb0a997b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -79,6 +79,8 @@ <string name="main.songs_genres">@string/main.albums_genres</string> <string name="main.back_confirm">Press back again to exit</string> <string name="main.scan_complete">Completed scan of Server</string> + <string name="main.artist">Artist</string> + <string name="main.title">Title</string> <string name="menu.search">Search</string> <string name="menu.shuffle">Shuffle</string> @@ -234,7 +236,9 @@ <string name="starring_content_starred">Starred \"%s\"</string> <string name="starring_content_unstarred">Unstarred \"%s\"</string> <string name="starring_content_error">Failed to update \"%s\", please try later.</string> - + + <string name="playlist.mine">My Playlists</string> + <string name="playlist.shared">Shared Playlists</string> <string name="playlist_error">Failed to grab list of playlists</string> <string name="updated_playlist">Added %1$s songs to \"%2$s\"</string> <string name="updated_playlist_error">Failed to update \"%s\", please try later.</string> diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 43271afd..83fa4a42 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="utf-8"?> <resources> <style name="BasicButton"> - <item name="android:background">@drawable/abc_item_background_holo_light</item> + <item name="android:background">?attr/selectableItemBackground</item> </style> <style name="MoreButton" parent="BasicButton"> @@ -23,15 +23,12 @@ <item name="android:layout_width">@dimen/Button.Small</item> <item name="android:layout_height">@dimen/Button.Small</item> </style> - - <style name="MenuBarButton" parent="@style/BasicButton"> - <item name="android:layout_width">0dip</item> - <item name="android:layout_height">45dip</item> - <item name="android:layout_weight">1</item> - <item name="android:textSize">14sp</item> - <item name="android:textStyle">bold</item> - <item name="android:textColor">?android:textColorPrimary</item> - </style> + + <style name="PlaybackControl.Match" parent="@style/PlaybackControl"> + <item name="android:padding">4dip</item> + <item name="android:layout_height">match_parent</item> + <item name="android:layout_width">wrap_content</item> + </style> <style name="DownloadActionButton" parent="@style/BasicButton"> <item name="android:layout_width">wrap_content</item> @@ -52,44 +49,4 @@ <item name="android:paddingRight">16dip</item> <item name="android:paddingLeft">16dip</item> </style> - - <style name="DragDropListView"> - <item name="drag_enabled">true</item> - <item name="collapsed_height">1dp</item> - <item name="drag_scroll_start">1.0</item> - <item name="max_drag_scroll_speed">2.0</item> - <item name="float_alpha">0.6</item> - <item name="slide_shuffle_speed">0.3</item> - <item name="track_drag_sort">false</item> - <item name="use_default_controller">true</item> - <item name="drag_handle_id">@id/drag_handle</item> - <item name="sort_enabled">true</item> - <item name="remove_enabled">false</item> - <item name="remove_mode">flingRemove</item> - <item name="drag_start_mode">onLongPress</item> - <item name="float_background_color">@android:color/transparent</item> - </style> - - <style name="MainAlbumButton"> - <item name="android:drawablePadding">6dip</item> - <item name="android:layout_width">fill_parent</item> - <item name="android:layout_height">wrap_content</item> - <item name="android:textAppearance">?android:attr/textAppearanceMedium</item> - <item name="android:gravity">center_vertical</item> - <item name="android:paddingLeft">6dip</item> - <item name="android:paddingRight">6dip</item> - <item name="android:minHeight">46dip</item> - </style> - - <style name="MainAlbumButtonLabel"> - <item name="android:layout_width">fill_parent</item> - <item name="android:layout_height">wrap_content</item> - <item name="android:textAppearance">?android:attr/textAppearanceSmall</item> - <item name="android:textColor">@color/cyan</item> - <item name="android:gravity">center_vertical</item> - <item name="android:paddingLeft">6dp</item> - <item name="android:textAllCaps">true</item> - <item name="android:textStyle">bold</item> - <item name="android:textSize">16sp</item> - </style> </resources>
\ No newline at end of file diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index 78a2c34d..fb47af81 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -1,8 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <resources> - <style name="Theme.DSub.Light" parent="@style/Theme.AppCompat.Light"> - <item name="actionBarStyle">@style/Widget.DSub.ActionBarStyle.Light</item> - <item name="android:actionBarStyle">@style/Widget.DSub.ActionBarStyle.Light</item> + <style name="Theme.DSub.Light.Base" parent="@style/Theme.AppCompat.Light.DarkActionBar"> <item name="offline_icon">@drawable/main_offline_light</item> <item name="media_button_backward">@drawable/media_backward_light</item> <item name="media_button_forward">@drawable/media_forward_light</item> @@ -10,34 +8,51 @@ <item name="media_button_repeat_off">@drawable/media_repeat_off_light</item> <item name="media_button_start">@drawable/media_start_light</item> <item name="media_button_stop">@drawable/media_stop_light</item> + <item name="actionbar_backward">@drawable/media_backward_dark</item> + <item name="actionbar_forward">@drawable/media_forward_dark</item> + <item name="actionbar_pause">@drawable/media_pause_dark</item> + <item name="actionbar_start">@drawable/media_start_dark</item> + <item name="actionbar_stop">@drawable/media_stop_dark</item> <item name="chat_send">@drawable/ic_menu_chat_send_light</item> - <item name="add">@drawable/ic_action_add_light</item> + <item name="add">@drawable/ic_action_add_dark</item> <item name="download_none">@drawable/download_none_light</item> - <item name="shuffle">@drawable/ic_menu_shuffle_light</item> - <item name="refresh">@drawable/ic_menu_refresh_light</item> - <item name="search">@drawable/ic_menu_search_light</item> - <item name="remove">@drawable/ic_menu_remove_light</item> - <item name="save">@drawable/ic_menu_save_light</item> + <item name="shuffle">@drawable/ic_menu_shuffle_dark</item> + <item name="shuffle_button">@drawable/ic_menu_shuffle_light</item> + <item name="refresh">@drawable/ic_menu_refresh_dark</item> + <item name="search">@drawable/ic_menu_search_dark</item> + <item name="remove">@drawable/ic_menu_remove_dark</item> + <item name="save">@drawable/ic_menu_save_dark</item> <item name="volume">@drawable/ic_action_volume_light</item> <item name="toggle_list">@drawable/action_toggle_list_light</item> <item name="select_server">@drawable/main_select_server_light</item> <item name="downloading">@drawable/downloading_light</item> <item name="bookmark">@drawable/ic_menu_bookmark_light</item> <item name="share">@drawable/ic_menu_share_light</item> - <item name="add_person">@drawable/ic_menu_add_person_light</item> - <item name="password">@drawable/ic_menu_password_light</item> + <item name="add_person">@drawable/ic_menu_add_person_dark</item> + <item name="password">@drawable/ic_menu_password_dark</item> <item name="rating_bad">@drawable/ic_action_rating_bad_light</item> <item name="rating_good">@drawable/ic_action_rating_good_light</item> - <item name="radio">@drawable/ic_menu_radio_light</item> - <item name="drawerItemsIcons">@array/drawerItemIconsLight</item> - <item name="android:textViewStyle">@style/DSub.TextViewStyle</item> - <item name="android:buttonStyle">@style/DSub.ButtonStyle.Light</item> + <item name="radio">@drawable/ic_menu_radio_dark</item> + <item name="drawerHome">@drawable/main_offline_light</item> + <item name="drawerLibrary">@drawable/ic_menu_library_light</item> + <item name="drawerPlaylists">@drawable/ic_menu_playlist_light</item> + <item name="drawerPodcasts">@drawable/ic_menu_podcast_light</item> + <item name="drawerBookmarks">@drawable/ic_menu_bookmark_light</item> + <item name="drawerShares">@drawable/ic_menu_share_light</item> + <item name="drawerChat">@drawable/ic_menu_chat_light</item> + <item name="drawerAdmin">@drawable/ic_menu_admin_light</item> + <item name="drawerDownloading">@drawable/ic_menu_download_light</item> + <item name="drawerSettings">@drawable/ic_menu_settings_light</item> <item name="drawerArrowStyle">@style/DSub.DrawerArrow</item> - <item name="colorAccent">@color/cyan</item> + <item name="colorPrimary">@color/lightPrimary</item> + <item name="colorPrimaryDark">@color/lightPrimaryDark</item> + <item name="colorAccent">@color/lightAccent</item> + <item name="actionbarTitleStyle">@style/TextAppearance.AppCompat.Widget.ActionBar.Title.Inverse</item> + <item name="actionbarSubtitleStyle">@style/TextAppearance.AppCompat.Widget.ActionBar.Subtitle.Inverse</item> + <item name="actionbarPopupStyle">@style/ThemeOverlay.AppCompat.Light</item> + <item name="android:spinnerItemStyle">@style/DarkSpinnerItem</item> </style> - <style name="Theme.DSub.Dark" parent="@style/Theme.AppCompat"> - <item name="actionBarStyle">@style/Widget.DSub.ActionBarStyle.Dark</item> - <item name="android:actionBarStyle">@style/Widget.DSub.ActionBarStyle.Dark</item> + <style name="Theme.DSub.Dark.Base" parent="@style/Theme.AppCompat"> <item name="offline_icon">@drawable/main_offline_dark</item> <item name="media_button_backward">@drawable/media_backward_dark</item> <item name="media_button_forward">@drawable/media_forward_dark</item> @@ -45,10 +60,16 @@ <item name="media_button_repeat_off">@drawable/media_repeat_off</item> <item name="media_button_start">@drawable/media_start_dark</item> <item name="media_button_stop">@drawable/media_stop_dark</item> + <item name="actionbar_backward">@drawable/media_backward_dark</item> + <item name="actionbar_forward">@drawable/media_forward_dark</item> + <item name="actionbar_pause">@drawable/media_pause_dark</item> + <item name="actionbar_start">@drawable/media_start_dark</item> + <item name="actionbar_stop">@drawable/media_stop_dark</item> <item name="chat_send">@drawable/ic_menu_chat_send_dark</item> <item name="add">@drawable/ic_action_add_dark</item> <item name="download_none">@drawable/download_none_dark</item> <item name="shuffle">@drawable/ic_menu_shuffle_dark</item> + <item name="shuffle_button">@drawable/ic_menu_shuffle_dark</item> <item name="refresh">@drawable/ic_menu_refresh_dark</item> <item name="search">@drawable/ic_menu_search_dark</item> <item name="remove">@drawable/ic_menu_remove_dark</item> @@ -64,46 +85,60 @@ <item name="rating_bad">@drawable/ic_action_rating_bad_dark</item> <item name="rating_good">@drawable/ic_action_rating_good_dark</item> <item name="radio">@drawable/ic_menu_radio_dark</item> - <item name="drawerItemsIcons">@array/drawerItemIconsDark</item> - <item name="android:textViewStyle">@style/DSub.TextViewStyle</item> - <item name="android:buttonStyle">@style/DSub.ButtonStyle.Dark</item> + <item name="drawerHome">@drawable/main_offline_dark</item> + <item name="drawerLibrary">@drawable/ic_menu_library_dark</item> + <item name="drawerPlaylists">@drawable/ic_menu_playlist_dark</item> + <item name="drawerPodcasts">@drawable/ic_menu_podcast_dark</item> + <item name="drawerBookmarks">@drawable/ic_menu_bookmark_dark</item> + <item name="drawerShares">@drawable/ic_menu_share_dark</item> + <item name="drawerChat">@drawable/ic_menu_chat_dark</item> + <item name="drawerAdmin">@drawable/ic_menu_admin_dark</item> + <item name="drawerDownloading">@drawable/ic_menu_download_dark</item> + <item name="drawerSettings">@drawable/ic_menu_settings_dark</item> <item name="drawerArrowStyle">@style/DSub.DrawerArrow</item> - <item name="colorAccent">@color/cyan</item> + <item name="colorPrimary">@color/darkPrimary</item> + <item name="colorPrimaryDark">@color/darkPrimaryDark</item> + <item name="colorAccent">@color/darkAccent</item> + <item name="actionbarTitleStyle">@style/TextAppearance.AppCompat.Widget.ActionBar.Title</item> + <item name="actionbarSubtitleStyle">@style/TextAppearance.AppCompat.Widget.ActionBar.Subtitle</item> + <item name="actionbarPopupStyle">@style/ThemeOverlay.AppCompat.Dark</item> + </style> + <style name="Theme.DSub.Light" parent="Theme.DSub.Light.Base"> + </style> + <style name="Theme.DSub.Dark" parent="Theme.DSub.Dark.Base"> </style> <style name="Theme.DSub.Black" parent="Theme.DSub.Dark"> <item name="android:windowBackground">@android:color/black</item> </style> <style name="Theme.DSub.Holo" parent="Theme.DSub.Dark"> <item name="android:windowBackground">@drawable/background</item> - </style> - - <style name="Widget.DSub.ActionBarStyle.Light" parent="Widget.AppCompat.Light.ActionBar.Solid"> - <item name="background">@android:color/transparent</item> - <item name="android:background">@android:color/transparent</item> - <item name="backgroundStacked">@android:color/transparent</item> - <item name="android:backgroundStacked">@android:color/transparent</item> + <item name="colorPrimary">@color/holoPrimary</item> + <item name="colorPrimaryDark">@color/holoPrimaryDark</item> + <item name="colorAccent">@color/holoAccent</item> </style> - <style name="Widget.DSub.ActionBarStyle.Dark" parent="Widget.AppCompat.ActionBar.Solid"> - <item name="background">@android:color/transparent</item> - <item name="android:background">@android:color/transparent</item> - <item name="backgroundStacked">@android:color/transparent</item> - <item name="android:backgroundStacked">@android:color/transparent</item> + <style name="Theme.DSub.Light.No_Actionbar" parent="Theme.DSub.Light"> + <item name="windowActionBar">false</item> + <item name="windowNoTitle">true</item> </style> - - <style name="DSub.TextViewStyle" parent="android:Widget.TextView"> + <style name="Theme.DSub.Black.No_Actionbar" parent="Theme.DSub.Black"> + <item name="windowActionBar">false</item> + <item name="windowNoTitle">true</item> </style> - - <style name="DSub.TextViewStyle.Bold" parent="android:Widget.TextView"> - <item name="android:textStyle">bold</item> + <style name="Theme.DSub.Dark.No_Actionbar" parent="Theme.DSub.Dark"> + <item name="windowActionBar">false</item> + <item name="windowNoTitle">true</item> </style> - - <style name="DSub.ButtonStyle.Dark" parent="android:Widget.Holo.Button"> - </style> - <style name="DSub.ButtonStyle.Light" parent="android:Widget.Holo.Light.Button"> + <style name="Theme.DSub.Holo.No_Actionbar" parent="Theme.DSub.Holo"> + <item name="windowActionBar">false</item> + <item name="windowNoTitle">true</item> </style> <style name="DSub.DrawerArrow" parent="Widget.AppCompat.DrawerArrowToggle"> <item name="spinBars">true</item> </style> + + <style name="DarkSpinnerItem" parent="Widget.AppCompat.TextView.SpinnerItem"> + <item name="android:textColor">?android:attr/textColorPrimaryInverse</item> + </style> </resources> |