Fixed search, put compareTo method into Interface
This commit is contained in:
parent
bdac092eff
commit
f1e789ea9b
|
@ -11,21 +11,4 @@ data class Artist(
|
||||||
override var coverArt: String? = null,
|
override var coverArt: String? = null,
|
||||||
override var albumCount: Long? = null,
|
override var albumCount: Long? = null,
|
||||||
override var closeness: Int = 0
|
override var closeness: Int = 0
|
||||||
) : ArtistOrIndex(id) {
|
) : ArtistOrIndex(id)
|
||||||
|
|
||||||
fun compareTo(other: Artist): Int {
|
|
||||||
when {
|
|
||||||
this.closeness == other.closeness -> {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
this.closeness > other.closeness -> {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun compareTo(other: Identifiable) = compareTo(other as Artist)
|
|
||||||
}
|
|
||||||
|
|
|
@ -15,4 +15,21 @@ abstract class ArtistOrIndex(
|
||||||
open var albumCount: Long? = null,
|
open var albumCount: Long? = null,
|
||||||
@Ignore
|
@Ignore
|
||||||
open var closeness: Int = 0
|
open var closeness: Int = 0
|
||||||
) : GenericEntry()
|
) : GenericEntry() {
|
||||||
|
|
||||||
|
fun compareTo(other: ArtistOrIndex): Int {
|
||||||
|
when {
|
||||||
|
this.closeness == other.closeness -> {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
this.closeness > other.closeness -> {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun compareTo(other: Identifiable) = compareTo(other as ArtistOrIndex)
|
||||||
|
}
|
||||||
|
|
|
@ -3,19 +3,17 @@ package org.moire.ultrasonic.domain
|
||||||
import androidx.room.Ignore
|
import androidx.room.Ignore
|
||||||
|
|
||||||
abstract class GenericEntry : Identifiable {
|
abstract class GenericEntry : Identifiable {
|
||||||
abstract override val id: String
|
|
||||||
@Ignore
|
@Ignore
|
||||||
open val name: String? = null
|
open val name: String? = null
|
||||||
override fun compareTo(other: Identifiable): Int {
|
|
||||||
return this.id.toInt().compareTo(other.id.toInt())
|
|
||||||
}
|
|
||||||
@delegate:Ignore
|
|
||||||
override val longId: Long by lazy {
|
|
||||||
id.hashCode().toLong()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Identifiable : Comparable<Identifiable> {
|
interface Identifiable : Comparable<Identifiable> {
|
||||||
val id: String
|
val id: String
|
||||||
|
|
||||||
val longId: Long
|
val longId: Long
|
||||||
|
get() = id.hashCode().toLong()
|
||||||
|
|
||||||
|
override fun compareTo(other: Identifiable): Int {
|
||||||
|
return longId.compareTo(other.longId)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,7 @@ class MusicDirectory : ArrayList<MusicDirectory.Child>() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class Child : Identifiable, GenericEntry() {
|
abstract class Child : GenericEntry() {
|
||||||
abstract override var id: String
|
abstract override var id: String
|
||||||
abstract var parent: String?
|
abstract var parent: String?
|
||||||
abstract var isDirectory: Boolean
|
abstract var isDirectory: Boolean
|
||||||
|
|
|
@ -7,7 +7,7 @@ import org.moire.ultrasonic.domain.MusicDirectory.Entry
|
||||||
* The result of a search. Contains matching artists, albums and songs.
|
* The result of a search. Contains matching artists, albums and songs.
|
||||||
*/
|
*/
|
||||||
data class SearchResult(
|
data class SearchResult(
|
||||||
val artists: List<Artist> = listOf(),
|
val artists: List<ArtistOrIndex> = listOf(),
|
||||||
val albums: List<Album> = listOf(),
|
val albums: List<Album> = listOf(),
|
||||||
val songs: List<Entry> = listOf()
|
val songs: List<Entry> = listOf()
|
||||||
)
|
)
|
||||||
|
|
|
@ -7,8 +7,8 @@ data class Album(
|
||||||
val id: String = "",
|
val id: String = "",
|
||||||
val parent: String = "",
|
val parent: String = "",
|
||||||
val album: String = "",
|
val album: String = "",
|
||||||
val title: String = "",
|
val title: String? = null,
|
||||||
val name: String = "",
|
val name: String? = null,
|
||||||
val discNumber: Int = 0,
|
val discNumber: Int = 0,
|
||||||
val coverArt: String = "",
|
val coverArt: String = "",
|
||||||
val songCount: Int = 0,
|
val songCount: Int = 0,
|
||||||
|
|
|
@ -2,7 +2,8 @@ org.gradle.parallel=true
|
||||||
org.gradle.daemon=true
|
org.gradle.daemon=true
|
||||||
org.gradle.configureondemand=true
|
org.gradle.configureondemand=true
|
||||||
org.gradle.caching=true
|
org.gradle.caching=true
|
||||||
org.gradle.jvmargs=-Xmx2g
|
org.gradle.jvmargs=-Xmx2g -XX:+UseParallelGC
|
||||||
|
|
||||||
|
|
||||||
kotlin.incremental=true
|
kotlin.incremental=true
|
||||||
kotlin.caching.enabled=true
|
kotlin.caching.enabled=true
|
||||||
|
|
|
@ -1,117 +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 org.moire.ultrasonic.view;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.ArrayAdapter;
|
|
||||||
import android.widget.SectionIndexer;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
|
|
||||||
import org.moire.ultrasonic.R;
|
|
||||||
import org.moire.ultrasonic.domain.Artist;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.LinkedHashSet;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Sindre Mehus
|
|
||||||
*/
|
|
||||||
public class ArtistAdapter extends ArrayAdapter<Artist> implements SectionIndexer
|
|
||||||
{
|
|
||||||
private final LayoutInflater layoutInflater;
|
|
||||||
|
|
||||||
// Both arrays are indexed by section ID.
|
|
||||||
private final Object[] sections;
|
|
||||||
private final Integer[] positions;
|
|
||||||
|
|
||||||
public ArtistAdapter(Context context, List<Artist> artists)
|
|
||||||
{
|
|
||||||
super(context, R.layout.list_item_generic, artists);
|
|
||||||
|
|
||||||
layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
|
||||||
|
|
||||||
Collection<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[0]);
|
|
||||||
positions = positionList.toArray(new Integer[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public View getView(
|
|
||||||
int position,
|
|
||||||
@Nullable View convertView,
|
|
||||||
@NonNull ViewGroup parent
|
|
||||||
) {
|
|
||||||
View rowView = convertView;
|
|
||||||
if (rowView == null) {
|
|
||||||
rowView = layoutInflater.inflate(R.layout.list_item_generic, parent, false);
|
|
||||||
}
|
|
||||||
((TextView) rowView).setText(getItem(position).getName());
|
|
||||||
|
|
||||||
return rowView;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object[] getSections()
|
|
||||||
{
|
|
||||||
return sections;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getPositionForSection(int section)
|
|
||||||
{
|
|
||||||
return positions.length > section ? positions[section] : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -88,8 +88,4 @@ class AlbumHeader(
|
||||||
|
|
||||||
override val longId: Long
|
override val longId: Long
|
||||||
get() = -1L
|
get() = -1L
|
||||||
|
|
||||||
override fun compareTo(other: Identifiable): Int {
|
|
||||||
return this.longId.compareTo(other.longId)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,8 +52,6 @@ class BaseAdapter<T : Identifiable> : MultiTypeAdapter() {
|
||||||
return mDiffer.currentList[position]
|
return mDiffer.currentList[position]
|
||||||
}
|
}
|
||||||
|
|
||||||
// override getIt
|
|
||||||
|
|
||||||
override var items: List<Any>
|
override var items: List<Any>
|
||||||
get() = getCurrentList()
|
get() = getCurrentList()
|
||||||
set(value) {
|
set(value) {
|
||||||
|
|
|
@ -41,9 +41,5 @@ class DividerBinder : ItemViewBinder<DividerBinder.Divider, DividerBinder.ViewHo
|
||||||
data class Divider(val stringId: Int) : Identifiable {
|
data class Divider(val stringId: Int) : Identifiable {
|
||||||
override val id: String
|
override val id: String
|
||||||
get() = stringId.toString()
|
get() = stringId.toString()
|
||||||
override val longId: Long
|
|
||||||
get() = stringId.toLong()
|
|
||||||
|
|
||||||
override fun compareTo(other: Identifiable): Int = longId.compareTo(other.longId)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -128,9 +128,5 @@ class FolderSelectorBinder(context: Context) :
|
||||||
|
|
||||||
override val longId: Long
|
override val longId: Long
|
||||||
get() = -1L
|
get() = -1L
|
||||||
|
|
||||||
override fun compareTo(other: Identifiable): Int {
|
|
||||||
return longId.compareTo(other.longId)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@ package org.moire.ultrasonic.adapters
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.TextView
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.drakeet.multitype.ItemViewBinder
|
import com.drakeet.multitype.ItemViewBinder
|
||||||
import org.moire.ultrasonic.R
|
import org.moire.ultrasonic.R
|
||||||
|
@ -37,14 +36,9 @@ class MoreButtonBinder : ItemViewBinder<MoreButtonBinder.MoreButton, RecyclerVie
|
||||||
data class MoreButton(
|
data class MoreButton(
|
||||||
val stringId: Int,
|
val stringId: Int,
|
||||||
val onClick: (() -> Unit)
|
val onClick: (() -> Unit)
|
||||||
): Identifiable {
|
) : Identifiable {
|
||||||
|
|
||||||
override val id: String
|
override val id: String
|
||||||
get() = stringId.toString()
|
get() = stringId.toString()
|
||||||
override val longId: Long
|
|
||||||
get() = stringId.toLong()
|
|
||||||
|
|
||||||
override fun compareTo(other: Identifiable): Int = longId.compareTo(other.longId)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ import org.moire.ultrasonic.api.subsonic.models.Album
|
||||||
|
|
||||||
fun Album.toDomainEntity(): MusicDirectory.Album = MusicDirectory.Album(
|
fun Album.toDomainEntity(): MusicDirectory.Album = MusicDirectory.Album(
|
||||||
id = this@toDomainEntity.id,
|
id = this@toDomainEntity.id,
|
||||||
title = this@toDomainEntity.title,
|
title = this@toDomainEntity.name ?: this@toDomainEntity.title,
|
||||||
album = this@toDomainEntity.album,
|
album = this@toDomainEntity.album,
|
||||||
coverArt = this@toDomainEntity.coverArt,
|
coverArt = this@toDomainEntity.coverArt,
|
||||||
artist = this@toDomainEntity.artist,
|
artist = this@toDomainEntity.artist,
|
||||||
|
|
|
@ -9,15 +9,12 @@ package org.moire.ultrasonic.fragment
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.core.view.isVisible
|
|
||||||
import androidx.fragment.app.viewModels
|
import androidx.fragment.app.viewModels
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import org.moire.ultrasonic.R
|
import org.moire.ultrasonic.R
|
||||||
import org.moire.ultrasonic.adapters.AlbumRowBinder
|
import org.moire.ultrasonic.adapters.AlbumRowBinder
|
||||||
import org.moire.ultrasonic.adapters.FolderSelectorBinder
|
|
||||||
import org.moire.ultrasonic.domain.Identifiable
|
|
||||||
import org.moire.ultrasonic.domain.MusicDirectory
|
import org.moire.ultrasonic.domain.MusicDirectory
|
||||||
import org.moire.ultrasonic.model.AlbumListModel
|
import org.moire.ultrasonic.model.AlbumListModel
|
||||||
import org.moire.ultrasonic.util.Constants
|
import org.moire.ultrasonic.util.Constants
|
||||||
|
@ -40,7 +37,10 @@ class AlbumListFragment : EntryListFragment<MusicDirectory.Album>() {
|
||||||
/**
|
/**
|
||||||
* The central function to pass a query to the model and return a LiveData object
|
* The central function to pass a query to the model and return a LiveData object
|
||||||
*/
|
*/
|
||||||
override fun getLiveData(args: Bundle?, refresh: Boolean): LiveData<List<MusicDirectory.Album>> {
|
override fun getLiveData(
|
||||||
|
args: Bundle?,
|
||||||
|
refresh: Boolean
|
||||||
|
): LiveData<List<MusicDirectory.Album>> {
|
||||||
if (args == null) throw IllegalArgumentException("Required arguments are missing")
|
if (args == null) throw IllegalArgumentException("Required arguments are missing")
|
||||||
|
|
||||||
val refresh = args.getBoolean(Constants.INTENT_EXTRA_NAME_REFRESH) || refresh
|
val refresh = args.getBoolean(Constants.INTENT_EXTRA_NAME_REFRESH) || refresh
|
||||||
|
|
|
@ -2,25 +2,20 @@ package org.moire.ultrasonic.fragment
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.core.view.isVisible
|
|
||||||
import androidx.fragment.app.viewModels
|
import androidx.fragment.app.viewModels
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.navigation.NavController
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import org.moire.ultrasonic.R
|
import org.moire.ultrasonic.R
|
||||||
import org.moire.ultrasonic.adapters.ArtistRowBinder
|
import org.moire.ultrasonic.adapters.ArtistRowBinder
|
||||||
import org.moire.ultrasonic.adapters.FolderSelectorBinder
|
|
||||||
import org.moire.ultrasonic.domain.Artist
|
import org.moire.ultrasonic.domain.Artist
|
||||||
import org.moire.ultrasonic.domain.ArtistOrIndex
|
import org.moire.ultrasonic.domain.ArtistOrIndex
|
||||||
import org.moire.ultrasonic.domain.Identifiable
|
|
||||||
import org.moire.ultrasonic.domain.Index
|
import org.moire.ultrasonic.domain.Index
|
||||||
import org.moire.ultrasonic.domain.MusicDirectory
|
|
||||||
import org.moire.ultrasonic.model.ArtistListModel
|
import org.moire.ultrasonic.model.ArtistListModel
|
||||||
import org.moire.ultrasonic.util.Constants
|
import org.moire.ultrasonic.util.Constants
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Displays the list of Artists from the media library
|
* Displays the list of Artists or Indexes (folders) from the media library
|
||||||
*
|
|
||||||
* FIXME: FOLDER HEADER NOT POPULATED ON FIST LOAD
|
|
||||||
*/
|
*/
|
||||||
class ArtistListFragment : EntryListFragment<ArtistOrIndex>() {
|
class ArtistListFragment : EntryListFragment<ArtistOrIndex>() {
|
||||||
|
|
||||||
|
@ -60,23 +55,32 @@ class ArtistListFragment : EntryListFragment<ArtistOrIndex>() {
|
||||||
* If we are showing artists, we need to go to AlbumList
|
* If we are showing artists, we need to go to AlbumList
|
||||||
*/
|
*/
|
||||||
override fun onItemClick(item: ArtistOrIndex) {
|
override fun onItemClick(item: ArtistOrIndex) {
|
||||||
val bundle = Bundle()
|
Companion.onItemClick(item, findNavController())
|
||||||
|
}
|
||||||
|
|
||||||
// Common arguments
|
companion object {
|
||||||
bundle.putString(Constants.INTENT_EXTRA_NAME_ID, item.id)
|
fun onItemClick(item: ArtistOrIndex, navController: NavController) {
|
||||||
bundle.putString(Constants.INTENT_EXTRA_NAME_NAME, item.name)
|
val bundle = Bundle()
|
||||||
bundle.putString(Constants.INTENT_EXTRA_NAME_PARENT_ID, item.id)
|
|
||||||
bundle.putBoolean(Constants.INTENT_EXTRA_NAME_ARTIST, (item is Artist))
|
|
||||||
|
|
||||||
// Check type
|
// Common arguments
|
||||||
if (item is Index) {
|
bundle.putString(Constants.INTENT_EXTRA_NAME_ID, item.id)
|
||||||
findNavController().navigate(R.id.artistsListToTrackCollection, bundle)
|
bundle.putString(Constants.INTENT_EXTRA_NAME_NAME, item.name)
|
||||||
} else {
|
bundle.putString(Constants.INTENT_EXTRA_NAME_PARENT_ID, item.id)
|
||||||
bundle.putString(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TYPE, Constants.ALBUMS_OF_ARTIST)
|
bundle.putBoolean(Constants.INTENT_EXTRA_NAME_ARTIST, (item is Artist))
|
||||||
bundle.putString(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TITLE, item.name)
|
|
||||||
bundle.putInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_SIZE, 1000)
|
// Check type
|
||||||
bundle.putInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_OFFSET, 0)
|
if (item is Index) {
|
||||||
findNavController().navigate(R.id.artistsListToAlbumsList, bundle)
|
navController.navigate(R.id.artistsListToTrackCollection, bundle)
|
||||||
|
} else {
|
||||||
|
bundle.putString(
|
||||||
|
Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TYPE,
|
||||||
|
Constants.ALBUMS_OF_ARTIST
|
||||||
|
)
|
||||||
|
bundle.putString(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TITLE, item.name)
|
||||||
|
bundle.putInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_SIZE, 1000)
|
||||||
|
bundle.putInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_OFFSET, 0)
|
||||||
|
navController.navigate(R.id.artistsListToAlbumsList, bundle)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,6 @@ import androidx.navigation.fragment.findNavController
|
||||||
import org.moire.ultrasonic.R
|
import org.moire.ultrasonic.R
|
||||||
import org.moire.ultrasonic.adapters.FolderSelectorBinder
|
import org.moire.ultrasonic.adapters.FolderSelectorBinder
|
||||||
import org.moire.ultrasonic.domain.Artist
|
import org.moire.ultrasonic.domain.Artist
|
||||||
import org.moire.ultrasonic.domain.ArtistOrIndex
|
|
||||||
import org.moire.ultrasonic.domain.GenericEntry
|
import org.moire.ultrasonic.domain.GenericEntry
|
||||||
import org.moire.ultrasonic.domain.Identifiable
|
import org.moire.ultrasonic.domain.Identifiable
|
||||||
import org.moire.ultrasonic.service.RxBus
|
import org.moire.ultrasonic.service.RxBus
|
||||||
|
@ -50,6 +49,10 @@ abstract class EntryListFragment<T : GenericEntry> : MultiListFragment<T>() {
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
// Call a cheap function on ServerSettingsModel to make sure it is initialized by Koin,
|
||||||
|
// because it can't be initialized from inside the callback
|
||||||
|
serverSettingsModel.toString()
|
||||||
|
|
||||||
RxBus.musicFolderChangedEventObservable.subscribe {
|
RxBus.musicFolderChangedEventObservable.subscribe {
|
||||||
if (!listModel.isOffline()) {
|
if (!listModel.isOffline()) {
|
||||||
val currentSetting = listModel.activeServer
|
val currentSetting = listModel.activeServer
|
||||||
|
|
|
@ -3,17 +3,16 @@ package org.moire.ultrasonic.fragment
|
||||||
import android.app.SearchManager
|
import android.app.SearchManager
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
import android.view.MenuInflater
|
import android.view.MenuInflater
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.ListAdapter
|
|
||||||
import androidx.appcompat.widget.SearchView
|
import androidx.appcompat.widget.SearchView
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.fragment.app.viewModels
|
import androidx.fragment.app.viewModels
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import androidx.navigation.Navigation
|
import androidx.navigation.Navigation
|
||||||
|
import androidx.navigation.fragment.findNavController
|
||||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.koin.core.component.KoinComponent
|
import org.koin.core.component.KoinComponent
|
||||||
|
@ -26,7 +25,9 @@ import org.moire.ultrasonic.adapters.MoreButtonBinder
|
||||||
import org.moire.ultrasonic.adapters.MoreButtonBinder.MoreButton
|
import org.moire.ultrasonic.adapters.MoreButtonBinder.MoreButton
|
||||||
import org.moire.ultrasonic.adapters.TrackViewBinder
|
import org.moire.ultrasonic.adapters.TrackViewBinder
|
||||||
import org.moire.ultrasonic.domain.Artist
|
import org.moire.ultrasonic.domain.Artist
|
||||||
|
import org.moire.ultrasonic.domain.ArtistOrIndex
|
||||||
import org.moire.ultrasonic.domain.Identifiable
|
import org.moire.ultrasonic.domain.Identifiable
|
||||||
|
import org.moire.ultrasonic.domain.Index
|
||||||
import org.moire.ultrasonic.domain.MusicDirectory
|
import org.moire.ultrasonic.domain.MusicDirectory
|
||||||
import org.moire.ultrasonic.domain.SearchResult
|
import org.moire.ultrasonic.domain.SearchResult
|
||||||
import org.moire.ultrasonic.fragment.FragmentTitle.Companion.setTitle
|
import org.moire.ultrasonic.fragment.FragmentTitle.Companion.setTitle
|
||||||
|
@ -41,12 +42,10 @@ import org.moire.ultrasonic.util.CommunicationError
|
||||||
import org.moire.ultrasonic.util.Constants
|
import org.moire.ultrasonic.util.Constants
|
||||||
import org.moire.ultrasonic.util.Settings
|
import org.moire.ultrasonic.util.Settings
|
||||||
import org.moire.ultrasonic.util.Util.toast
|
import org.moire.ultrasonic.util.Util.toast
|
||||||
import org.moire.ultrasonic.view.ArtistAdapter
|
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initiates a search on the media library and displays the results
|
* Initiates a search on the media library and displays the results
|
||||||
* FIXME: Artist click, display
|
|
||||||
*/
|
*/
|
||||||
class SearchFragment : MultiListFragment<Identifiable>(), KoinComponent {
|
class SearchFragment : MultiListFragment<Identifiable>(), KoinComponent {
|
||||||
private var searchResult: SearchResult? = null
|
private var searchResult: SearchResult? = null
|
||||||
|
@ -265,11 +264,28 @@ class SearchFragment : MultiListFragment<Identifiable>(), KoinComponent {
|
||||||
populateList(listModel.trimResultLength(searchResult!!, maxSongs = Int.MAX_VALUE))
|
populateList(listModel.trimResultLength(searchResult!!, maxSongs = Int.MAX_VALUE))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onArtistSelected(artist: Artist) {
|
private fun onArtistSelected(item: ArtistOrIndex) {
|
||||||
val bundle = Bundle()
|
val bundle = Bundle()
|
||||||
bundle.putString(Constants.INTENT_EXTRA_NAME_ID, artist.id)
|
|
||||||
bundle.putString(Constants.INTENT_EXTRA_NAME_NAME, artist.id)
|
// Common arguments
|
||||||
Navigation.findNavController(requireView()).navigate(R.id.searchToSelectAlbum, bundle)
|
bundle.putString(Constants.INTENT_EXTRA_NAME_ID, item.id)
|
||||||
|
bundle.putString(Constants.INTENT_EXTRA_NAME_NAME, item.name)
|
||||||
|
bundle.putString(Constants.INTENT_EXTRA_NAME_PARENT_ID, item.id)
|
||||||
|
bundle.putBoolean(Constants.INTENT_EXTRA_NAME_ARTIST, (item is Artist))
|
||||||
|
|
||||||
|
// Check type
|
||||||
|
if (item is Index) {
|
||||||
|
findNavController().navigate(R.id.searchToTrackCollection, bundle)
|
||||||
|
} else {
|
||||||
|
bundle.putString(
|
||||||
|
Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TYPE,
|
||||||
|
Constants.ALBUMS_OF_ARTIST
|
||||||
|
)
|
||||||
|
bundle.putString(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TITLE, item.name)
|
||||||
|
bundle.putInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_SIZE, 1000)
|
||||||
|
bundle.putInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_OFFSET, 0)
|
||||||
|
findNavController().navigate(R.id.searchToAlbumsList, bundle)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onAlbumSelected(album: MusicDirectory.Album, autoplay: Boolean) {
|
private fun onAlbumSelected(album: MusicDirectory.Album, autoplay: Boolean) {
|
||||||
|
@ -278,14 +294,21 @@ class SearchFragment : MultiListFragment<Identifiable>(), KoinComponent {
|
||||||
bundle.putString(Constants.INTENT_EXTRA_NAME_NAME, album.title)
|
bundle.putString(Constants.INTENT_EXTRA_NAME_NAME, album.title)
|
||||||
bundle.putBoolean(Constants.INTENT_EXTRA_NAME_IS_ALBUM, album.isDirectory)
|
bundle.putBoolean(Constants.INTENT_EXTRA_NAME_IS_ALBUM, album.isDirectory)
|
||||||
bundle.putBoolean(Constants.INTENT_EXTRA_NAME_AUTOPLAY, autoplay)
|
bundle.putBoolean(Constants.INTENT_EXTRA_NAME_AUTOPLAY, autoplay)
|
||||||
Navigation.findNavController(requireView()).navigate(R.id.searchToSelectAlbum, bundle)
|
Navigation.findNavController(requireView()).navigate(R.id.searchToTrackCollection, bundle)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onSongSelected(song: MusicDirectory.Entry, append: Boolean) {
|
private fun onSongSelected(song: MusicDirectory.Entry, append: Boolean) {
|
||||||
if (!append) {
|
if (!append) {
|
||||||
mediaPlayerController.clear()
|
mediaPlayerController.clear()
|
||||||
}
|
}
|
||||||
mediaPlayerController.addToPlaylist(listOf(song), false, false, false, false, false)
|
mediaPlayerController.addToPlaylist(
|
||||||
|
listOf(song),
|
||||||
|
save = false,
|
||||||
|
autoPlay = false,
|
||||||
|
playNext = false,
|
||||||
|
shuffle = false,
|
||||||
|
newPlaylist = false
|
||||||
|
)
|
||||||
mediaPlayerController.play(mediaPlayerController.playlistSize - 1)
|
mediaPlayerController.play(mediaPlayerController.playlistSize - 1)
|
||||||
toast(context, resources.getQuantityString(R.plurals.select_album_n_songs_added, 1, 1))
|
toast(context, resources.getQuantityString(R.plurals.select_album_n_songs_added, 1, 1))
|
||||||
}
|
}
|
||||||
|
@ -304,7 +327,7 @@ class SearchFragment : MultiListFragment<Identifiable>(), KoinComponent {
|
||||||
|
|
||||||
override fun onItemClick(item: Identifiable) {
|
override fun onItemClick(item: Identifiable) {
|
||||||
when (item) {
|
when (item) {
|
||||||
is Artist -> {
|
is ArtistOrIndex -> {
|
||||||
onArtistSelected(item)
|
onArtistSelected(item)
|
||||||
}
|
}
|
||||||
is MusicDirectory.Entry -> {
|
is MusicDirectory.Entry -> {
|
||||||
|
|
|
@ -49,13 +49,11 @@ import org.moire.ultrasonic.util.Settings
|
||||||
import org.moire.ultrasonic.util.Util
|
import org.moire.ultrasonic.util.Util
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* Displays a group of tracks, eg. the songs of an album, of a playlist etc.
|
* Displays a group of tracks, eg. the songs of an album, of a playlist etc.
|
||||||
*
|
*
|
||||||
* In most cases the data should be just a list of Entries, but there are some cases
|
* In most cases the data should be just a list of Entries, but there are some cases
|
||||||
* where the list can contain Albums as well. This happens especially when having ID3 tags disabled,
|
* where the list can contain Albums as well. This happens especially when having ID3 tags disabled,
|
||||||
* or using Offline mode, both in which Indexes instead of Artists are being used.
|
* or using Offline mode, both in which Indexes instead of Artists are being used.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
@Suppress("TooManyFunctions")
|
@Suppress("TooManyFunctions")
|
||||||
open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
|
open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
|
||||||
|
@ -96,7 +94,7 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
|
||||||
// Setup refresh handler
|
// Setup refresh handler
|
||||||
refreshListView = view.findViewById(refreshListId)
|
refreshListView = view.findViewById(refreshListId)
|
||||||
refreshListView?.setOnRefreshListener {
|
refreshListView?.setOnRefreshListener {
|
||||||
refreshData(true)
|
getLiveData(arguments, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: remove special casing for songsForGenre
|
// TODO: remove special casing for songsForGenre
|
||||||
|
@ -209,12 +207,6 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
|
||||||
refreshListView?.isRefreshing = false
|
refreshListView?.isRefreshing = false
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun refreshData(refresh: Boolean = false) {
|
|
||||||
val args = getArgumentsClone()
|
|
||||||
args.putBoolean(Constants.INTENT_EXTRA_NAME_REFRESH, refresh)
|
|
||||||
getLiveData(args)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onPrepareOptionsMenu(menu: Menu) {
|
override fun onPrepareOptionsMenu(menu: Menu) {
|
||||||
super.onPrepareOptionsMenu(menu)
|
super.onPrepareOptionsMenu(menu)
|
||||||
playAllButton = menu.findItem(R.id.select_album_play_all)
|
playAllButton = menu.findItem(R.id.select_album_play_all)
|
||||||
|
@ -293,8 +285,6 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
|
||||||
}
|
}
|
||||||
|
|
||||||
val isArtist = arguments?.getBoolean(Constants.INTENT_EXTRA_NAME_ARTIST, false) ?: false
|
val isArtist = arguments?.getBoolean(Constants.INTENT_EXTRA_NAME_ARTIST, false) ?: false
|
||||||
|
|
||||||
// FIXME WHICH id if no arguments?
|
|
||||||
val id = arguments?.getString(Constants.INTENT_EXTRA_NAME_ID)
|
val id = arguments?.getString(Constants.INTENT_EXTRA_NAME_ID)
|
||||||
|
|
||||||
if (hasSubFolders && id != null) {
|
if (hasSubFolders && id != null) {
|
||||||
|
@ -565,7 +555,10 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("LongMethod")
|
@Suppress("LongMethod")
|
||||||
override fun getLiveData(args: Bundle?, refresh: Boolean): LiveData<List<MusicDirectory.Child>> {
|
override fun getLiveData(
|
||||||
|
args: Bundle?,
|
||||||
|
refresh: Boolean
|
||||||
|
): LiveData<List<MusicDirectory.Child>> {
|
||||||
if (args == null) return listModel.currentList
|
if (args == null) return listModel.currentList
|
||||||
val id = args.getString(Constants.INTENT_EXTRA_NAME_ID)
|
val id = args.getString(Constants.INTENT_EXTRA_NAME_ID)
|
||||||
val isAlbum = args.getBoolean(Constants.INTENT_EXTRA_NAME_IS_ALBUM, false)
|
val isAlbum = args.getBoolean(Constants.INTENT_EXTRA_NAME_IS_ALBUM, false)
|
||||||
|
@ -588,7 +581,7 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
|
||||||
val albumListOffset = args.getInt(
|
val albumListOffset = args.getInt(
|
||||||
Constants.INTENT_EXTRA_NAME_ALBUM_LIST_OFFSET, 0
|
Constants.INTENT_EXTRA_NAME_ALBUM_LIST_OFFSET, 0
|
||||||
)
|
)
|
||||||
val refresh = args.getBoolean(Constants.INTENT_EXTRA_NAME_REFRESH, true) || refresh
|
val refresh2 = args.getBoolean(Constants.INTENT_EXTRA_NAME_REFRESH, true) || refresh
|
||||||
|
|
||||||
listModel.viewModelScope.launch(handler) {
|
listModel.viewModelScope.launch(handler) {
|
||||||
refreshListView?.isRefreshing = true
|
refreshListView?.isRefreshing = true
|
||||||
|
@ -610,7 +603,7 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
|
||||||
listModel.getStarred()
|
listModel.getStarred()
|
||||||
} else if (getVideos != 0) {
|
} else if (getVideos != 0) {
|
||||||
setTitle(R.string.main_videos)
|
setTitle(R.string.main_videos)
|
||||||
listModel.getVideos(refresh)
|
listModel.getVideos(refresh2)
|
||||||
} else if (getRandomTracks != 0) {
|
} else if (getRandomTracks != 0) {
|
||||||
setTitle(R.string.main_songs_random)
|
setTitle(R.string.main_songs_random)
|
||||||
listModel.getRandom(albumListSize)
|
listModel.getRandom(albumListSize)
|
||||||
|
@ -618,12 +611,12 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
|
||||||
setTitle(name)
|
setTitle(name)
|
||||||
if (!isOffline() && Settings.shouldUseId3Tags) {
|
if (!isOffline() && Settings.shouldUseId3Tags) {
|
||||||
if (isAlbum) {
|
if (isAlbum) {
|
||||||
listModel.getAlbum(refresh, id!!, name)
|
listModel.getAlbum(refresh2, id!!, name)
|
||||||
} else {
|
} else {
|
||||||
throw IllegalAccessException("Use AlbumFragment instead!")
|
throw IllegalAccessException("Use AlbumFragment instead!")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
listModel.getMusicDirectory(refresh, id!!, name)
|
listModel.getMusicDirectory(refresh2, id!!, name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -436,10 +436,6 @@ class DownloadFile(
|
||||||
override val id: String
|
override val id: String
|
||||||
get() = song.id
|
get() = song.id
|
||||||
|
|
||||||
override val longId: Long by lazy {
|
|
||||||
id.hashCode().toLong()
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val MAX_RETRIES = 5
|
const val MAX_RETRIES = 5
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ import org.koin.core.component.KoinComponent
|
||||||
import org.koin.core.component.inject
|
import org.koin.core.component.inject
|
||||||
import org.moire.ultrasonic.data.ActiveServerProvider
|
import org.moire.ultrasonic.data.ActiveServerProvider
|
||||||
import org.moire.ultrasonic.domain.Artist
|
import org.moire.ultrasonic.domain.Artist
|
||||||
|
import org.moire.ultrasonic.domain.ArtistOrIndex
|
||||||
import org.moire.ultrasonic.domain.Bookmark
|
import org.moire.ultrasonic.domain.Bookmark
|
||||||
import org.moire.ultrasonic.domain.ChatMessage
|
import org.moire.ultrasonic.domain.ChatMessage
|
||||||
import org.moire.ultrasonic.domain.Genre
|
import org.moire.ultrasonic.domain.Genre
|
||||||
|
@ -122,7 +123,7 @@ class OfflineMusicService : MusicService, KoinComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun search(criteria: SearchCriteria): SearchResult {
|
override fun search(criteria: SearchCriteria): SearchResult {
|
||||||
val artists: MutableList<Artist> = ArrayList()
|
val artists: MutableList<ArtistOrIndex> = ArrayList()
|
||||||
val albums: MutableList<MusicDirectory.Album> = ArrayList()
|
val albums: MutableList<MusicDirectory.Album> = ArrayList()
|
||||||
val songs: MutableList<MusicDirectory.Entry> = ArrayList()
|
val songs: MutableList<MusicDirectory.Entry> = ArrayList()
|
||||||
val root = FileUtil.musicDirectory
|
val root = FileUtil.musicDirectory
|
||||||
|
@ -131,7 +132,7 @@ class OfflineMusicService : MusicService, KoinComponent {
|
||||||
val artistName = artistFile.name
|
val artistName = artistFile.name
|
||||||
if (artistFile.isDirectory) {
|
if (artistFile.isDirectory) {
|
||||||
if (matchCriteria(criteria, artistName).also { closeness = it } > 0) {
|
if (matchCriteria(criteria, artistName).also { closeness = it } > 0) {
|
||||||
val artist = Artist(artistFile.path)
|
val artist = Index(artistFile.path)
|
||||||
artist.index = artistFile.name.substring(0, 1)
|
artist.index = artistFile.name.substring(0, 1)
|
||||||
artist.name = artistName
|
artist.name = artistName
|
||||||
artist.closeness = closeness
|
artist.closeness = closeness
|
||||||
|
|
|
@ -361,7 +361,6 @@ open class RESTMusicService(
|
||||||
musicFolderId
|
musicFolderId
|
||||||
).execute().throwOnFailure()
|
).execute().throwOnFailure()
|
||||||
|
|
||||||
|
|
||||||
return response.body()!!.albumList.toDomainEntityList()
|
return response.body()!!.albumList.toDomainEntityList()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -60,8 +60,11 @@
|
||||||
android:id="@+id/searchFragment"
|
android:id="@+id/searchFragment"
|
||||||
android:name="org.moire.ultrasonic.fragment.SearchFragment" >
|
android:name="org.moire.ultrasonic.fragment.SearchFragment" >
|
||||||
<action
|
<action
|
||||||
android:id="@+id/searchToSelectAlbum"
|
android:id="@+id/searchToTrackCollection"
|
||||||
app:destination="@id/trackCollectionFragment" />
|
app:destination="@id/trackCollectionFragment" />
|
||||||
|
<action
|
||||||
|
android:id="@+id/searchToAlbumsList"
|
||||||
|
app:destination="@id/albumListFragment" />
|
||||||
</fragment>
|
</fragment>
|
||||||
<fragment
|
<fragment
|
||||||
android:id="@+id/playlistsFragment"
|
android:id="@+id/playlistsFragment"
|
||||||
|
|
Loading…
Reference in New Issue