diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/util/MergeAdapter.java b/ultrasonic/src/main/java/org/moire/ultrasonic/util/MergeAdapter.java deleted file mode 100644 index 7d8880be..00000000 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/util/MergeAdapter.java +++ /dev/null @@ -1,316 +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 org.moire.ultrasonic.util; - -import android.database.DataSetObserver; -import android.view.View; -import android.view.ViewGroup; -import android.widget.BaseAdapter; -import android.widget.ListAdapter; - -import java.util.AbstractList; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - * Adapter that merges multiple child adapters and views - * into a single contiguous whole. - *

- * 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 AbstractList pieces = new ArrayList(); - - /** - * 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(Collections.singletonList(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 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 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 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/ultrasonic/src/main/java/org/moire/ultrasonic/util/SackOfViewsAdapter.java b/ultrasonic/src/main/java/org/moire/ultrasonic/util/SackOfViewsAdapter.java deleted file mode 100644 index 080e1430..00000000 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/util/SackOfViewsAdapter.java +++ /dev/null @@ -1,195 +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 org.moire.ultrasonic.util; - -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. - *

- * If you supply a size, you must implement newView(), to - * create a required view. The adapter will then cache these - * views. - *

- * 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. - *

- * Subclasses may also wish to override areAllItemsEnabled() - * (default: false) and isEnabled() (default: false), if some - * of their rows should be selectable. - *

- * It is assumed each view is unique, and therefore will not - * get recycled. - *

- * 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 views; - - /** - * Constructor creating an empty list of views, but with - * a specified count. Subclasses must override newView(). - */ - public SackOfViewsAdapter(int count) - { - super(); - - views = new ArrayList(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 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 static View newView(int position, ViewGroup parent) - { - throw new RuntimeException("You must override newView()!"); - } -} diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/MainFragment.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/MainFragment.kt index 940fb1b5..6969a47f 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/MainFragment.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/MainFragment.kt @@ -4,16 +4,15 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.widget.AdapterView -import android.widget.AdapterView.OnItemClickListener -import android.widget.ListView +import android.widget.LinearLayout +import android.widget.TextView +import androidx.core.view.isVisible import androidx.fragment.app.Fragment import androidx.navigation.Navigation import org.koin.core.component.KoinComponent import org.moire.ultrasonic.R import org.moire.ultrasonic.data.ActiveServerProvider.Companion.isOffline import org.moire.ultrasonic.util.Constants -import org.moire.ultrasonic.util.MergeAdapter import org.moire.ultrasonic.util.Settings import org.moire.ultrasonic.util.Util @@ -21,26 +20,26 @@ import org.moire.ultrasonic.util.Util * Displays the Main screen of Ultrasonic, where the music library can be browsed */ class MainFragment : Fragment(), KoinComponent { - private var list: ListView? = null - private lateinit var musicTitle: View - private lateinit var artistsButton: View - private lateinit var albumsButton: View - private lateinit var genresButton: View - private lateinit var videosTitle: View - private lateinit var songsTitle: View - private lateinit var randomSongsButton: View - private lateinit var songsStarredButton: View - private lateinit var albumsTitle: View - private lateinit var albumsNewestButton: View - private lateinit var albumsRandomButton: View - private lateinit var albumsHighestButton: View - private lateinit var albumsStarredButton: View - private lateinit var albumsRecentButton: View - private lateinit var albumsFrequentButton: View - private lateinit var albumsAlphaByNameButton: View - private lateinit var albumsAlphaByArtistButton: View - private lateinit var videosButton: View + private lateinit var list: LinearLayout + private lateinit var musicTitle: TextView + private lateinit var artistsButton: TextView + private lateinit var albumsButton: TextView + private lateinit var genresButton: TextView + private lateinit var videosTitle: TextView + private lateinit var songsTitle: TextView + private lateinit var randomSongsButton: TextView + private lateinit var songsStarredButton: TextView + private lateinit var albumsTitle: TextView + private lateinit var albumsNewestButton: TextView + private lateinit var albumsRandomButton: TextView + private lateinit var albumsHighestButton: TextView + private lateinit var albumsStarredButton: TextView + private lateinit var albumsRecentButton: TextView + private lateinit var albumsFrequentButton: TextView + private lateinit var albumsAlphaByNameButton: TextView + private lateinit var albumsAlphaByArtistButton: TextView + private lateinit var videosButton: TextView override fun onCreate(savedInstanceState: Bundle?) { Util.applyTheme(this.context) @@ -59,8 +58,8 @@ class MainFragment : Fragment(), KoinComponent { list = view.findViewById(R.id.main_list) setupButtons() - - if (list != null) setupMenuList(list!!) + setupClickListener() + setupItemVisibility() super.onViewCreated(view, savedInstanceState) } @@ -71,134 +70,128 @@ class MainFragment : Fragment(), KoinComponent { val currentId3Setting = Settings.shouldUseId3Tags // If setting has changed... - if (currentId3Setting != shouldUseId3) { - shouldUseId3 = currentId3Setting + if (currentId3Setting != cachedId3Setting) { + cachedId3Setting = currentId3Setting shouldRestart = true } // then setup the list anew. if (shouldRestart) { - if (list != null) setupMenuList(list!!) + setupItemVisibility() } } private fun setupButtons() { - val buttons = layoutInflater.inflate(R.layout.main_buttons, list, false) - musicTitle = buttons.findViewById(R.id.main_music) - artistsButton = buttons.findViewById(R.id.main_artists_button) - albumsButton = buttons.findViewById(R.id.main_albums_button) - genresButton = buttons.findViewById(R.id.main_genres_button) - videosTitle = buttons.findViewById(R.id.main_videos_title) - songsTitle = buttons.findViewById(R.id.main_songs) - randomSongsButton = buttons.findViewById(R.id.main_songs_button) - songsStarredButton = buttons.findViewById(R.id.main_songs_starred) - albumsTitle = buttons.findViewById(R.id.main_albums) - albumsNewestButton = buttons.findViewById(R.id.main_albums_newest) - albumsRandomButton = buttons.findViewById(R.id.main_albums_random) - albumsHighestButton = buttons.findViewById(R.id.main_albums_highest) - albumsStarredButton = buttons.findViewById(R.id.main_albums_starred) - albumsRecentButton = buttons.findViewById(R.id.main_albums_recent) - albumsFrequentButton = buttons.findViewById(R.id.main_albums_frequent) - albumsAlphaByNameButton = buttons.findViewById(R.id.main_albums_alphaByName) - albumsAlphaByArtistButton = buttons.findViewById(R.id.main_albums_alphaByArtist) - videosButton = buttons.findViewById(R.id.main_videos) + musicTitle = list.findViewById(R.id.main_music) + artistsButton = list.findViewById(R.id.main_artists_button) + albumsButton = list.findViewById(R.id.main_albums_button) + genresButton = list.findViewById(R.id.main_genres_button) + videosTitle = list.findViewById(R.id.main_videos_title) + songsTitle = list.findViewById(R.id.main_songs) + randomSongsButton = list.findViewById(R.id.main_songs_button) + songsStarredButton = list.findViewById(R.id.main_songs_starred) + albumsTitle = list.findViewById(R.id.main_albums) + albumsNewestButton = list.findViewById(R.id.main_albums_newest) + albumsRandomButton = list.findViewById(R.id.main_albums_random) + albumsHighestButton = list.findViewById(R.id.main_albums_highest) + albumsStarredButton = list.findViewById(R.id.main_albums_starred) + albumsRecentButton = list.findViewById(R.id.main_albums_recent) + albumsFrequentButton = list.findViewById(R.id.main_albums_frequent) + albumsAlphaByNameButton = list.findViewById(R.id.main_albums_alphaByName) + albumsAlphaByArtistButton = list.findViewById(R.id.main_albums_alphaByArtist) + videosButton = list.findViewById(R.id.main_videos) } - private fun setupMenuList(list: ListView) { + private fun setupItemVisibility() { + // Cache some values + cachedId3Setting = Settings.shouldUseId3Tags + val isOnline = !isOffline() - // TODO: Should use RecyclerView - val adapter = MergeAdapter() + // Music + musicTitle.isVisible = true + artistsButton.isVisible = true + albumsButton.isVisible = isOnline + genresButton.isVisible = true - shouldUseId3 = Settings.shouldUseId3Tags + // Songs + songsTitle.isVisible = isOnline + randomSongsButton.isVisible = true + songsStarredButton.isVisible = isOnline - if (!isOffline()) { - adapter.addView(musicTitle, false) - adapter.addViews(listOf(artistsButton, albumsButton, genresButton), true) - adapter.addView(songsTitle, false) - adapter.addViews(listOf(randomSongsButton, songsStarredButton), true) - adapter.addView(albumsTitle, false) - adapter.addViews( - listOf( - albumsNewestButton, - albumsRecentButton, - albumsFrequentButton - ), - true - ) - if (!shouldUseId3) { - adapter.addView(albumsHighestButton, true) - } - adapter.addViews( - listOf( - albumsRandomButton, - albumsStarredButton, - albumsAlphaByNameButton, - albumsAlphaByArtistButton - ), - true - ) - adapter.addView(videosTitle, false) - adapter.addViews(listOf(videosButton), true) - } else { - // Offline supported calls - adapter.addView(musicTitle, false) - adapter.addViews(listOf(artistsButton, genresButton), true) - adapter.addView(songsTitle, false) - adapter.addView(randomSongsButton, true) - } + // Albums + albumsTitle.isVisible = isOnline + albumsNewestButton.isVisible = isOnline + albumsRecentButton.isVisible = isOnline + albumsFrequentButton.isVisible = isOnline + albumsHighestButton.isVisible = isOnline && !cachedId3Setting + albumsRandomButton.isVisible = isOnline + albumsStarredButton.isVisible = isOnline + albumsAlphaByNameButton.isVisible = isOnline + albumsAlphaByArtistButton.isVisible = isOnline - list.adapter = adapter - list.onItemClickListener = listListener + // Videos + videosTitle.isVisible = isOnline + videosButton.isVisible = isOnline } - private val listListener = - OnItemClickListener { _: AdapterView<*>?, view: View, _: Int, _: Long -> - when { - view === albumsNewestButton -> { - showAlbumList("newest", R.string.main_albums_newest) - } - view === albumsRandomButton -> { - showAlbumList("random", R.string.main_albums_random) - } - view === albumsHighestButton -> { - showAlbumList("highest", R.string.main_albums_highest) - } - view === albumsRecentButton -> { - showAlbumList("recent", R.string.main_albums_recent) - } - view === albumsFrequentButton -> { - showAlbumList("frequent", R.string.main_albums_frequent) - } - view === albumsStarredButton -> { - showAlbumList(Constants.STARRED, R.string.main_albums_starred) - } - view === albumsAlphaByNameButton -> { - showAlbumList(Constants.ALPHABETICAL_BY_NAME, R.string.main_albums_alphaByName) - } - view === albumsAlphaByArtistButton -> { - showAlbumList("alphabeticalByArtist", R.string.main_albums_alphaByArtist) - } - view === songsStarredButton -> { - showStarredSongs() - } - view === artistsButton -> { - showArtists() - } - view === albumsButton -> { - showAlbumList(Constants.ALPHABETICAL_BY_NAME, R.string.main_albums_title) - } - view === randomSongsButton -> { - showRandomSongs() - } - view === genresButton -> { - showGenres() - } - view === videosButton -> { - showVideos() - } - } + private fun setupClickListener() { + albumsNewestButton.setOnClickListener { + showAlbumList("newest", R.string.main_albums_newest) } + albumsRandomButton.setOnClickListener { + showAlbumList("random", R.string.main_albums_random) + } + + albumsHighestButton.setOnClickListener { + showAlbumList("highest", R.string.main_albums_highest) + } + + albumsRecentButton.setOnClickListener { + showAlbumList("recent", R.string.main_albums_recent) + } + + albumsFrequentButton.setOnClickListener { + showAlbumList("frequent", R.string.main_albums_frequent) + } + + albumsStarredButton.setOnClickListener { + showAlbumList(Constants.STARRED, R.string.main_albums_starred) + } + + albumsAlphaByNameButton.setOnClickListener { + showAlbumList(Constants.ALPHABETICAL_BY_NAME, R.string.main_albums_alphaByName) + } + + albumsAlphaByArtistButton.setOnClickListener { + showAlbumList("alphabeticalByArtist", R.string.main_albums_alphaByArtist) + } + + songsStarredButton.setOnClickListener { + showStarredSongs() + } + + artistsButton.setOnClickListener { + showArtists() + } + + albumsButton.setOnClickListener { + showAlbumList(Constants.ALPHABETICAL_BY_NAME, R.string.main_albums_title) + } + + randomSongsButton.setOnClickListener { + showRandomSongs() + } + + genresButton.setOnClickListener { + showGenres() + } + + videosButton.setOnClickListener { + showVideos() + } + } + private fun showStarredSongs() { val bundle = Bundle() bundle.putInt(Constants.INTENT_STARRED, 1) @@ -242,6 +235,6 @@ class MainFragment : Fragment(), KoinComponent { } companion object { - private var shouldUseId3 = false + private var cachedId3Setting = false } } diff --git a/ultrasonic/src/main/res/layout/main.xml b/ultrasonic/src/main/res/layout/main.xml index dcd8fbd0..e6763155 100644 --- a/ultrasonic/src/main/res/layout/main.xml +++ b/ultrasonic/src/main/res/layout/main.xml @@ -1,18 +1,211 @@ + a:orientation="vertical"> - + a:layout_height="wrap_content" + a:gravity="center_vertical" + a:paddingStart="6dp" + a:text="@string/main.music" + a:textAllCaps="true" + a:textAppearance="?android:attr/textAppearanceSmall" + a:textColor="@color/cyan" + a:textStyle="bold" /> - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - \ No newline at end of file diff --git a/ultrasonic/src/main/res/layout/main_buttons.xml b/ultrasonic/src/main/res/layout/main_buttons.xml deleted file mode 100644 index ef1395c7..00000000 --- a/ultrasonic/src/main/res/layout/main_buttons.xml +++ /dev/null @@ -1,100 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -