From 71144b1f8ee91ddc07854366a7638a25d0238a26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=B4=D0=BE=D0=BD=D0=B0=D0=B9=20=D0=AD=D0=BB=D0=BE?= =?UTF-8?q?=D1=85=D0=B8=D0=BC?= Date: Sat, 16 Jul 2016 19:54:13 +0300 Subject: [PATCH] Implement scroll-to-song and option in menu --- res/values-ru/translatable.xml | 2 + res/values/translatable.xml | 3 ++ res/xml/preference_library.xml | 5 +++ .../android/vanilla/LibraryActivity.java | 13 ++----- .../android/vanilla/LibraryPagerAdapter.java | 38 ++++++++++++++++++- .../android/vanilla/MediaUtils.java | 38 +++++++++++++++---- .../android/vanilla/PrefDefaults.java | 1 + .../android/vanilla/PrefKeys.java | 3 +- 8 files changed, 84 insertions(+), 19 deletions(-) diff --git a/res/values-ru/translatable.xml b/res/values-ru/translatable.xml index d6379f0a..053ece1a 100644 --- a/res/values-ru/translatable.xml +++ b/res/values-ru/translatable.xml @@ -189,6 +189,8 @@ Задействовать таймер простоя Воспроизведение остановится после определенного периода бездействия Время ожидания + Прокрутка до песни + Прокручивать до текущей песни/альбома/исполнителя в списках библиотеки Загружать обложки из баз Android Запрашивать обложки из встроенной базы медиа данных ОС Android Загружать обложки из папки diff --git a/res/values/translatable.xml b/res/values/translatable.xml index ad4bf1e2..a8db2288 100644 --- a/res/values/translatable.xml +++ b/res/values/translatable.xml @@ -215,6 +215,9 @@ THE SOFTWARE. When active, playback will be stopped after the given period of inactivity Idle timeout + Scroll to song title + Scroll to currently playing song/album/artist in library lists + Load artwork from Android Query Androids internal media database for album artwork diff --git a/res/xml/preference_library.xml b/res/xml/preference_library.xml index 4fa00e2d..6aa9440e 100644 --- a/res/xml/preference_library.xml +++ b/res/xml/preference_library.xml @@ -42,6 +42,11 @@ THE SOFTWARE. android:entries="@array/default_playlist_action_entries" android:entryValues="@array/default_playlist_action_entry_values" android:defaultValue="0" /> + diff --git a/src/ch/blinkenlights/android/vanilla/LibraryActivity.java b/src/ch/blinkenlights/android/vanilla/LibraryActivity.java index 3204d3ea..90aaffab 100644 --- a/src/ch/blinkenlights/android/vanilla/LibraryActivity.java +++ b/src/ch/blinkenlights/android/vanilla/LibraryActivity.java @@ -28,39 +28,30 @@ import android.content.ContentResolver; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; -import android.content.pm.ApplicationInfo; import android.content.res.Resources; import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.Color; import android.graphics.drawable.PaintDrawable; import android.net.Uri; -import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.provider.MediaStore; import android.support.iosched.tabs.VanillaTabLayout; import android.support.v4.view.ViewPager; -import android.text.Editable; import android.text.TextUtils; import android.view.ContextMenu; import android.view.KeyEvent; import android.view.Menu; import android.view.MenuItem; -import android.view.SubMenu; import android.view.View; import android.view.ViewGroup; -import android.view.ViewParent; -import android.view.inputmethod.InputMethodManager; import android.widget.CheckBox; import android.widget.HorizontalScrollView; -import android.widget.ImageButton; -import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.ListView; import android.widget.TextView; -import android.widget.Toast; import android.widget.SearchView; import java.io.File; @@ -872,8 +863,10 @@ public class LibraryActivity super.onSongChange(song); mBottomBarControls.setSong(song); - if (song != null) + if (song != null) { mHandler.sendMessage(mHandler.obtainMessage(MSG_UPDATE_COVER, song)); + mPagerAdapter.onSongChange(song); + } } @Override diff --git a/src/ch/blinkenlights/android/vanilla/LibraryPagerAdapter.java b/src/ch/blinkenlights/android/vanilla/LibraryPagerAdapter.java index 13b9773f..65d23aba 100644 --- a/src/ch/blinkenlights/android/vanilla/LibraryPagerAdapter.java +++ b/src/ch/blinkenlights/android/vanilla/LibraryPagerAdapter.java @@ -26,7 +26,7 @@ package ch.blinkenlights.android.vanilla; import android.content.Intent; import android.content.SharedPreferences; import android.database.ContentObserver; -import android.os.Build; +import android.graphics.drawable.GradientDrawable; import android.os.Bundle; import android.os.Handler; import android.os.Looper; @@ -35,6 +35,7 @@ import android.os.Parcelable; import android.provider.MediaStore; import android.support.v4.view.PagerAdapter; import android.support.v4.view.ViewPager; +import android.util.TypedValue; import android.view.ContextMenu; import android.view.ContextMenu.ContextMenuInfo; import android.view.LayoutInflater; @@ -48,6 +49,8 @@ import android.widget.TextView; import android.widget.LinearLayout; import java.util.Arrays; +import static android.graphics.drawable.GradientDrawable.Orientation.RIGHT_LEFT; + /** * PagerAdapter that manages the library media ListViews. */ @@ -851,6 +854,39 @@ public class LibraryPagerAdapter } } + /** + * Perform usability-related actions on pager and contained lists, e.g. highlight current song + * or scroll to it if opted-in + * @param song song that is currently playing, can be null + */ + public void onSongChange(Song song) { + int type = mTabOrder[mCurrentPage]; + long id = MediaUtils.getCurrentIdForType(song, type); + if (id == -1) // unknown type + return; + + ListView view = mLists[type]; + if (view == null) // not initialized yet, nothing to do + return; + + // scroll to song on song change if opted-in + SharedPreferences sharedPrefs = PlaybackService.getSettings(mActivity); + boolean shouldScroll = sharedPrefs.getBoolean(PrefKeys.ENABLE_SCROLL_TO_SONG, PrefDefaults.ENABLE_SCROLL_TO_SONG); + if(shouldScroll) { + int middlePos = (view.getFirstVisiblePosition() + view.getLastVisiblePosition()) / 2; + for (int pos = 0; pos < view.getCount(); pos++) { + if (view.getItemIdAtPosition(pos) == id) { + if (Math.abs(middlePos - pos) < 30) { + view.smoothScrollToPosition(pos); + } else { + view.setSelection(pos); + } + break; + } + } + } + } + /** * LRU implementation: saves the adapter position */ diff --git a/src/ch/blinkenlights/android/vanilla/MediaUtils.java b/src/ch/blinkenlights/android/vanilla/MediaUtils.java index 098581bc..c1fa48bf 100644 --- a/src/ch/blinkenlights/android/vanilla/MediaUtils.java +++ b/src/ch/blinkenlights/android/vanilla/MediaUtils.java @@ -341,7 +341,7 @@ public class MediaUtils { String[] projection = { "_id" }; Uri uri = MediaStore.Audio.Genres.getContentUriForAudioId("external", (int)id); Cursor cursor = queryResolver(resolver, uri, projection, null, null, null); - + if (cursor != null) { if (cursor.moveToNext()) return cursor.getLong(0); @@ -381,7 +381,7 @@ public class MediaUtils { if (albumShuffle) { List tempList = new ArrayList(list); Collections.sort(tempList); - + // Build map of albumId to start index in sorted list Map albumStartIndices = new HashMap(); int index = 0; @@ -391,11 +391,11 @@ public class MediaUtils { } index++; } - + //Extract album list and shuffle List shuffledAlbums = new ArrayList(albumStartIndices.keySet()); Collections.shuffle(shuffledAlbums, random); - + //Build Song list from album list list.clear(); for (Long albumId : shuffledAlbums) { @@ -588,16 +588,16 @@ public class MediaUtils { break; } } - + pfx = (new File(pfx)).getParent(); if(pfx == null) break; /* hit root */ } } - + return path; } - + /** * Adds a final slash if the path points to an existing directory */ @@ -682,4 +682,28 @@ public class MediaUtils { return matrixCursor; } + /** + * Retrieve ID of specified media type for requested song. This works only for + * media-oriented types: {@link #TYPE_ARTIST}, {@link #TYPE_ALBUM}, {@link #TYPE_SONG} + * @param song requested song + * @param mType media type e.g. {@link #TYPE_ARTIST} + * @return ID of media type, {@link #TYPE_INVALID} if unsupported + */ + public static long getCurrentIdForType(Song song, int mType) + { + if(song == null) + return TYPE_INVALID; + + switch(mType) { + case TYPE_ARTIST: + return song.artistId; + case TYPE_ALBUM: + return song.albumId; + case TYPE_SONG: + return song.id; + default: + return TYPE_INVALID; + } + } + } diff --git a/src/ch/blinkenlights/android/vanilla/PrefDefaults.java b/src/ch/blinkenlights/android/vanilla/PrefDefaults.java index 9f5be06f..477ab629 100644 --- a/src/ch/blinkenlights/android/vanilla/PrefDefaults.java +++ b/src/ch/blinkenlights/android/vanilla/PrefDefaults.java @@ -68,4 +68,5 @@ public class PrefDefaults { public static final int VOLUME_DURING_DUCKING = 50; public static final int AUTOPLAYLIST_PLAYCOUNTS = 0; public static final boolean IGNORE_AUDIOFOCUS_LOSS = false; + public static final boolean ENABLE_SCROLL_TO_SONG = false; } diff --git a/src/ch/blinkenlights/android/vanilla/PrefKeys.java b/src/ch/blinkenlights/android/vanilla/PrefKeys.java index 143d1b07..7adf5819 100644 --- a/src/ch/blinkenlights/android/vanilla/PrefKeys.java +++ b/src/ch/blinkenlights/android/vanilla/PrefKeys.java @@ -24,7 +24,7 @@ package ch.blinkenlights.android.vanilla; /** - * SharedPreference keys. Must be kept in sync with PrefDefaults.java. + * SharedPreference keys. Must be kept in sync with {@link PrefDefaults}. */ public class PrefKeys { public static final String COVER_LONGPRESS_ACTION = "cover_longpress_action"; @@ -69,4 +69,5 @@ public class PrefKeys { public static final String VOLUME_DURING_DUCKING = "volume_during_ducking"; public static final String AUTOPLAYLIST_PLAYCOUNTS = "playcounts_autoplaylist"; public static final String IGNORE_AUDIOFOCUS_LOSS = "ignore_audiofocus_loss"; + public static final String ENABLE_SCROLL_TO_SONG = "enable_scroll_to_song"; }