Merge pull request #722 from ultrasonic/ready/OnlyRename

Cleanup terminology surrounding entry vs track vs song.
This commit is contained in:
tzugen 2022-03-28 23:56:22 +02:00 committed by GitHub
commit 287169649a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
51 changed files with 379 additions and 382 deletions

View File

@ -0,0 +1,27 @@
package org.moire.ultrasonic.domain
import androidx.room.PrimaryKey
import java.util.Date
data class Album(
@PrimaryKey override var id: String,
override var parent: String? = null,
override var album: String? = null,
override var title: String? = null,
override val name: String? = null,
override var discNumber: Int? = 0,
override var coverArt: String? = null,
override var songCount: Long? = null,
override var created: Date? = null,
override var artist: String? = null,
override var artistId: String? = null,
override var duration: Int? = 0,
override var year: Int? = 0,
override var genre: String? = null,
override var starred: Boolean = false,
override var path: String? = null,
override var closeness: Int = 0,
) : MusicDirectory.Child() {
override var isDirectory = true
override var isVideo = false
}

View File

@ -2,7 +2,6 @@ package org.moire.ultrasonic.domain
import java.io.Serializable import java.io.Serializable
import java.util.Date import java.util.Date
import org.moire.ultrasonic.domain.MusicDirectory.Entry
data class Bookmark( data class Bookmark(
val position: Int = 0, val position: Int = 0,
@ -10,7 +9,7 @@ data class Bookmark(
val comment: String, val comment: String,
val created: Date? = null, val created: Date? = null,
val changed: Date? = null, val changed: Date? = null,
val entry: Entry val track: Track
) : Serializable { ) : Serializable {
companion object { companion object {
private const val serialVersionUID = 8988990025189807803L private const val serialVersionUID = 8988990025189807803L

View File

@ -1,8 +1,5 @@
package org.moire.ultrasonic.domain package org.moire.ultrasonic.domain
import androidx.room.Entity
import androidx.room.PrimaryKey
import java.io.Serializable
import java.util.Date import java.util.Date
class MusicDirectory : ArrayList<MusicDirectory.Child>() { class MusicDirectory : ArrayList<MusicDirectory.Child>() {
@ -20,9 +17,9 @@ class MusicDirectory : ArrayList<MusicDirectory.Child>() {
return filter { it.isDirectory && includeDirs || !it.isDirectory && includeFiles } return filter { it.isDirectory && includeDirs || !it.isDirectory && includeFiles }
} }
fun getTracks(): List<Entry> { fun getTracks(): List<Track> {
return mapNotNull { return mapNotNull {
it as? Entry it as? Track
} }
} }
@ -53,87 +50,4 @@ class MusicDirectory : ArrayList<MusicDirectory.Child>() {
abstract var closeness: Int abstract var closeness: Int
abstract var isVideo: Boolean abstract var isVideo: Boolean
} }
// TODO: Rename to Track
@Entity
data class Entry(
@PrimaryKey override var id: String,
override var parent: String? = null,
override var isDirectory: Boolean = false,
override var title: String? = null,
override var album: String? = null,
var albumId: String? = null,
override var artist: String? = null,
override var artistId: String? = null,
var track: Int? = null,
override var year: Int? = null,
override var genre: String? = null,
var contentType: String? = null,
var suffix: String? = null,
var transcodedContentType: String? = null,
var transcodedSuffix: String? = null,
override var coverArt: String? = null,
var size: Long? = null,
override var songCount: Long? = null,
override var duration: Int? = null,
var bitRate: Int? = null,
override var path: String? = null,
override var isVideo: Boolean = false,
override var starred: Boolean = false,
override var discNumber: Int? = null,
var type: String? = null,
override var created: Date? = null,
override var closeness: Int = 0,
var bookmarkPosition: Int = 0,
var userRating: Int? = null,
var averageRating: Float? = null,
override var name: String? = null
) : Serializable, Child() {
fun setDuration(duration: Long) {
this.duration = duration.toInt()
}
companion object {
private const val serialVersionUID = -3339106650010798108L
}
fun compareTo(other: Entry): 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 Entry)
}
data class Album(
@PrimaryKey override var id: String,
override var parent: String? = null,
override var album: String? = null,
override var title: String? = null,
override val name: String? = null,
override var discNumber: Int? = 0,
override var coverArt: String? = null,
override var songCount: Long? = null,
override var created: Date? = null,
override var artist: String? = null,
override var artistId: String? = null,
override var duration: Int? = 0,
override var year: Int? = 0,
override var genre: String? = null,
override var starred: Boolean = false,
override var path: String? = null,
override var closeness: Int = 0,
) : Child() {
override var isDirectory = true
override var isVideo = false
}
} }

View File

@ -1,13 +1,10 @@
package org.moire.ultrasonic.domain package org.moire.ultrasonic.domain
import org.moire.ultrasonic.domain.MusicDirectory.Album
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<ArtistOrIndex> = listOf(), val artists: List<ArtistOrIndex> = listOf(),
val albums: List<Album> = listOf(), val albums: List<Album> = listOf(),
val songs: List<Entry> = listOf() val songs: List<Track> = listOf()
) )

View File

@ -1,7 +1,6 @@
package org.moire.ultrasonic.domain package org.moire.ultrasonic.domain
import java.io.Serializable import java.io.Serializable
import org.moire.ultrasonic.domain.MusicDirectory.Entry
data class Share( data class Share(
override var id: String, override var id: String,
@ -12,7 +11,7 @@ data class Share(
var lastVisited: String? = null, var lastVisited: String? = null,
var expires: String? = null, var expires: String? = null,
var visitCount: Long? = null, var visitCount: Long? = null,
private val entries: MutableList<Entry> = mutableListOf() private val tracks: MutableList<Track> = mutableListOf()
) : Serializable, GenericEntry() { ) : Serializable, GenericEntry() {
override val name: String? override val name: String?
get() { get() {
@ -22,12 +21,12 @@ data class Share(
return null return null
} }
fun getEntries(): List<Entry> { fun getEntries(): List<Track> {
return entries.toList() return tracks.toList()
} }
fun addEntry(entry: Entry) { fun addEntry(track: Track) {
entries.add(entry) tracks.add(track)
} }
companion object { companion object {

View File

@ -0,0 +1,65 @@
package org.moire.ultrasonic.domain
import androidx.room.Entity
import androidx.room.PrimaryKey
import java.io.Serializable
import java.util.Date
@Entity
data class Track(
@PrimaryKey override var id: String,
override var parent: String? = null,
override var isDirectory: Boolean = false,
override var title: String? = null,
override var album: String? = null,
var albumId: String? = null,
override var artist: String? = null,
override var artistId: String? = null,
var track: Int? = null,
override var year: Int? = null,
override var genre: String? = null,
var contentType: String? = null,
var suffix: String? = null,
var transcodedContentType: String? = null,
var transcodedSuffix: String? = null,
override var coverArt: String? = null,
var size: Long? = null,
override var songCount: Long? = null,
override var duration: Int? = null,
var bitRate: Int? = null,
override var path: String? = null,
override var isVideo: Boolean = false,
override var starred: Boolean = false,
override var discNumber: Int? = null,
var type: String? = null,
override var created: Date? = null,
override var closeness: Int = 0,
var bookmarkPosition: Int = 0,
var userRating: Int? = null,
var averageRating: Float? = null,
override var name: String? = null
) : Serializable, MusicDirectory.Child() {
fun setDuration(duration: Long) {
this.duration = duration.toInt()
}
companion object {
private const val serialVersionUID = -3339106650010798108L
}
fun compareTo(other: Track): 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 Track)
}

View File

@ -1,41 +1,24 @@
<?xml version="1.0" ?> <?xml version='1.0' encoding='UTF-8'?>
<SmellBaseline> <SmellBaseline>
<ManuallySuppressedIssues></ManuallySuppressedIssues> <ManuallySuppressedIssues/>
<CurrentIssues> <CurrentIssues>
<ID>ComplexCondition:DownloadHandler.kt$DownloadHandler.&lt;no name provided&gt;$!append &amp;&amp; !playNext &amp;&amp; !unpin &amp;&amp; !background</ID> <ID>ComplexCondition:DownloadHandler.kt$DownloadHandler.&lt;no name provided>$!append &amp;&amp; !playNext &amp;&amp; !unpin &amp;&amp; !background</ID>
<ID>ComplexCondition:FilePickerAdapter.kt$FilePickerAdapter$currentDirectory.absolutePath == "/" || currentDirectory.absolutePath == "/storage" || currentDirectory.absolutePath == "/storage/emulated" || currentDirectory.absolutePath == "/mnt"</ID>
<ID>ComplexMethod:DownloadFile.kt$DownloadFile.DownloadTask$override fun execute()</ID> <ID>ComplexMethod:DownloadFile.kt$DownloadFile.DownloadTask$override fun execute()</ID>
<ID>ComplexMethod:FilePickerAdapter.kt$FilePickerAdapter$private fun fileLister(currentDirectory: File)</ID> <ID>ImplicitDefaultLocale:EditServerFragment.kt$EditServerFragment.&lt;no name provided>$String.format( "%s %s", resources.getString(R.string.settings_connection_failure), getErrorMessage(error) )</ID>
<ID>ComplexMethod:SongView.kt$SongView$fun setSong(song: MusicDirectory.Entry, checkable: Boolean, draggable: Boolean)</ID>
<ID>ComplexMethod:TrackCollectionFragment.kt$TrackCollectionFragment$private fun enableButtons()</ID>
<ID>ComplexMethod:TrackCollectionFragment.kt$TrackCollectionFragment$private fun updateInterfaceWithEntries(musicDirectory: MusicDirectory)</ID>
<ID>ImplicitDefaultLocale:DownloadFile.kt$DownloadFile$String.format("DownloadFile (%s)", song)</ID>
<ID>ImplicitDefaultLocale:DownloadFile.kt$DownloadFile.DownloadTask$String.format("Download of '%s' was cancelled", song)</ID>
<ID>ImplicitDefaultLocale:DownloadFile.kt$DownloadFile.DownloadTask$String.format("DownloadTask (%s)", song)</ID>
<ID>ImplicitDefaultLocale:EditServerFragment.kt$EditServerFragment.&lt;no name provided&gt;$String.format( "%s %s", resources.getString(R.string.settings_connection_failure), getErrorMessage(error) )</ID>
<ID>ImplicitDefaultLocale:FileLoggerTree.kt$FileLoggerTree$String.format("Failed to write log to %s", file)</ID> <ID>ImplicitDefaultLocale:FileLoggerTree.kt$FileLoggerTree$String.format("Failed to write log to %s", file)</ID>
<ID>ImplicitDefaultLocale:FileLoggerTree.kt$FileLoggerTree$String.format("Log file rotated, logging into file %s", file?.name)</ID> <ID>ImplicitDefaultLocale:FileLoggerTree.kt$FileLoggerTree$String.format("Log file rotated, logging into file %s", file?.name)</ID>
<ID>ImplicitDefaultLocale:FileLoggerTree.kt$FileLoggerTree$String.format("Logging into file %s", file?.name)</ID> <ID>ImplicitDefaultLocale:FileLoggerTree.kt$FileLoggerTree$String.format("Logging into file %s", file?.name)</ID>
<ID>ImplicitDefaultLocale:LocalMediaPlayer.kt$LocalMediaPlayer.BufferTask$String.format("BufferTask (%s)", downloadFile)</ID> <ID>ImplicitDefaultLocale:LocalMediaPlayer.kt$LocalMediaPlayer.BufferTask$String.format("BufferTask (%s)", downloadFile)</ID>
<ID>ImplicitDefaultLocale:LocalMediaPlayer.kt$LocalMediaPlayer.CheckCompletionTask$String.format("CheckCompletionTask (%s)", downloadFile)</ID> <ID>ImplicitDefaultLocale:LocalMediaPlayer.kt$LocalMediaPlayer.CheckCompletionTask$String.format("CheckCompletionTask (%s)", downloadFile)</ID>
<ID>ImplicitDefaultLocale:ShareHandler.kt$ShareHandler$String.format("%d:%s", timeSpanAmount, timeSpanType)</ID> <ID>ImplicitDefaultLocale:ShareHandler.kt$ShareHandler$String.format("%d:%s", timeSpanAmount, timeSpanType)</ID>
<ID>ImplicitDefaultLocale:SongView.kt$SongView$String.format("%02d.", trackNumber)</ID>
<ID>ImplicitDefaultLocale:SongView.kt$SongView$String.format("%s ", bitRate)</ID>
<ID>ImplicitDefaultLocale:SongView.kt$SongView$String.format("%s &gt; %s", suffix, transcodedSuffix)</ID>
<ID>LargeClass:TrackCollectionFragment.kt$TrackCollectionFragment : Fragment</ID>
<ID>LongMethod:DownloadFile.kt$DownloadFile.DownloadTask$override fun execute()</ID> <ID>LongMethod:DownloadFile.kt$DownloadFile.DownloadTask$override fun execute()</ID>
<ID>LongMethod:EditServerFragment.kt$EditServerFragment$override fun onViewCreated(view: View, savedInstanceState: Bundle?)</ID> <ID>LongMethod:EditServerFragment.kt$EditServerFragment$override fun onViewCreated(view: View, savedInstanceState: Bundle?)</ID>
<ID>LongMethod:LocalMediaPlayer.kt$LocalMediaPlayer$@Synchronized private fun doPlay(downloadFile: DownloadFile, position: Int, start: Boolean)</ID> <ID>LongMethod:LocalMediaPlayer.kt$LocalMediaPlayer$@Synchronized private fun doPlay(downloadFile: DownloadFile, position: Int, start: Boolean)</ID>
<ID>LongMethod:NavigationActivity.kt$NavigationActivity$override fun onCreate(savedInstanceState: Bundle?)</ID> <ID>LongMethod:NavigationActivity.kt$NavigationActivity$override fun onCreate(savedInstanceState: Bundle?)</ID>
<ID>LongMethod:ShareHandler.kt$ShareHandler$private fun showDialog( fragment: Fragment, shareDetails: ShareDetails, swipe: SwipeRefreshLayout?, cancellationToken: CancellationToken )</ID> <ID>LongMethod:ShareHandler.kt$ShareHandler$private fun showDialog( fragment: Fragment, shareDetails: ShareDetails, swipe: SwipeRefreshLayout?, cancellationToken: CancellationToken )</ID>
<ID>LongMethod:SongView.kt$SongView$fun setSong(song: MusicDirectory.Entry, checkable: Boolean, draggable: Boolean)</ID> <ID>LongParameterList:ServerRowAdapter.kt$ServerRowAdapter$( private var context: Context, private var data: Array&lt;ServerSetting>, private val model: ServerSettingsModel, private val activeServerProvider: ActiveServerProvider, private val manageMode: Boolean, private val serverDeletedCallback: ((Int) -> Unit), private val serverEditRequestedCallback: ((Int) -> Unit) )</ID>
<ID>LongMethod:TrackCollectionFragment.kt$TrackCollectionFragment$override fun onContextItemSelected(menuItem: MenuItem): Boolean</ID>
<ID>LongMethod:TrackCollectionFragment.kt$TrackCollectionFragment$override fun onViewCreated(view: View, savedInstanceState: Bundle?)</ID>
<ID>LongMethod:TrackCollectionFragment.kt$TrackCollectionFragment$private fun updateDisplay(refresh: Boolean)</ID>
<ID>LongMethod:TrackCollectionFragment.kt$TrackCollectionFragment$private fun updateInterfaceWithEntries(musicDirectory: MusicDirectory)</ID>
<ID>LongParameterList:ServerRowAdapter.kt$ServerRowAdapter$( private var context: Context, private var data: Array&lt;ServerSetting&gt;, private val model: ServerSettingsModel, private val activeServerProvider: ActiveServerProvider, private val manageMode: Boolean, private val serverDeletedCallback: ((Int) -&gt; Unit), private val serverEditRequestedCallback: ((Int) -&gt; Unit) )</ID>
<ID>MagicNumber:ActiveServerProvider.kt$ActiveServerProvider$8192</ID> <ID>MagicNumber:ActiveServerProvider.kt$ActiveServerProvider$8192</ID>
<ID>MagicNumber:LocalMediaPlayer.kt$LocalMediaPlayer.&lt;no name provided&gt;$60000</ID> <ID>MagicNumber:LocalMediaPlayer.kt$LocalMediaPlayer.&lt;no name provided>$60000</ID>
<ID>MagicNumber:LocalMediaPlayer.kt$LocalMediaPlayer.BufferTask$100000</ID> <ID>MagicNumber:LocalMediaPlayer.kt$LocalMediaPlayer.BufferTask$100000</ID>
<ID>MagicNumber:LocalMediaPlayer.kt$LocalMediaPlayer.BufferTask$8</ID> <ID>MagicNumber:LocalMediaPlayer.kt$LocalMediaPlayer.BufferTask$8</ID>
<ID>MagicNumber:LocalMediaPlayer.kt$LocalMediaPlayer.BufferTask$86400L</ID> <ID>MagicNumber:LocalMediaPlayer.kt$LocalMediaPlayer.BufferTask$86400L</ID>
@ -44,25 +27,17 @@
<ID>MagicNumber:MediaPlayerService.kt$MediaPlayerService$3</ID> <ID>MagicNumber:MediaPlayerService.kt$MediaPlayerService$3</ID>
<ID>MagicNumber:MediaPlayerService.kt$MediaPlayerService$4</ID> <ID>MagicNumber:MediaPlayerService.kt$MediaPlayerService$4</ID>
<ID>MagicNumber:RESTMusicService.kt$RESTMusicService$206</ID> <ID>MagicNumber:RESTMusicService.kt$RESTMusicService$206</ID>
<ID>MagicNumber:SongView.kt$SongView$3</ID>
<ID>MagicNumber:SongView.kt$SongView$4</ID>
<ID>MagicNumber:SongView.kt$SongView$60</ID>
<ID>NestedBlockDepth:DownloadFile.kt$DownloadFile.DownloadTask$override fun execute()</ID> <ID>NestedBlockDepth:DownloadFile.kt$DownloadFile.DownloadTask$override fun execute()</ID>
<ID>NestedBlockDepth:DownloadHandler.kt$DownloadHandler$private fun downloadRecursively( fragment: Fragment, id: String, name: String?, isShare: Boolean, isDirectory: Boolean, save: Boolean, append: Boolean, autoPlay: Boolean, shuffle: Boolean, background: Boolean, playNext: Boolean, unpin: Boolean, isArtist: Boolean )</ID> <ID>NestedBlockDepth:DownloadHandler.kt$DownloadHandler$private fun downloadRecursively( fragment: Fragment, id: String, name: String?, isShare: Boolean, isDirectory: Boolean, save: Boolean, append: Boolean, autoPlay: Boolean, shuffle: Boolean, background: Boolean, playNext: Boolean, unpin: Boolean, isArtist: Boolean )</ID>
<ID>NestedBlockDepth:MediaPlayerService.kt$MediaPlayerService$private fun setupOnSongCompletedHandler()</ID> <ID>NestedBlockDepth:MediaPlayerService.kt$MediaPlayerService$private fun setupOnSongCompletedHandler()</ID>
<ID>ReturnCount:ServerRowAdapter.kt$ServerRowAdapter$ private fun popupMenuItemClick(menuItem: MenuItem, position: Int): Boolean</ID>
<ID>ReturnCount:TrackCollectionFragment.kt$TrackCollectionFragment$override fun onContextItemSelected(menuItem: MenuItem): Boolean</ID>
<ID>TooGenericExceptionCaught:DownloadFile.kt$DownloadFile$e: Exception</ID>
<ID>TooGenericExceptionCaught:FileLoggerTree.kt$FileLoggerTree$x: Throwable</ID> <ID>TooGenericExceptionCaught:FileLoggerTree.kt$FileLoggerTree$x: Throwable</ID>
<ID>TooGenericExceptionCaught:LocalMediaPlayer.kt$LocalMediaPlayer$ex: Exception</ID> <ID>TooGenericExceptionCaught:LocalMediaPlayer.kt$LocalMediaPlayer$ex: Exception</ID>
<ID>TooGenericExceptionCaught:LocalMediaPlayer.kt$LocalMediaPlayer$exception: Throwable</ID> <ID>TooGenericExceptionCaught:LocalMediaPlayer.kt$LocalMediaPlayer$exception: Throwable</ID>
<ID>TooGenericExceptionCaught:LocalMediaPlayer.kt$LocalMediaPlayer$x: Exception</ID> <ID>TooGenericExceptionCaught:LocalMediaPlayer.kt$LocalMediaPlayer$x: Exception</ID>
<ID>TooGenericExceptionCaught:LocalMediaPlayer.kt$LocalMediaPlayer.PositionCache$e: Exception</ID> <ID>TooGenericExceptionCaught:LocalMediaPlayer.kt$LocalMediaPlayer.PositionCache$e: Exception</ID>
<ID>TooGenericExceptionCaught:SongView.kt$SongView$e: Exception</ID> <ID>TooGenericExceptionThrown:DownloadFile.kt$DownloadFile.DownloadTask$throw RuntimeException( String.format(Locale.ROOT, "Download of '%s' was cancelled", track) )</ID>
<ID>TooGenericExceptionThrown:DownloadFile.kt$DownloadFile.DownloadTask$throw Exception(String.format("Download of '%s' was cancelled", song))</ID>
<ID>TooManyFunctions:MediaPlayerService.kt$MediaPlayerService : Service</ID> <ID>TooManyFunctions:MediaPlayerService.kt$MediaPlayerService : Service</ID>
<ID>TooManyFunctions:RESTMusicService.kt$RESTMusicService : MusicService</ID> <ID>TooManyFunctions:RESTMusicService.kt$RESTMusicService : MusicService</ID>
<ID>TooManyFunctions:TrackCollectionFragment.kt$TrackCollectionFragment : Fragment</ID>
<ID>UtilityClassWithPublicConstructor:FragmentTitle.kt$FragmentTitle</ID> <ID>UtilityClassWithPublicConstructor:FragmentTitle.kt$FragmentTitle</ID>
</CurrentIssues> </CurrentIssues>
</SmellBaseline> </SmellBaseline>

View File

@ -14,7 +14,7 @@ import android.widget.RemoteViews;
import org.moire.ultrasonic.R; import org.moire.ultrasonic.R;
import org.moire.ultrasonic.activity.NavigationActivity; import org.moire.ultrasonic.activity.NavigationActivity;
import org.moire.ultrasonic.domain.MusicDirectory; import org.moire.ultrasonic.domain.Track;
import org.moire.ultrasonic.imageloader.BitmapUtils; import org.moire.ultrasonic.imageloader.BitmapUtils;
import org.moire.ultrasonic.receiver.MediaButtonIntentReceiver; import org.moire.ultrasonic.receiver.MediaButtonIntentReceiver;
import org.moire.ultrasonic.service.MediaPlayerController; import org.moire.ultrasonic.service.MediaPlayerController;
@ -73,7 +73,7 @@ public class UltrasonicAppWidgetProvider extends AppWidgetProvider
/** /**
* Handle a change notification coming over from {@link MediaPlayerController} * Handle a change notification coming over from {@link MediaPlayerController}
*/ */
public void notifyChange(Context context, MusicDirectory.Entry currentSong, boolean playing, boolean setAlbum) public void notifyChange(Context context, Track currentSong, boolean playing, boolean setAlbum)
{ {
if (hasInstances(context)) if (hasInstances(context))
{ {
@ -100,7 +100,7 @@ public class UltrasonicAppWidgetProvider extends AppWidgetProvider
/** /**
* Update all active widget instances by pushing changes * Update all active widget instances by pushing changes
*/ */
private void performUpdate(Context context, MusicDirectory.Entry currentSong, boolean playing, boolean setAlbum) private void performUpdate(Context context, Track currentSong, boolean playing, boolean setAlbum)
{ {
final Resources res = context.getResources(); final Resources res = context.getResources();
final RemoteViews views = new RemoteViews(context.getPackageName(), this.layoutId); final RemoteViews views = new RemoteViews(context.getPackageName(), this.layoutId);

View File

@ -1,16 +1,16 @@
package org.moire.ultrasonic.receiver; package org.moire.ultrasonic.receiver;
import static org.koin.java.KoinJavaComponent.inject;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import org.moire.ultrasonic.domain.MusicDirectory.Entry; import org.moire.ultrasonic.domain.Track;
import org.moire.ultrasonic.service.MediaPlayerController; import org.moire.ultrasonic.service.MediaPlayerController;
import kotlin.Lazy; import kotlin.Lazy;
import static org.koin.java.KoinJavaComponent.inject;
public class A2dpIntentReceiver extends BroadcastReceiver public class A2dpIntentReceiver extends BroadcastReceiver
{ {
private static final String PLAYSTATUS_RESPONSE = "com.android.music.playstatusresponse"; private static final String PLAYSTATUS_RESPONSE = "com.android.music.playstatusresponse";
@ -21,7 +21,7 @@ public class A2dpIntentReceiver extends BroadcastReceiver
{ {
if (mediaPlayerControllerLazy.getValue().getCurrentPlaying() == null) return; if (mediaPlayerControllerLazy.getValue().getCurrentPlaying() == null) return;
Entry song = mediaPlayerControllerLazy.getValue().getCurrentPlaying().getSong(); Track song = mediaPlayerControllerLazy.getValue().getCurrentPlaying().getTrack();
if (song == null) return; if (song == null) return;
Intent avrcpIntent = new Intent(PLAYSTATUS_RESPONSE); Intent avrcpIntent = new Intent(PLAYSTATUS_RESPONSE);

View File

@ -234,7 +234,7 @@ public class JukeboxMediaPlayer
List<String> ids = new ArrayList<>(); List<String> ids = new ArrayList<>();
for (DownloadFile file : downloader.getAll()) for (DownloadFile file : downloader.getAll())
{ {
ids.add(file.getSong().getId()); ids.add(file.getTrack().getId());
} }
tasks.add(new SetPlaylist(ids)); tasks.add(new SetPlaylist(ids));

View File

@ -18,7 +18,7 @@ public class Scrobbler
{ {
if (song == null || !ActiveServerProvider.Companion.isScrobblingEnabled()) return; if (song == null || !ActiveServerProvider.Companion.isScrobblingEnabled()) return;
final String id = song.getSong().getId(); final String id = song.getTrack().getId();
if (id == null) return; if (id == null) return;
// Avoid duplicate registrations. // Avoid duplicate registrations.

View File

@ -1,6 +1,6 @@
package org.moire.ultrasonic.service; package org.moire.ultrasonic.service;
import org.moire.ultrasonic.domain.MusicDirectory; import org.moire.ultrasonic.domain.Track;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
@ -13,7 +13,7 @@ public class State implements Serializable
{ {
public static final long serialVersionUID = -6346438781062572270L; public static final long serialVersionUID = -6346438781062572270L;
public List<MusicDirectory.Entry> songs = new ArrayList<>(); public List<Track> songs = new ArrayList<>();
public int currentPlayingIndex; public int currentPlayingIndex;
public int currentPlayingPosition; public int currentPlayingPosition;
} }

View File

@ -1,6 +1,6 @@
package org.moire.ultrasonic.util; package org.moire.ultrasonic.util;
import org.moire.ultrasonic.domain.MusicDirectory; import org.moire.ultrasonic.domain.Track;
import java.util.List; import java.util.List;
@ -12,5 +12,5 @@ public class ShareDetails
public String Description; public String Description;
public boolean ShareOnServer; public boolean ShareOnServer;
public long Expiration; public long Expiration;
public List<MusicDirectory.Entry> Entries; public List<Track> Entries;
} }

View File

@ -20,6 +20,7 @@ package org.moire.ultrasonic.util;
import org.moire.ultrasonic.data.ActiveServerProvider; import org.moire.ultrasonic.data.ActiveServerProvider;
import org.moire.ultrasonic.domain.MusicDirectory; import org.moire.ultrasonic.domain.MusicDirectory;
import org.moire.ultrasonic.domain.Track;
import org.moire.ultrasonic.service.MusicService; import org.moire.ultrasonic.service.MusicService;
import org.moire.ultrasonic.service.MusicServiceFactory; import org.moire.ultrasonic.service.MusicServiceFactory;
@ -40,7 +41,7 @@ public class ShufflePlayBuffer
private static final int CAPACITY = 50; private static final int CAPACITY = 50;
private static final int REFILL_THRESHOLD = 40; private static final int REFILL_THRESHOLD = 40;
private final List<MusicDirectory.Entry> buffer = new ArrayList<>(); private final List<Track> buffer = new ArrayList<>();
private ScheduledExecutorService executorService; private ScheduledExecutorService executorService;
private int currentServer; private int currentServer;
@ -64,11 +65,11 @@ public class ShufflePlayBuffer
Timber.i("ShufflePlayBuffer destroyed"); Timber.i("ShufflePlayBuffer destroyed");
} }
public List<MusicDirectory.Entry> get(int size) public List<Track> get(int size)
{ {
clearBufferIfNecessary(); clearBufferIfNecessary();
List<MusicDirectory.Entry> result = new ArrayList<>(size); List<Track> result = new ArrayList<>(size);
synchronized (buffer) synchronized (buffer)
{ {
while (!buffer.isEmpty() && result.size() < size) while (!buffer.isEmpty() && result.size() < size)

View File

@ -1,6 +1,6 @@
package org.moire.ultrasonic.util; package org.moire.ultrasonic.util;
import org.moire.ultrasonic.domain.MusicDirectory; import org.moire.ultrasonic.domain.Track;
import org.moire.ultrasonic.service.DownloadFile; import org.moire.ultrasonic.service.DownloadFile;
import org.moire.ultrasonic.service.Supplier; import org.moire.ultrasonic.service.Supplier;
@ -180,7 +180,7 @@ public class StreamProxy implements Runnable
{ {
Timber.i("Streaming song in background"); Timber.i("Streaming song in background");
DownloadFile downloadFile = currentPlaying == null? null : currentPlaying.get(); DownloadFile downloadFile = currentPlaying == null? null : currentPlaying.get();
MusicDirectory.Entry song = downloadFile.getSong(); Track song = downloadFile.getTrack();
long fileSize = downloadFile.getBitRate() * ((song.getDuration() != null) ? song.getDuration() : 0) * 1000 / 8; long fileSize = downloadFile.getBitRate() * ((song.getDuration() != null) ? song.getDuration() : 0) * 1000 / 8;
Timber.i("Streaming fileSize: %d", fileSize); Timber.i("Streaming fileSize: %d", fileSize);

View File

@ -20,7 +20,7 @@ import androidx.recyclerview.widget.RecyclerView
import com.drakeet.multitype.ItemViewBinder import com.drakeet.multitype.ItemViewBinder
import org.koin.core.component.KoinComponent import org.koin.core.component.KoinComponent
import org.moire.ultrasonic.R import org.moire.ultrasonic.R
import org.moire.ultrasonic.domain.MusicDirectory import org.moire.ultrasonic.domain.Album
import org.moire.ultrasonic.imageloader.ImageLoader import org.moire.ultrasonic.imageloader.ImageLoader
import org.moire.ultrasonic.service.MusicServiceFactory.getMusicService import org.moire.ultrasonic.service.MusicServiceFactory.getMusicService
import org.moire.ultrasonic.util.Settings.shouldUseId3Tags import org.moire.ultrasonic.util.Settings.shouldUseId3Tags
@ -31,11 +31,11 @@ import timber.log.Timber
* Creates a Row in a RecyclerView which contains the details of an Album * Creates a Row in a RecyclerView which contains the details of an Album
*/ */
class AlbumRowBinder( class AlbumRowBinder(
val onItemClick: (MusicDirectory.Album) -> Unit, val onItemClick: (Album) -> Unit,
val onContextMenuClick: (MenuItem, MusicDirectory.Album) -> Boolean, val onContextMenuClick: (MenuItem, Album) -> Boolean,
private val imageLoader: ImageLoader, private val imageLoader: ImageLoader,
context: Context context: Context
) : ItemViewBinder<MusicDirectory.Album, AlbumRowBinder.ViewHolder>(), KoinComponent { ) : ItemViewBinder<Album, AlbumRowBinder.ViewHolder>(), KoinComponent {
private val starDrawable: Drawable = private val starDrawable: Drawable =
Util.getDrawableFromAttribute(context, R.attr.star_full) Util.getDrawableFromAttribute(context, R.attr.star_full)
@ -46,7 +46,7 @@ class AlbumRowBinder(
val layout = R.layout.list_item_album val layout = R.layout.list_item_album
val contextMenuLayout = R.menu.context_menu_artist val contextMenuLayout = R.menu.context_menu_artist
override fun onBindViewHolder(holder: ViewHolder, item: MusicDirectory.Album) { override fun onBindViewHolder(holder: ViewHolder, item: Album) {
holder.album.text = item.title holder.album.text = item.title
holder.artist.text = item.artist holder.artist.text = item.artist
holder.details.setOnClickListener { onItemClick(item) } holder.details.setOnClickListener { onItemClick(item) }
@ -86,7 +86,7 @@ class AlbumRowBinder(
/** /**
* Handles the star / unstar action for an album * Handles the star / unstar action for an album
*/ */
private fun onStarClick(entry: MusicDirectory.Album, star: ImageView) { private fun onStarClick(entry: Album, star: ImageView) {
entry.starred = !entry.starred entry.starred = !entry.starred
star.setImageDrawable(if (entry.starred) starDrawable else starHollowDrawable) star.setImageDrawable(if (entry.starred) starDrawable else starHollowDrawable)
val musicService = getMusicService() val musicService = getMusicService()

View File

@ -12,7 +12,7 @@ import org.koin.core.component.KoinComponent
import org.koin.core.component.inject import org.koin.core.component.inject
import org.moire.ultrasonic.R import org.moire.ultrasonic.R
import org.moire.ultrasonic.domain.Identifiable import org.moire.ultrasonic.domain.Identifiable
import org.moire.ultrasonic.domain.MusicDirectory import org.moire.ultrasonic.domain.Track
import org.moire.ultrasonic.service.DownloadFile import org.moire.ultrasonic.service.DownloadFile
import org.moire.ultrasonic.service.Downloader import org.moire.ultrasonic.service.Downloader
@ -45,7 +45,7 @@ class TrackViewBinder(
val diffAdapter = adapter as BaseAdapter<*> val diffAdapter = adapter as BaseAdapter<*>
when (item) { when (item) {
is MusicDirectory.Entry -> { is Track -> {
downloadFile = downloader.getDownloadFileForSong(item) downloadFile = downloader.getDownloadFileForSong(item)
} }
is DownloadFile -> { is DownloadFile -> {
@ -77,7 +77,7 @@ class TrackViewBinder(
} }
} else { } else {
// Minimize or maximize the Text view (if song title is very long) // Minimize or maximize the Text view (if song title is very long)
if (!downloadFile.song.isDirectory) { if (!downloadFile.track.isDirectory) {
holder.maximizeOrMinimize() holder.maximizeOrMinimize()
} }
} }
@ -86,7 +86,7 @@ class TrackViewBinder(
} }
holder.itemView.setOnClickListener { holder.itemView.setOnClickListener {
if (checkable && !downloadFile.song.isVideo) { if (checkable && !downloadFile.track.isVideo) {
val nowChecked = !holder.check.isChecked val nowChecked = !holder.check.isChecked
holder.isChecked = nowChecked holder.isChecked = nowChecked
} else { } else {

View File

@ -15,7 +15,7 @@ import io.reactivex.rxjava3.disposables.Disposable
import org.koin.core.component.KoinComponent import org.koin.core.component.KoinComponent
import org.moire.ultrasonic.R import org.moire.ultrasonic.R
import org.moire.ultrasonic.data.ActiveServerProvider import org.moire.ultrasonic.data.ActiveServerProvider
import org.moire.ultrasonic.domain.MusicDirectory import org.moire.ultrasonic.domain.Track
import org.moire.ultrasonic.service.DownloadFile import org.moire.ultrasonic.service.DownloadFile
import org.moire.ultrasonic.service.DownloadStatus import org.moire.ultrasonic.service.DownloadStatus
import org.moire.ultrasonic.service.MusicServiceFactory import org.moire.ultrasonic.service.MusicServiceFactory
@ -44,7 +44,7 @@ class TrackViewHolder(val view: View) : RecyclerView.ViewHolder(view), Checkable
var duration: TextView = view.findViewById(R.id.song_duration) var duration: TextView = view.findViewById(R.id.song_duration)
var progress: TextView = view.findViewById(R.id.song_status) var progress: TextView = view.findViewById(R.id.song_status)
var entry: MusicDirectory.Entry? = null var entry: Track? = null
private set private set
var downloadFile: DownloadFile? = null var downloadFile: DownloadFile? = null
private set private set
@ -67,7 +67,7 @@ class TrackViewHolder(val view: View) : RecyclerView.ViewHolder(view), Checkable
isSelected: Boolean = false isSelected: Boolean = false
) { ) {
val useFiveStarRating = Settings.useFiveStarRating val useFiveStarRating = Settings.useFiveStarRating
val song = file.song val song = file.track
downloadFile = file downloadFile = file
entry = song entry = song
@ -131,7 +131,7 @@ class TrackViewHolder(val view: View) : RecyclerView.ViewHolder(view), Checkable
} }
} }
private fun setupStarButtons(song: MusicDirectory.Entry, useFiveStarRating: Boolean) { private fun setupStarButtons(song: Track, useFiveStarRating: Boolean) {
if (useFiveStarRating) { if (useFiveStarRating) {
// Hide single star // Hide single star
star.isVisible = false star.isVisible = false

View File

@ -4,8 +4,9 @@
package org.moire.ultrasonic.domain package org.moire.ultrasonic.domain
import org.moire.ultrasonic.api.subsonic.models.Album import org.moire.ultrasonic.api.subsonic.models.Album
typealias DomainAlbum = org.moire.ultrasonic.domain.Album
fun Album.toDomainEntity(): MusicDirectory.Album = MusicDirectory.Album( fun Album.toDomainEntity(): DomainAlbum = Album(
id = this@toDomainEntity.id, id = this@toDomainEntity.id,
title = this@toDomainEntity.name ?: this@toDomainEntity.title, title = this@toDomainEntity.name ?: this@toDomainEntity.title,
album = this@toDomainEntity.album, album = this@toDomainEntity.album,
@ -24,4 +25,4 @@ fun Album.toMusicDirectoryDomainEntity(): MusicDirectory = MusicDirectory().appl
addAll(this@toMusicDirectoryDomainEntity.songList.map { it.toTrackEntity() }) addAll(this@toMusicDirectoryDomainEntity.songList.map { it.toTrackEntity() })
} }
fun List<Album>.toDomainEntityList(): List<MusicDirectory.Album> = this.map { it.toDomainEntity() } fun List<Album>.toDomainEntityList(): List<DomainAlbum> = this.map { it.toDomainEntity() }

View File

@ -24,6 +24,6 @@ fun APIArtist.toMusicDirectoryDomainEntity(): MusicDirectory = MusicDirectory().
addAll(this@toMusicDirectoryDomainEntity.albumsList.map { it.toDomainEntity() }) addAll(this@toMusicDirectoryDomainEntity.albumsList.map { it.toDomainEntity() })
} }
fun APIArtist.toDomainEntityList(): List<MusicDirectory.Album> { fun APIArtist.toDomainEntityList(): List<Album> {
return this.albumsList.map { it.toDomainEntity() } return this.albumsList.map { it.toDomainEntity() }
} }

View File

@ -10,7 +10,7 @@ fun ApiBookmark.toDomainEntity(): Bookmark = Bookmark(
comment = this@toDomainEntity.comment, comment = this@toDomainEntity.comment,
created = this@toDomainEntity.created?.time, created = this@toDomainEntity.created?.time,
changed = this@toDomainEntity.changed?.time, changed = this@toDomainEntity.changed?.time,
entry = this@toDomainEntity.entry.toTrackEntity() track = this@toDomainEntity.entry.toTrackEntity()
) )
fun List<ApiBookmark>.toDomainEntitiesList(): List<Bookmark> = map { it.toDomainEntity() } fun List<ApiBookmark>.toDomainEntitiesList(): List<Bookmark> = map { it.toDomainEntity() }

View File

@ -26,12 +26,12 @@ internal val dateFormat: DateFormat by lazy {
SimpleDateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, Locale.getDefault()) SimpleDateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, Locale.getDefault())
} }
fun MusicDirectoryChild.toTrackEntity(): MusicDirectory.Entry = MusicDirectory.Entry(id).apply { fun MusicDirectoryChild.toTrackEntity(): Track = Track(id).apply {
populateCommonProps(this, this@toTrackEntity) populateCommonProps(this, this@toTrackEntity)
populateTrackProps(this, this@toTrackEntity) populateTrackProps(this, this@toTrackEntity)
} }
fun MusicDirectoryChild.toAlbumEntity(): MusicDirectory.Album = MusicDirectory.Album(id).apply { fun MusicDirectoryChild.toAlbumEntity(): Album = Album(id).apply {
populateCommonProps(this, this@toAlbumEntity) populateCommonProps(this, this@toAlbumEntity)
} }
@ -64,20 +64,20 @@ private fun populateCommonProps(
} }
private fun populateTrackProps( private fun populateTrackProps(
entry: MusicDirectory.Entry, track: Track,
source: MusicDirectoryChild source: MusicDirectoryChild
) { ) {
entry.size = source.size track.size = source.size
entry.contentType = source.contentType track.contentType = source.contentType
entry.suffix = source.suffix track.suffix = source.suffix
entry.transcodedContentType = source.transcodedContentType track.transcodedContentType = source.transcodedContentType
entry.transcodedSuffix = source.transcodedSuffix track.transcodedSuffix = source.transcodedSuffix
entry.track = source.track track.track = source.track
entry.albumId = source.albumId track.albumId = source.albumId
entry.bitRate = source.bitRate track.bitRate = source.bitRate
entry.type = source.type track.type = source.type
entry.userRating = source.userRating track.userRating = source.userRating
entry.averageRating = source.averageRating track.averageRating = source.averageRating
} }
fun List<MusicDirectoryChild>.toDomainEntityList(): List<MusicDirectory.Child> { fun List<MusicDirectoryChild>.toDomainEntityList(): List<MusicDirectory.Child> {
@ -93,7 +93,7 @@ fun List<MusicDirectoryChild>.toDomainEntityList(): List<MusicDirectory.Child> {
return newList return newList
} }
fun List<MusicDirectoryChild>.toTrackList(): List<MusicDirectory.Entry> = this.map { fun List<MusicDirectoryChild>.toTrackList(): List<Track> = this.map {
it.toTrackEntity() it.toTrackEntity()
} }

View File

@ -22,5 +22,5 @@ fun APIShare.toDomainEntity(): Share = Share(
url = this@toDomainEntity.url, url = this@toDomainEntity.url,
username = this@toDomainEntity.username, username = this@toDomainEntity.username,
visitCount = this@toDomainEntity.visitCount.toLong(), visitCount = this@toDomainEntity.visitCount.toLong(),
entries = this@toDomainEntity.items.toTrackList().toMutableList() tracks = this@toDomainEntity.items.toTrackList().toMutableList()
) )

View File

@ -15,14 +15,14 @@ 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.domain.MusicDirectory import org.moire.ultrasonic.domain.Album
import org.moire.ultrasonic.model.AlbumListModel import org.moire.ultrasonic.model.AlbumListModel
import org.moire.ultrasonic.util.Constants import org.moire.ultrasonic.util.Constants
/** /**
* Displays a list of Albums from the media library * Displays a list of Albums from the media library
*/ */
class AlbumListFragment : EntryListFragment<MusicDirectory.Album>() { class AlbumListFragment : EntryListFragment<Album>() {
/** /**
* The ViewModel to use to get the data * The ViewModel to use to get the data
@ -45,7 +45,7 @@ class AlbumListFragment : EntryListFragment<MusicDirectory.Album>() {
override fun getLiveData( override fun getLiveData(
args: Bundle?, args: Bundle?,
refresh: Boolean refresh: Boolean
): LiveData<List<MusicDirectory.Album>> { ): LiveData<List<Album>> {
if (args == null) throw IllegalArgumentException("Required arguments are missing") if (args == null) throw IllegalArgumentException("Required arguments are missing")
val refresh2 = args.getBoolean(Constants.INTENT_REFRESH) || refresh val refresh2 = args.getBoolean(Constants.INTENT_REFRESH) || refresh
@ -83,7 +83,7 @@ class AlbumListFragment : EntryListFragment<MusicDirectory.Album>() {
emptyTextView.setText(R.string.select_album_empty) emptyTextView.setText(R.string.select_album_empty)
} }
override fun onItemClick(item: MusicDirectory.Album) { override fun onItemClick(item: Album) {
val bundle = Bundle() val bundle = Bundle()
bundle.putString(Constants.INTENT_ID, item.id) bundle.putString(Constants.INTENT_ID, item.id)
bundle.putBoolean(Constants.INTENT_IS_ALBUM, item.isDirectory) bundle.putBoolean(Constants.INTENT_IS_ALBUM, item.isDirectory)

View File

@ -15,6 +15,7 @@ import kotlinx.coroutines.launch
import org.moire.ultrasonic.R import org.moire.ultrasonic.R
import org.moire.ultrasonic.adapters.BaseAdapter import org.moire.ultrasonic.adapters.BaseAdapter
import org.moire.ultrasonic.domain.MusicDirectory import org.moire.ultrasonic.domain.MusicDirectory
import org.moire.ultrasonic.domain.Track
import org.moire.ultrasonic.fragment.FragmentTitle.Companion.setTitle import org.moire.ultrasonic.fragment.FragmentTitle.Companion.setTitle
/** /**
@ -61,7 +62,7 @@ class BookmarksFragment : TrackCollectionFragment() {
/** /**
* Custom playback function which uses the restore functionality. A bit of a hack.. * Custom playback function which uses the restore functionality. A bit of a hack..
*/ */
private fun playNow(songs: List<MusicDirectory.Entry>) { private fun playNow(songs: List<Track>) {
if (songs.isNotEmpty()) { if (songs.isNotEmpty()) {
val position = songs[0].bookmarkPosition val position = songs[0].bookmarkPosition

View File

@ -105,7 +105,7 @@ class NowPlayingFragment : Fragment() {
val file = mediaPlayerController.currentPlaying val file = mediaPlayerController.currentPlaying
if (file != null) { if (file != null) {
val song = file.song val song = file.track
val title = song.title val title = song.title
val artist = song.artist val artist = song.artist

View File

@ -66,9 +66,9 @@ import org.moire.ultrasonic.audiofx.EqualizerController
import org.moire.ultrasonic.audiofx.VisualizerController import org.moire.ultrasonic.audiofx.VisualizerController
import org.moire.ultrasonic.data.ActiveServerProvider.Companion.isOffline import org.moire.ultrasonic.data.ActiveServerProvider.Companion.isOffline
import org.moire.ultrasonic.domain.Identifiable import org.moire.ultrasonic.domain.Identifiable
import org.moire.ultrasonic.domain.MusicDirectory
import org.moire.ultrasonic.domain.PlayerState import org.moire.ultrasonic.domain.PlayerState
import org.moire.ultrasonic.domain.RepeatMode import org.moire.ultrasonic.domain.RepeatMode
import org.moire.ultrasonic.domain.Track
import org.moire.ultrasonic.fragment.FragmentTitle.Companion.setTitle import org.moire.ultrasonic.fragment.FragmentTitle.Companion.setTitle
import org.moire.ultrasonic.service.DownloadFile import org.moire.ultrasonic.service.DownloadFile
import org.moire.ultrasonic.service.LocalMediaPlayer import org.moire.ultrasonic.service.LocalMediaPlayer
@ -118,7 +118,7 @@ class PlayerFragment :
private val imageLoaderProvider: ImageLoaderProvider by inject() private val imageLoaderProvider: ImageLoaderProvider by inject()
private lateinit var executorService: ScheduledExecutorService private lateinit var executorService: ScheduledExecutorService
private var currentPlaying: DownloadFile? = null private var currentPlaying: DownloadFile? = null
private var currentSong: MusicDirectory.Entry? = null private var currentSong: Track? = null
private lateinit var viewManager: LinearLayoutManager private lateinit var viewManager: LinearLayoutManager
private var rxBusSubscription: Disposable? = null private var rxBusSubscription: Disposable? = null
private var ioScope = CoroutineScope(Dispatchers.IO) private var ioScope = CoroutineScope(Dispatchers.IO)
@ -507,7 +507,7 @@ class PlayerFragment :
val downloadFile = mediaPlayerController.currentPlaying val downloadFile = mediaPlayerController.currentPlaying
if (downloadFile != null) { if (downloadFile != null) {
currentSong = downloadFile.song currentSong = downloadFile.track
} }
if (useFiveStarRating) starMenuItem.isVisible = false if (useFiveStarRating) starMenuItem.isVisible = false
@ -544,9 +544,9 @@ class PlayerFragment :
val downloadFile = viewAdapter.getCurrentList()[info!!.position] as DownloadFile val downloadFile = viewAdapter.getCurrentList()[info!!.position] as DownloadFile
val menuInflater = requireActivity().menuInflater val menuInflater = requireActivity().menuInflater
menuInflater.inflate(R.menu.nowplaying_context, menu) menuInflater.inflate(R.menu.nowplaying_context, menu)
val song: MusicDirectory.Entry? val song: Track?
song = downloadFile.song song = downloadFile.track
if (song.parent == null) { if (song.parent == null) {
val menuItem = menu.findItem(R.id.menu_show_album) val menuItem = menu.findItem(R.id.menu_show_album)
@ -571,21 +571,21 @@ class PlayerFragment :
@Suppress("ComplexMethod", "LongMethod", "ReturnCount") @Suppress("ComplexMethod", "LongMethod", "ReturnCount")
private fun menuItemSelected(menuItemId: Int, song: DownloadFile?): Boolean { private fun menuItemSelected(menuItemId: Int, song: DownloadFile?): Boolean {
var entry: MusicDirectory.Entry? = null var track: Track? = null
val bundle: Bundle val bundle: Bundle
if (song != null) { if (song != null) {
entry = song.song track = song.track
} }
when (menuItemId) { when (menuItemId) {
R.id.menu_show_artist -> { R.id.menu_show_artist -> {
if (entry == null) return false if (track == null) return false
if (Settings.shouldUseId3Tags) { if (Settings.shouldUseId3Tags) {
bundle = Bundle() bundle = Bundle()
bundle.putString(Constants.INTENT_ID, entry.artistId) bundle.putString(Constants.INTENT_ID, track.artistId)
bundle.putString(Constants.INTENT_NAME, entry.artist) bundle.putString(Constants.INTENT_NAME, track.artist)
bundle.putString(Constants.INTENT_PARENT_ID, entry.artistId) bundle.putString(Constants.INTENT_PARENT_ID, track.artistId)
bundle.putBoolean(Constants.INTENT_ARTIST, true) bundle.putBoolean(Constants.INTENT_ARTIST, true)
Navigation.findNavController(requireView()) Navigation.findNavController(requireView())
.navigate(R.id.playerToSelectAlbum, bundle) .navigate(R.id.playerToSelectAlbum, bundle)
@ -593,24 +593,24 @@ class PlayerFragment :
return true return true
} }
R.id.menu_show_album -> { R.id.menu_show_album -> {
if (entry == null) return false if (track == null) return false
val albumId = if (Settings.shouldUseId3Tags) entry.albumId else entry.parent val albumId = if (Settings.shouldUseId3Tags) track.albumId else track.parent
bundle = Bundle() bundle = Bundle()
bundle.putString(Constants.INTENT_ID, albumId) bundle.putString(Constants.INTENT_ID, albumId)
bundle.putString(Constants.INTENT_NAME, entry.album) bundle.putString(Constants.INTENT_NAME, track.album)
bundle.putString(Constants.INTENT_PARENT_ID, entry.parent) bundle.putString(Constants.INTENT_PARENT_ID, track.parent)
bundle.putBoolean(Constants.INTENT_IS_ALBUM, true) bundle.putBoolean(Constants.INTENT_IS_ALBUM, true)
Navigation.findNavController(requireView()) Navigation.findNavController(requireView())
.navigate(R.id.playerToSelectAlbum, bundle) .navigate(R.id.playerToSelectAlbum, bundle)
return true return true
} }
R.id.menu_lyrics -> { R.id.menu_lyrics -> {
if (entry == null) return false if (track == null) return false
bundle = Bundle() bundle = Bundle()
bundle.putString(Constants.INTENT_ARTIST, entry.artist) bundle.putString(Constants.INTENT_ARTIST, track.artist)
bundle.putString(Constants.INTENT_TITLE, entry.title) bundle.putString(Constants.INTENT_TITLE, track.title)
Navigation.findNavController(requireView()).navigate(R.id.playerToLyrics, bundle) Navigation.findNavController(requireView()).navigate(R.id.playerToLyrics, bundle)
return true return true
} }
@ -746,22 +746,22 @@ class PlayerFragment :
} }
R.id.menu_item_share -> { R.id.menu_item_share -> {
val mediaPlayerController = mediaPlayerController val mediaPlayerController = mediaPlayerController
val entries: MutableList<MusicDirectory.Entry?> = ArrayList() val tracks: MutableList<Track?> = ArrayList()
val downloadServiceSongs = mediaPlayerController.playList val downloadServiceSongs = mediaPlayerController.playList
for (downloadFile in downloadServiceSongs) { for (downloadFile in downloadServiceSongs) {
val playlistEntry = downloadFile.song val playlistEntry = downloadFile.track
entries.add(playlistEntry) tracks.add(playlistEntry)
} }
shareHandler.createShare(this, entries, null, cancellationToken) shareHandler.createShare(this, tracks, null, cancellationToken)
return true return true
} }
R.id.menu_item_share_song -> { R.id.menu_item_share_song -> {
if (currentSong == null) return true if (currentSong == null) return true
val entries: MutableList<MusicDirectory.Entry?> = ArrayList() val tracks: MutableList<Track?> = ArrayList()
entries.add(currentSong) tracks.add(currentSong)
shareHandler.createShare(this, entries, null, cancellationToken) shareHandler.createShare(this, tracks, null, cancellationToken)
return true return true
} }
else -> return false else -> return false
@ -785,7 +785,7 @@ class PlayerFragment :
ioScope.launch { ioScope.launch {
val entries = mediaPlayerController.playList.map { val entries = mediaPlayerController.playList.map {
it.song it.track
} }
val musicService = getMusicService() val musicService = getMusicService()
musicService.createPlaylist(null, playlistName, entries) musicService.createPlaylist(null, playlistName, entries)
@ -903,7 +903,7 @@ class PlayerFragment :
val songRemoved = String.format( val songRemoved = String.format(
resources.getString(R.string.download_song_removed), resources.getString(R.string.download_song_removed),
file.song.title file.track.title
) )
Util.toast(context, songRemoved) Util.toast(context, songRemoved)
@ -980,7 +980,7 @@ class PlayerFragment :
val trackFormat = val trackFormat =
String.format(Locale.getDefault(), "%d / %d", currentSongIndex, totalSongs) String.format(Locale.getDefault(), "%d / %d", currentSongIndex, totalSongs)
if (currentPlaying != null) { if (currentPlaying != null) {
currentSong = currentPlaying!!.song currentSong = currentPlaying!!.track
songTitleTextView.text = currentSong!!.title songTitleTextView.text = currentSong!!.title
artistTextView.text = currentSong!!.artist artistTextView.text = currentSong!!.artist
albumTextView.text = currentSong!!.album albumTextView.text = currentSong!!.album

View File

@ -24,12 +24,13 @@ import org.moire.ultrasonic.adapters.DividerBinder
import org.moire.ultrasonic.adapters.MoreButtonBinder 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.Album
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.Identifiable
import org.moire.ultrasonic.domain.Index import org.moire.ultrasonic.domain.Index
import org.moire.ultrasonic.domain.MusicDirectory
import org.moire.ultrasonic.domain.SearchResult import org.moire.ultrasonic.domain.SearchResult
import org.moire.ultrasonic.domain.Track
import org.moire.ultrasonic.fragment.FragmentTitle.Companion.setTitle import org.moire.ultrasonic.fragment.FragmentTitle.Companion.setTitle
import org.moire.ultrasonic.model.SearchListModel import org.moire.ultrasonic.model.SearchListModel
import org.moire.ultrasonic.service.DownloadFile import org.moire.ultrasonic.service.DownloadFile
@ -199,7 +200,7 @@ class SearchFragment : MultiListFragment<Identifiable>(), KoinComponent {
super.onDestroyView() super.onDestroyView()
} }
private fun downloadBackground(save: Boolean, songs: List<MusicDirectory.Entry?>) { private fun downloadBackground(save: Boolean, songs: List<Track?>) {
val onValid = Runnable { val onValid = Runnable {
networkAndStorageChecker.warnIfNetworkOrStorageUnavailable() networkAndStorageChecker.warnIfNetworkOrStorageUnavailable()
mediaPlayerController.downloadBackground(songs, save) mediaPlayerController.downloadBackground(songs, save)
@ -287,7 +288,7 @@ class SearchFragment : MultiListFragment<Identifiable>(), KoinComponent {
} }
} }
private fun onAlbumSelected(album: MusicDirectory.Album, autoplay: Boolean) { private fun onAlbumSelected(album: Album, autoplay: Boolean) {
val bundle = Bundle() val bundle = Bundle()
bundle.putString(Constants.INTENT_ID, album.id) bundle.putString(Constants.INTENT_ID, album.id)
bundle.putString(Constants.INTENT_NAME, album.title) bundle.putString(Constants.INTENT_NAME, album.title)
@ -296,7 +297,7 @@ class SearchFragment : MultiListFragment<Identifiable>(), KoinComponent {
Navigation.findNavController(requireView()).navigate(R.id.searchToTrackCollection, bundle) Navigation.findNavController(requireView()).navigate(R.id.searchToTrackCollection, bundle)
} }
private fun onSongSelected(song: MusicDirectory.Entry, append: Boolean) { private fun onSongSelected(song: Track, append: Boolean) {
if (!append) { if (!append) {
mediaPlayerController.clear() mediaPlayerController.clear()
} }
@ -312,8 +313,8 @@ class SearchFragment : MultiListFragment<Identifiable>(), KoinComponent {
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))
} }
private fun onVideoSelected(entry: MusicDirectory.Entry) { private fun onVideoSelected(track: Track) {
playVideo(requireContext(), entry) playVideo(requireContext(), track)
} }
private fun autoplay() { private fun autoplay() {
@ -329,14 +330,14 @@ class SearchFragment : MultiListFragment<Identifiable>(), KoinComponent {
is ArtistOrIndex -> { is ArtistOrIndex -> {
onArtistSelected(item) onArtistSelected(item)
} }
is MusicDirectory.Entry -> { is Track -> {
if (item.isVideo) { if (item.isVideo) {
onVideoSelected(item) onVideoSelected(item)
} else { } else {
onSongSelected(item, true) onSongSelected(item, true)
} }
} }
is MusicDirectory.Album -> { is Album -> {
onAlbumSelected(item, false) onAlbumSelected(item, false)
} }
} }
@ -356,11 +357,11 @@ class SearchFragment : MultiListFragment<Identifiable>(), KoinComponent {
if (found || item !is DownloadFile) return true if (found || item !is DownloadFile) return true
val songs = mutableListOf<MusicDirectory.Entry>() val songs = mutableListOf<Track>()
when (menuItem.itemId) { when (menuItem.itemId) {
R.id.song_menu_play_now -> { R.id.song_menu_play_now -> {
songs.add(item.song) songs.add(item.track)
downloadHandler.download( downloadHandler.download(
fragment = this, fragment = this,
append = false, append = false,
@ -372,7 +373,7 @@ class SearchFragment : MultiListFragment<Identifiable>(), KoinComponent {
) )
} }
R.id.song_menu_play_next -> { R.id.song_menu_play_next -> {
songs.add(item.song) songs.add(item.track)
downloadHandler.download( downloadHandler.download(
fragment = this, fragment = this,
append = true, append = true,
@ -384,7 +385,7 @@ class SearchFragment : MultiListFragment<Identifiable>(), KoinComponent {
) )
} }
R.id.song_menu_play_last -> { R.id.song_menu_play_last -> {
songs.add(item.song) songs.add(item.track)
downloadHandler.download( downloadHandler.download(
fragment = this, fragment = this,
append = true, append = true,
@ -396,7 +397,7 @@ class SearchFragment : MultiListFragment<Identifiable>(), KoinComponent {
) )
} }
R.id.song_menu_pin -> { R.id.song_menu_pin -> {
songs.add(item.song) songs.add(item.track)
toast( toast(
context, context,
resources.getQuantityString( resources.getQuantityString(
@ -408,7 +409,7 @@ class SearchFragment : MultiListFragment<Identifiable>(), KoinComponent {
downloadBackground(true, songs) downloadBackground(true, songs)
} }
R.id.song_menu_download -> { R.id.song_menu_download -> {
songs.add(item.song) songs.add(item.track)
toast( toast(
context, context,
resources.getQuantityString( resources.getQuantityString(
@ -420,7 +421,7 @@ class SearchFragment : MultiListFragment<Identifiable>(), KoinComponent {
downloadBackground(false, songs) downloadBackground(false, songs)
} }
R.id.song_menu_unpin -> { R.id.song_menu_unpin -> {
songs.add(item.song) songs.add(item.track)
toast( toast(
context, context,
resources.getQuantityString( resources.getQuantityString(
@ -432,7 +433,7 @@ class SearchFragment : MultiListFragment<Identifiable>(), KoinComponent {
mediaPlayerController.unpin(songs) mediaPlayerController.unpin(songs)
} }
R.id.song_menu_share -> { R.id.song_menu_share -> {
songs.add(item.song) songs.add(item.track)
shareHandler.createShare(this, songs, searchRefresh, cancellationToken!!) shareHandler.createShare(this, songs, searchRefresh, cancellationToken!!)
} }
} }

View File

@ -34,6 +34,7 @@ import org.moire.ultrasonic.adapters.TrackViewBinder
import org.moire.ultrasonic.data.ActiveServerProvider.Companion.isOffline import org.moire.ultrasonic.data.ActiveServerProvider.Companion.isOffline
import org.moire.ultrasonic.domain.Identifiable import org.moire.ultrasonic.domain.Identifiable
import org.moire.ultrasonic.domain.MusicDirectory import org.moire.ultrasonic.domain.MusicDirectory
import org.moire.ultrasonic.domain.Track
import org.moire.ultrasonic.fragment.FragmentTitle.Companion.setTitle import org.moire.ultrasonic.fragment.FragmentTitle.Companion.setTitle
import org.moire.ultrasonic.model.TrackCollectionModel import org.moire.ultrasonic.model.TrackCollectionModel
import org.moire.ultrasonic.service.MediaPlayerController import org.moire.ultrasonic.service.MediaPlayerController
@ -46,7 +47,6 @@ import org.moire.ultrasonic.util.Constants
import org.moire.ultrasonic.util.EntryByDiscAndTrackComparator import org.moire.ultrasonic.util.EntryByDiscAndTrackComparator
import org.moire.ultrasonic.util.Settings import org.moire.ultrasonic.util.Settings
import org.moire.ultrasonic.util.Util import org.moire.ultrasonic.util.Util
import org.moire.ultrasonic.util.Util.toast
/** /**
* 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.
@ -122,8 +122,8 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
viewAdapter.register( viewAdapter.register(
TrackViewBinder( TrackViewBinder(
onItemClick = { onItemClick(it.song) }, onItemClick = { onItemClick(it.track) },
onContextMenuClick = { menu, id -> onContextMenuItemSelected(menu, id.song) }, onContextMenuClick = { menu, id -> onContextMenuItemSelected(menu, id.track) },
checkable = true, checkable = true,
draggable = false, draggable = false,
context = requireContext(), context = requireContext(),
@ -249,7 +249,7 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
private fun playNow( private fun playNow(
append: Boolean, append: Boolean,
selectedSongs: List<MusicDirectory.Entry> = getSelectedSongs() selectedSongs: List<Track> = getSelectedSongs()
) { ) {
if (selectedSongs.isNotEmpty()) { if (selectedSongs.isNotEmpty()) {
downloadHandler.download( downloadHandler.download(
@ -314,10 +314,10 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
} }
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
private fun getAllSongs(): List<MusicDirectory.Entry> { private fun getAllSongs(): List<Track> {
return viewAdapter.getCurrentList().filter { return viewAdapter.getCurrentList().filter {
it is MusicDirectory.Entry && !it.isDirectory it is Track && !it.isDirectory
} as List<MusicDirectory.Entry> } as List<Track>
} }
internal fun selectAllOrNone() { internal fun selectAllOrNone() {
@ -338,7 +338,7 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
} }
} }
internal open fun enableButtons(selection: List<MusicDirectory.Entry> = getSelectedSongs()) { internal open fun enableButtons(selection: List<Track> = getSelectedSongs()) {
val enabled = selection.isNotEmpty() val enabled = selection.isNotEmpty()
var unpinEnabled = false var unpinEnabled = false
var deleteEnabled = false var deleteEnabled = false
@ -378,7 +378,7 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
private fun downloadBackground( private fun downloadBackground(
save: Boolean, save: Boolean,
songs: List<MusicDirectory.Entry?> songs: List<Track?>
) { ) {
val onValid = Runnable { val onValid = Runnable {
networkAndStorageChecker.warnIfNetworkOrStorageUnavailable() networkAndStorageChecker.warnIfNetworkOrStorageUnavailable()
@ -403,7 +403,7 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
onValid.run() onValid.run()
} }
internal fun delete(songs: List<MusicDirectory.Entry> = getSelectedSongs()) { internal fun delete(songs: List<Track> = getSelectedSongs()) {
Util.toast( Util.toast(
context, context,
resources.getQuantityString( resources.getQuantityString(
@ -414,7 +414,7 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
mediaPlayerController.delete(songs) mediaPlayerController.delete(songs)
} }
internal fun unpin(songs: List<MusicDirectory.Entry> = getSelectedSongs()) { internal fun unpin(songs: List<Track> = getSelectedSongs()) {
Util.toast( Util.toast(
context, context,
resources.getQuantityString( resources.getQuantityString(
@ -533,10 +533,10 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
} }
} }
internal fun getSelectedSongs(): List<MusicDirectory.Entry> { internal fun getSelectedSongs(): List<Track> {
// Walk through selected set and get the Entries based on the saved ids. // Walk through selected set and get the Entries based on the saved ids.
return viewAdapter.getCurrentList().mapNotNull { return viewAdapter.getCurrentList().mapNotNull {
if (it is MusicDirectory.Entry && viewAdapter.isSelected(it.longId)) if (it is Track && viewAdapter.isSelected(it.longId))
it it
else else
null null
@ -655,7 +655,7 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
playAll() playAll()
} }
R.id.song_menu_share -> { R.id.song_menu_share -> {
if (item is MusicDirectory.Entry) { if (item is Track) {
shareHandler.createShare( shareHandler.createShare(
this, listOf(item), refreshListView, this, listOf(item), refreshListView,
cancellationToken!! cancellationToken!!
@ -669,10 +669,10 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
return true return true
} }
internal fun getClickedSong(item: MusicDirectory.Child): List<MusicDirectory.Entry> { internal fun getClickedSong(item: MusicDirectory.Child): List<Track> {
// This can probably be done better // This can probably be done better
return viewAdapter.getCurrentList().mapNotNull { return viewAdapter.getCurrentList().mapNotNull {
if (it is MusicDirectory.Entry && (it.id == item.id)) if (it is Track && (it.id == item.id))
it it
else else
null null
@ -692,7 +692,7 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
bundle bundle
) )
} }
item is MusicDirectory.Entry && item.isVideo -> { item is Track && item.isVideo -> {
VideoPlayer.playVideo(requireContext(), item) VideoPlayer.playVideo(requireContext(), item)
} }
else -> { else -> {

View File

@ -4,7 +4,7 @@ import android.graphics.Bitmap
import android.graphics.BitmapFactory import android.graphics.BitmapFactory
import android.os.Build import android.os.Build
import java.io.File import java.io.File
import org.moire.ultrasonic.domain.MusicDirectory import org.moire.ultrasonic.domain.Track
import org.moire.ultrasonic.util.FileUtil import org.moire.ultrasonic.util.FileUtil
import org.moire.ultrasonic.util.Util import org.moire.ultrasonic.util.Util
import timber.log.Timber import timber.log.Timber
@ -26,11 +26,11 @@ class BitmapUtils {
} }
fun getAlbumArtBitmapFromDisk( fun getAlbumArtBitmapFromDisk(
entry: MusicDirectory.Entry?, track: Track?,
size: Int size: Int
): Bitmap? { ): Bitmap? {
if (entry == null) return null if (track == null) return null
val albumArtFile = FileUtil.getAlbumArtFile(entry) val albumArtFile = FileUtil.getAlbumArtFile(track)
val bitmap: Bitmap? = null val bitmap: Bitmap? = null
if (albumArtFile != null && File(albumArtFile).exists()) { if (albumArtFile != null && File(albumArtFile).exists()) {
return getBitmapFromDisk(albumArtFile, size, bitmap) return getBitmapFromDisk(albumArtFile, size, bitmap)

View File

@ -20,6 +20,7 @@ import org.moire.ultrasonic.api.subsonic.SubsonicAPIClient
import org.moire.ultrasonic.api.subsonic.throwOnFailure import org.moire.ultrasonic.api.subsonic.throwOnFailure
import org.moire.ultrasonic.api.subsonic.toStreamResponse import org.moire.ultrasonic.api.subsonic.toStreamResponse
import org.moire.ultrasonic.domain.MusicDirectory import org.moire.ultrasonic.domain.MusicDirectory
import org.moire.ultrasonic.domain.Track
import org.moire.ultrasonic.util.FileUtil import org.moire.ultrasonic.util.FileUtil
import org.moire.ultrasonic.util.Util.safeClose import org.moire.ultrasonic.util.Util.safeClose
import timber.log.Timber import timber.log.Timber
@ -148,28 +149,28 @@ class ImageLoader(
* Download a cover art file and cache it on disk * Download a cover art file and cache it on disk
*/ */
fun cacheCoverArt( fun cacheCoverArt(
entry: MusicDirectory.Entry track: Track
) { ) {
// Synchronize on the entry so that we don't download concurrently for // Synchronize on the entry so that we don't download concurrently for
// the same song. // the same song.
synchronized(entry) { synchronized(track) {
// Always download the large size.. // Always download the large size..
val size = config.largeSize val size = config.largeSize
// Check cache to avoid downloading existing files // Check cache to avoid downloading existing files
val file = FileUtil.getAlbumArtFile(entry) val file = FileUtil.getAlbumArtFile(track)
// Return if have a cache hit // Return if have a cache hit
if (file != null && File(file).exists()) return if (file != null && File(file).exists()) return
File(file!!).createNewFile() File(file!!).createNewFile()
// Can't load empty string ids // Can't load empty string ids
val id = entry.coverArt val id = track.coverArt
if (TextUtils.isEmpty(id)) return if (TextUtils.isEmpty(id)) return
// Query the API // Query the API
Timber.d("Loading cover art for: %s", entry) Timber.d("Loading cover art for: %s", track)
val response = API.getCoverArt(id!!, size.toLong()).execute().toStreamResponse() val response = API.getCoverArt(id!!, size.toLong()).execute().toStreamResponse()
response.throwOnFailure() response.throwOnFailure()

View File

@ -6,14 +6,14 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import org.moire.ultrasonic.api.subsonic.models.AlbumListType import org.moire.ultrasonic.api.subsonic.models.AlbumListType
import org.moire.ultrasonic.domain.MusicDirectory import org.moire.ultrasonic.domain.Album
import org.moire.ultrasonic.service.MusicService import org.moire.ultrasonic.service.MusicService
import org.moire.ultrasonic.util.Constants import org.moire.ultrasonic.util.Constants
import org.moire.ultrasonic.util.Settings import org.moire.ultrasonic.util.Settings
class AlbumListModel(application: Application) : GenericListModel(application) { class AlbumListModel(application: Application) : GenericListModel(application) {
val list: MutableLiveData<List<MusicDirectory.Album>> = MutableLiveData() val list: MutableLiveData<List<Album>> = MutableLiveData()
var lastType: String? = null var lastType: String? = null
private var loadedUntil: Int = 0 private var loadedUntil: Int = 0
@ -21,7 +21,7 @@ class AlbumListModel(application: Application) : GenericListModel(application) {
refresh: Boolean, refresh: Boolean,
swipe: SwipeRefreshLayout, swipe: SwipeRefreshLayout,
args: Bundle args: Bundle
): LiveData<List<MusicDirectory.Album>> { ): LiveData<List<Album>> {
// Don't reload the data if navigating back to the view that was active before. // Don't reload the data if navigating back to the view that was active before.
// This way, we keep the scroll position // This way, we keep the scroll position
val albumListType = args.getString(Constants.INTENT_ALBUM_LIST_TYPE)!! val albumListType = args.getString(Constants.INTENT_ALBUM_LIST_TYPE)!!
@ -56,7 +56,7 @@ class AlbumListModel(application: Application) : GenericListModel(application) {
var offset = args.getInt(Constants.INTENT_ALBUM_LIST_OFFSET, 0) var offset = args.getInt(Constants.INTENT_ALBUM_LIST_OFFSET, 0)
val append = args.getBoolean(Constants.INTENT_APPEND, false) val append = args.getBoolean(Constants.INTENT_APPEND, false)
val musicDirectory: List<MusicDirectory.Album> val musicDirectory: List<Album>
val musicFolderId = if (showSelectFolderHeader(args)) { val musicFolderId = if (showSelectFolderHeader(args)) {
activeServerProvider.getActiveServer().musicFolderId activeServerProvider.getActiveServer().musicFolderId
} else { } else {
@ -98,7 +98,7 @@ class AlbumListModel(application: Application) : GenericListModel(application) {
currentListIsSortable = isCollectionSortable(albumListType) currentListIsSortable = isCollectionSortable(albumListType)
if (append && list.value != null) { if (append && list.value != null) {
val newList = ArrayList<MusicDirectory.Album>() val newList = ArrayList<Album>()
newList.addAll(list.value!!) newList.addAll(list.value!!)
newList.addAll(musicDirectory) newList.addAll(musicDirectory)
list.postValue(newList) list.postValue(newList)

View File

@ -25,6 +25,7 @@ import org.moire.ultrasonic.data.ActiveServerProvider
import org.moire.ultrasonic.domain.MusicDirectory import org.moire.ultrasonic.domain.MusicDirectory
import org.moire.ultrasonic.domain.SearchCriteria import org.moire.ultrasonic.domain.SearchCriteria
import org.moire.ultrasonic.domain.SearchResult import org.moire.ultrasonic.domain.SearchResult
import org.moire.ultrasonic.domain.Track
import org.moire.ultrasonic.util.MediaSessionHandler import org.moire.ultrasonic.util.MediaSessionHandler
import org.moire.ultrasonic.util.Settings import org.moire.ultrasonic.util.Settings
import org.moire.ultrasonic.util.Util import org.moire.ultrasonic.util.Util
@ -80,10 +81,10 @@ class AutoMediaBrowserService : MediaBrowserServiceCompat() {
private val serviceJob = Job() private val serviceJob = Job()
private val serviceScope = CoroutineScope(Dispatchers.IO + serviceJob) private val serviceScope = CoroutineScope(Dispatchers.IO + serviceJob)
private var playlistCache: List<MusicDirectory.Entry>? = null private var playlistCache: List<Track>? = null
private var starredSongsCache: List<MusicDirectory.Entry>? = null private var starredSongsCache: List<Track>? = null
private var randomSongsCache: List<MusicDirectory.Entry>? = null private var randomSongsCache: List<Track>? = null
private var searchSongsCache: List<MusicDirectory.Entry>? = null private var searchSongsCache: List<Track>? = null
private val isOffline get() = ActiveServerProvider.isOffline() private val isOffline get() = ActiveServerProvider.isOffline()
private val useId3Tags get() = Settings.shouldUseId3Tags private val useId3Tags get() = Settings.shouldUseId3Tags
@ -1070,7 +1071,7 @@ class AutoMediaBrowserService : MediaBrowserServiceCompat() {
return section.toString() return section.toString()
} }
private fun playSongs(songs: List<MusicDirectory.Entry?>?) { private fun playSongs(songs: List<Track?>?) {
mediaPlayerController.addToPlaylist( mediaPlayerController.addToPlaylist(
songs, songs,
save = false, save = false,
@ -1081,7 +1082,7 @@ class AutoMediaBrowserService : MediaBrowserServiceCompat() {
) )
} }
private fun playSong(song: MusicDirectory.Entry) { private fun playSong(song: Track) {
mediaPlayerController.addToPlaylist( mediaPlayerController.addToPlaylist(
listOf(song), listOf(song),
save = false, save = false,

View File

@ -13,6 +13,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.data.MetaDatabase import org.moire.ultrasonic.data.MetaDatabase
import org.moire.ultrasonic.domain.Album
import org.moire.ultrasonic.domain.Artist import org.moire.ultrasonic.domain.Artist
import org.moire.ultrasonic.domain.Bookmark import org.moire.ultrasonic.domain.Bookmark
import org.moire.ultrasonic.domain.ChatMessage import org.moire.ultrasonic.domain.ChatMessage
@ -27,6 +28,7 @@ import org.moire.ultrasonic.domain.PodcastsChannel
import org.moire.ultrasonic.domain.SearchCriteria import org.moire.ultrasonic.domain.SearchCriteria
import org.moire.ultrasonic.domain.SearchResult import org.moire.ultrasonic.domain.SearchResult
import org.moire.ultrasonic.domain.Share import org.moire.ultrasonic.domain.Share
import org.moire.ultrasonic.domain.Track
import org.moire.ultrasonic.domain.UserInfo import org.moire.ultrasonic.domain.UserInfo
import org.moire.ultrasonic.util.Constants import org.moire.ultrasonic.util.Constants
import org.moire.ultrasonic.util.LRUCache import org.moire.ultrasonic.util.LRUCache
@ -41,7 +43,7 @@ class CachedMusicService(private val musicService: MusicService) : MusicService,
// Old style TimeLimitedCache // Old style TimeLimitedCache
private val cachedMusicDirectories: LRUCache<String, TimeLimitedCache<MusicDirectory?>> private val cachedMusicDirectories: LRUCache<String, TimeLimitedCache<MusicDirectory?>>
private val cachedArtist: LRUCache<String, TimeLimitedCache<List<MusicDirectory.Album>>> private val cachedArtist: LRUCache<String, TimeLimitedCache<List<Album>>>
private val cachedAlbum: LRUCache<String, TimeLimitedCache<MusicDirectory?>> private val cachedAlbum: LRUCache<String, TimeLimitedCache<MusicDirectory?>>
private val cachedUserInfo: LRUCache<String, TimeLimitedCache<UserInfo?>> private val cachedUserInfo: LRUCache<String, TimeLimitedCache<UserInfo?>>
private val cachedLicenseValid = TimeLimitedCache<Boolean>(120, TimeUnit.SECONDS) private val cachedLicenseValid = TimeLimitedCache<Boolean>(120, TimeUnit.SECONDS)
@ -149,7 +151,7 @@ class CachedMusicService(private val musicService: MusicService) : MusicService,
@Throws(Exception::class) @Throws(Exception::class)
override fun getArtist(id: String, name: String?, refresh: Boolean): override fun getArtist(id: String, name: String?, refresh: Boolean):
List<MusicDirectory.Album> { List<Album> {
checkSettingsChanged() checkSettingsChanged()
var cache = if (refresh) null else cachedArtist[id] var cache = if (refresh) null else cachedArtist[id]
var dir = cache?.get() var dir = cache?.get()
@ -218,9 +220,9 @@ class CachedMusicService(private val musicService: MusicService) : MusicService,
} }
@Throws(Exception::class) @Throws(Exception::class)
override fun createPlaylist(id: String?, name: String?, entries: List<MusicDirectory.Entry>) { override fun createPlaylist(id: String?, name: String?, tracks: List<Track>) {
cachedPlaylists.clear() cachedPlaylists.clear()
musicService.createPlaylist(id, name, entries) musicService.createPlaylist(id, name, tracks)
} }
@Throws(Exception::class) @Throws(Exception::class)
@ -249,7 +251,7 @@ class CachedMusicService(private val musicService: MusicService) : MusicService,
size: Int, size: Int,
offset: Int, offset: Int,
musicFolderId: String? musicFolderId: String?
): List<MusicDirectory.Album> { ): List<Album> {
return musicService.getAlbumList(type, size, offset, musicFolderId) return musicService.getAlbumList(type, size, offset, musicFolderId)
} }
@ -259,7 +261,7 @@ class CachedMusicService(private val musicService: MusicService) : MusicService,
size: Int, size: Int,
offset: Int, offset: Int,
musicFolderId: String? musicFolderId: String?
): List<MusicDirectory.Album> { ): List<Album> {
return musicService.getAlbumList2(type, size, offset, musicFolderId) return musicService.getAlbumList2(type, size, offset, musicFolderId)
} }
@ -276,7 +278,7 @@ class CachedMusicService(private val musicService: MusicService) : MusicService,
@Throws(Exception::class) @Throws(Exception::class)
override fun getDownloadInputStream( override fun getDownloadInputStream(
song: MusicDirectory.Entry, song: Track,
offset: Long, offset: Long,
maxBitrate: Int, maxBitrate: Int,
save: Boolean save: Boolean

View File

@ -12,12 +12,13 @@ import androidx.lifecycle.MutableLiveData
import java.io.IOException import java.io.IOException
import java.io.InputStream import java.io.InputStream
import java.io.OutputStream import java.io.OutputStream
import java.util.Locale
import org.koin.core.component.KoinComponent 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.Identifiable import org.moire.ultrasonic.domain.Identifiable
import org.moire.ultrasonic.domain.MusicDirectory import org.moire.ultrasonic.domain.Track
import org.moire.ultrasonic.service.MusicServiceFactory.getMusicService import org.moire.ultrasonic.service.MusicServiceFactory.getMusicService
import org.moire.ultrasonic.subsonic.ImageLoaderProvider import org.moire.ultrasonic.subsonic.ImageLoaderProvider
import org.moire.ultrasonic.util.CacheCleaner import org.moire.ultrasonic.util.CacheCleaner
@ -38,12 +39,12 @@ import timber.log.Timber
* *
*/ */
class DownloadFile( class DownloadFile(
val song: MusicDirectory.Entry, val track: Track,
save: Boolean save: Boolean
) : KoinComponent, Identifiable { ) : KoinComponent, Identifiable {
val partialFile: String val partialFile: String
lateinit var completeFile: String lateinit var completeFile: String
val saveFile: String = FileUtil.getSongFile(song) val saveFile: String = FileUtil.getSongFile(track)
var shouldSave = save var shouldSave = save
private var downloadTask: CancellableTask? = null private var downloadTask: CancellableTask? = null
var isFailed = false var isFailed = false
@ -104,7 +105,7 @@ class DownloadFile(
* Returns the effective bit rate. * Returns the effective bit rate.
*/ */
fun getBitRate(): Int { fun getBitRate(): Int {
return if (song.bitRate == null) desiredBitRate else song.bitRate!! return if (track.bitRate == null) desiredBitRate else track.bitRate!!
} }
@Synchronized @Synchronized
@ -221,7 +222,7 @@ class DownloadFile(
} }
override fun toString(): String { override fun toString(): String {
return String.format("DownloadFile (%s)", song) return String.format(Locale.ROOT, "DownloadFile (%s)", track)
} }
private inner class DownloadTask : CancellableTask() { private inner class DownloadTask : CancellableTask() {
@ -259,7 +260,7 @@ class DownloadFile(
// Some devices seem to throw error on partial file which doesn't exist // Some devices seem to throw error on partial file which doesn't exist
val needsDownloading: Boolean val needsDownloading: Boolean
val duration = song.duration val duration = track.duration
val fileLength = Storage.getFromPath(partialFile)?.length ?: 0 val fileLength = Storage.getFromPath(partialFile)?.length ?: 0
needsDownloading = ( needsDownloading = (
@ -269,7 +270,7 @@ class DownloadFile(
if (needsDownloading) { if (needsDownloading) {
// Attempt partial HTTP GET, appending to the file if it exists. // Attempt partial HTTP GET, appending to the file if it exists.
val (inStream, isPartial) = musicService.getDownloadInputStream( val (inStream, isPartial) = musicService.getDownloadInputStream(
song, fileLength, desiredBitRate, shouldSave track, fileLength, desiredBitRate, shouldSave
) )
inputStream = inStream inputStream = inStream
@ -293,11 +294,13 @@ class DownloadFile(
if (isCancelled) { if (isCancelled) {
status.postValue(DownloadStatus.CANCELLED) status.postValue(DownloadStatus.CANCELLED)
throw Exception(String.format("Download of '%s' was cancelled", song)) throw RuntimeException(
String.format(Locale.ROOT, "Download of '%s' was cancelled", track)
)
} }
if (song.artistId != null) { if (track.artistId != null) {
cacheMetadata(song.artistId!!) cacheMetadata(track.artistId!!)
} }
downloadAndSaveCoverArt() downloadAndSaveCoverArt()
@ -328,7 +331,7 @@ class DownloadFile(
status.postValue(DownloadStatus.FAILED) status.postValue(DownloadStatus.FAILED)
--retryCount --retryCount
} }
Timber.w(all, "Failed to download '%s'.", song) Timber.w(all, "Failed to download '%s'.", track)
} }
} finally { } finally {
inputStream.safeClose() inputStream.safeClose()
@ -339,7 +342,7 @@ class DownloadFile(
} }
override fun toString(): String { override fun toString(): String {
return String.format("DownloadTask (%s)", song) return String.format(Locale.ROOT, "DownloadTask (%s)", track)
} }
private fun cacheMetadata(artistId: String) { private fun cacheMetadata(artistId: String) {
@ -367,9 +370,9 @@ class DownloadFile(
private fun downloadAndSaveCoverArt() { private fun downloadAndSaveCoverArt() {
try { try {
if (!TextUtils.isEmpty(song.coverArt)) { if (!TextUtils.isEmpty(track.coverArt)) {
// Download the largest size that we can display in the UI // Download the largest size that we can display in the UI
imageLoaderProvider.getImageLoader().cacheCoverArt(song) imageLoaderProvider.getImageLoader().cacheCoverArt(track)
} }
} catch (all: Exception) { } catch (all: Exception) {
Timber.e(all, "Failed to get cover art.") Timber.e(all, "Failed to get cover art.")
@ -392,8 +395,8 @@ class DownloadFile(
} }
private fun setProgress(totalBytesCopied: Long) { private fun setProgress(totalBytesCopied: Long) {
if (song.size != null) { if (track.size != null) {
progress.postValue((totalBytesCopied * 100 / song.size!!).toInt()) progress.postValue((totalBytesCopied * 100 / track.size!!).toInt())
} }
} }
@ -404,7 +407,7 @@ class DownloadFile(
} }
override val id: String override val id: String
get() = song.id get() = track.id
companion object { companion object {
const val MAX_RETRIES = 5 const val MAX_RETRIES = 5

View File

@ -10,8 +10,8 @@ import java.util.concurrent.ScheduledExecutorService
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import org.koin.core.component.KoinComponent import org.koin.core.component.KoinComponent
import org.koin.core.component.inject import org.koin.core.component.inject
import org.moire.ultrasonic.domain.MusicDirectory
import org.moire.ultrasonic.domain.PlayerState import org.moire.ultrasonic.domain.PlayerState
import org.moire.ultrasonic.domain.Track
import org.moire.ultrasonic.util.LRUCache import org.moire.ultrasonic.util.LRUCache
import org.moire.ultrasonic.util.Settings import org.moire.ultrasonic.util.Settings
import org.moire.ultrasonic.util.ShufflePlayBuffer import org.moire.ultrasonic.util.ShufflePlayBuffer
@ -45,7 +45,7 @@ class Downloader(
private val jukeboxMediaPlayer: JukeboxMediaPlayer by inject() private val jukeboxMediaPlayer: JukeboxMediaPlayer by inject()
// This cache helps us to avoid creating duplicate DownloadFile instances when showing Entries // This cache helps us to avoid creating duplicate DownloadFile instances when showing Entries
private val downloadFileCache = LRUCache<MusicDirectory.Entry, DownloadFile>(100) private val downloadFileCache = LRUCache<Track, DownloadFile>(100)
private var executorService: ScheduledExecutorService? = null private var executorService: ScheduledExecutorService? = null
private var wifiLock: WifiManager.WifiLock? = null private var wifiLock: WifiManager.WifiLock? = null
@ -234,7 +234,7 @@ class Downloader(
get() { get() {
var totalDuration: Long = 0 var totalDuration: Long = 0
for (downloadFile in playlist) { for (downloadFile in playlist) {
val song = downloadFile.song val song = downloadFile.track
if (!song.isDirectory) { if (!song.isDirectory) {
if (song.artist != null) { if (song.artist != null) {
if (song.duration != null) { if (song.duration != null) {
@ -345,7 +345,7 @@ class Downloader(
@Synchronized @Synchronized
fun addToPlaylist( fun addToPlaylist(
songs: List<MusicDirectory.Entry>, songs: List<Track>,
save: Boolean, save: Boolean,
autoPlay: Boolean, autoPlay: Boolean,
playNext: Boolean, playNext: Boolean,
@ -407,7 +407,7 @@ class Downloader(
} }
@Synchronized @Synchronized
fun downloadBackground(songs: List<MusicDirectory.Entry>, save: Boolean) { fun downloadBackground(songs: List<Track>, save: Boolean) {
// By using the counter we ensure that the songs are added in the correct order // By using the counter we ensure that the songs are added in the correct order
for (song in songs) { for (song in songs) {
@ -435,19 +435,19 @@ class Downloader(
@Synchronized @Synchronized
@Suppress("ReturnCount") @Suppress("ReturnCount")
fun getDownloadFileForSong(song: MusicDirectory.Entry): DownloadFile { fun getDownloadFileForSong(song: Track): DownloadFile {
for (downloadFile in playlist) { for (downloadFile in playlist) {
if (downloadFile.song == song) { if (downloadFile.track == song) {
return downloadFile return downloadFile
} }
} }
for (downloadFile in activelyDownloading) { for (downloadFile in activelyDownloading) {
if (downloadFile.song == song) { if (downloadFile.track == song) {
return downloadFile return downloadFile
} }
} }
for (downloadFile in downloadQueue) { for (downloadFile in downloadQueue) {
if (downloadFile.song == song) { if (downloadFile.track == song) {
return downloadFile return downloadFile
} }
} }
@ -513,7 +513,7 @@ class Downloader(
* Extension function * Extension function
* Gathers the download file for a given song, and modifies shouldSave if provided. * Gathers the download file for a given song, and modifies shouldSave if provided.
*/ */
fun MusicDirectory.Entry.getDownloadFile(save: Boolean? = null): DownloadFile { fun Track.getDownloadFile(save: Boolean? = null): DownloadFile {
return getDownloadFileForSong(this).apply { return getDownloadFileForSong(this).apply {
if (save != null) this.shouldSave = save if (save != null) this.shouldSave = save
} }

View File

@ -303,7 +303,7 @@ class LocalMediaPlayer : KoinComponent {
val playerDuration: Int val playerDuration: Int
get() { get() {
if (currentPlaying != null) { if (currentPlaying != null) {
val duration = currentPlaying!!.song.duration val duration = currentPlaying!!.track.duration
if (duration != null) { if (duration != null) {
return duration * 1000 return duration * 1000
} }
@ -391,7 +391,7 @@ class LocalMediaPlayer : KoinComponent {
setPlayerState(PlayerState.PREPARING, downloadFile) setPlayerState(PlayerState.PREPARING, downloadFile)
mediaPlayer.setOnBufferingUpdateListener { mp, percent -> mediaPlayer.setOnBufferingUpdateListener { mp, percent ->
val song = downloadFile.song val song = downloadFile.track
if (percent == 100) { if (percent == 100) {
mp.setOnBufferingUpdateListener(null) mp.setOnBufferingUpdateListener(null)
@ -512,8 +512,8 @@ class LocalMediaPlayer : KoinComponent {
} }
var duration = 0 var duration = 0
if (downloadFile.song.duration != null) { if (downloadFile.track.duration != null) {
duration = downloadFile.song.duration!! * 1000 duration = downloadFile.track.duration!! * 1000
} }
mediaPlayer.setOnCompletionListener(object : OnCompletionListener { mediaPlayer.setOnCompletionListener(object : OnCompletionListener {

View File

@ -11,9 +11,9 @@ import org.koin.core.component.KoinComponent
import org.koin.core.component.inject import org.koin.core.component.inject
import org.moire.ultrasonic.app.UApp import org.moire.ultrasonic.app.UApp
import org.moire.ultrasonic.data.ActiveServerProvider import org.moire.ultrasonic.data.ActiveServerProvider
import org.moire.ultrasonic.domain.MusicDirectory
import org.moire.ultrasonic.domain.PlayerState import org.moire.ultrasonic.domain.PlayerState
import org.moire.ultrasonic.domain.RepeatMode import org.moire.ultrasonic.domain.RepeatMode
import org.moire.ultrasonic.domain.Track
import org.moire.ultrasonic.service.MediaPlayerService.Companion.executeOnStartedMediaPlayerService import org.moire.ultrasonic.service.MediaPlayerService.Companion.executeOnStartedMediaPlayerService
import org.moire.ultrasonic.service.MediaPlayerService.Companion.getInstance import org.moire.ultrasonic.service.MediaPlayerService.Companion.getInstance
import org.moire.ultrasonic.service.MediaPlayerService.Companion.runningInstance import org.moire.ultrasonic.service.MediaPlayerService.Companion.runningInstance
@ -65,7 +65,7 @@ class MediaPlayerController(
@Synchronized @Synchronized
fun restore( fun restore(
songs: List<MusicDirectory.Entry?>?, songs: List<Track?>?,
currentPlayingIndex: Int, currentPlayingIndex: Int,
currentPlayingPosition: Int, currentPlayingPosition: Int,
autoPlay: Boolean, autoPlay: Boolean,
@ -165,7 +165,7 @@ class MediaPlayerController(
@Synchronized @Synchronized
@Suppress("LongParameterList") @Suppress("LongParameterList")
fun addToPlaylist( fun addToPlaylist(
songs: List<MusicDirectory.Entry?>?, songs: List<Track?>?,
save: Boolean, save: Boolean,
autoPlay: Boolean, autoPlay: Boolean,
playNext: Boolean, playNext: Boolean,
@ -202,7 +202,7 @@ class MediaPlayerController(
} }
@Synchronized @Synchronized
fun downloadBackground(songs: List<MusicDirectory.Entry?>?, save: Boolean) { fun downloadBackground(songs: List<Track?>?, save: Boolean) {
if (songs == null) return if (songs == null) return
val filteredSongs = songs.filterNotNull() val filteredSongs = songs.filterNotNull()
downloader.downloadBackground(filteredSongs, save) downloader.downloadBackground(filteredSongs, save)
@ -325,7 +325,7 @@ class MediaPlayerController(
@Synchronized @Synchronized
// TODO: Make it require not null // TODO: Make it require not null
fun delete(songs: List<MusicDirectory.Entry?>) { fun delete(songs: List<Track?>) {
for (song in songs.filterNotNull()) { for (song in songs.filterNotNull()) {
downloader.getDownloadFileForSong(song).delete() downloader.getDownloadFileForSong(song).delete()
} }
@ -333,7 +333,7 @@ class MediaPlayerController(
@Synchronized @Synchronized
// TODO: Make it require not null // TODO: Make it require not null
fun unpin(songs: List<MusicDirectory.Entry?>) { fun unpin(songs: List<Track?>) {
for (song in songs.filterNotNull()) { for (song in songs.filterNotNull()) {
downloader.getDownloadFileForSong(song).unpin() downloader.getDownloadFileForSong(song).unpin()
} }
@ -453,7 +453,7 @@ class MediaPlayerController(
fun toggleSongStarred() { fun toggleSongStarred() {
if (localMediaPlayer.currentPlaying == null) return if (localMediaPlayer.currentPlaying == null) return
val song = localMediaPlayer.currentPlaying!!.song val song = localMediaPlayer.currentPlaying!!.track
Thread { Thread {
val musicService = getMusicService() val musicService = getMusicService()
@ -477,7 +477,7 @@ class MediaPlayerController(
fun setSongRating(rating: Int) { fun setSongRating(rating: Int) {
if (!Settings.useFiveStarRating) return if (!Settings.useFiveStarRating) return
if (localMediaPlayer.currentPlaying == null) return if (localMediaPlayer.currentPlaying == null) return
val song = localMediaPlayer.currentPlaying!!.song val song = localMediaPlayer.currentPlaying!!.track
song.userRating = rating song.userRating = rating
Thread { Thread {
try { try {
@ -509,7 +509,7 @@ class MediaPlayerController(
val playListDuration: Long val playListDuration: Long
get() = downloader.downloadListDuration get() = downloader.downloadListDuration
fun getDownloadFileForSong(song: MusicDirectory.Entry): DownloadFile { fun getDownloadFileForSong(song: Track): DownloadFile {
return downloader.getDownloadFileForSong(song) return downloader.getDownloadFileForSong(song)
} }

View File

@ -28,9 +28,9 @@ import org.koin.android.ext.android.inject
import org.moire.ultrasonic.R import org.moire.ultrasonic.R
import org.moire.ultrasonic.activity.NavigationActivity import org.moire.ultrasonic.activity.NavigationActivity
import org.moire.ultrasonic.app.UApp import org.moire.ultrasonic.app.UApp
import org.moire.ultrasonic.domain.MusicDirectory
import org.moire.ultrasonic.domain.PlayerState import org.moire.ultrasonic.domain.PlayerState
import org.moire.ultrasonic.domain.RepeatMode import org.moire.ultrasonic.domain.RepeatMode
import org.moire.ultrasonic.domain.Track
import org.moire.ultrasonic.imageloader.BitmapUtils import org.moire.ultrasonic.imageloader.BitmapUtils
import org.moire.ultrasonic.provider.UltrasonicAppWidgetProvider4X1 import org.moire.ultrasonic.provider.UltrasonicAppWidgetProvider4X1
import org.moire.ultrasonic.provider.UltrasonicAppWidgetProvider4X2 import org.moire.ultrasonic.provider.UltrasonicAppWidgetProvider4X2
@ -340,7 +340,7 @@ class MediaPlayerService : Service() {
localMediaPlayer.setPlayerState(PlayerState.STARTED, localMediaPlayer.currentPlaying) localMediaPlayer.setPlayerState(PlayerState.STARTED, localMediaPlayer.currentPlaying)
} }
private fun updateWidget(playerState: PlayerState, song: MusicDirectory.Entry?) { private fun updateWidget(playerState: PlayerState, song: Track?) {
val started = playerState === PlayerState.STARTED val started = playerState === PlayerState.STARTED
val context = this@MediaPlayerService val context = this@MediaPlayerService
@ -364,7 +364,7 @@ class MediaPlayerService : Service() {
Settings.isNotificationAlwaysEnabled Settings.isNotificationAlwaysEnabled
val show = playerState === PlayerState.STARTED || showWhenPaused val show = playerState === PlayerState.STARTED || showWhenPaused
val song = currentPlaying?.song val song = currentPlaying?.track
if (isStateChanged) { if (isStateChanged) {
when { when {
@ -396,7 +396,7 @@ class MediaPlayerService : Service() {
} }
if (isTrackChanged) { if (isTrackChanged) {
Util.broadcastNewTrackInfo(this@MediaPlayerService, currentPlaying?.song) Util.broadcastNewTrackInfo(this@MediaPlayerService, currentPlaying?.track)
} }
// Update widget // Update widget
@ -424,7 +424,7 @@ class MediaPlayerService : Service() {
val index = downloader.currentPlayingIndex val index = downloader.currentPlayingIndex
if (currentPlaying != null) { if (currentPlaying != null) {
val song = currentPlaying.song val song = currentPlaying.track
if (song.bookmarkPosition > 0 && Settings.shouldClearBookmark) { if (song.bookmarkPosition > 0 && Settings.shouldClearBookmark) {
val musicService = getMusicService() val musicService = getMusicService()
try { try {
@ -523,7 +523,7 @@ class MediaPlayerService : Service() {
// Init // Init
val context = applicationContext val context = applicationContext
val song = currentPlaying?.song val song = currentPlaying?.track
val stopIntent = Util.getPendingIntentForMediaAction( val stopIntent = Util.getPendingIntentForMediaAction(
context, context,
KeyEvent.KEYCODE_MEDIA_STOP, KeyEvent.KEYCODE_MEDIA_STOP,
@ -589,7 +589,7 @@ class MediaPlayerService : Service() {
context: Context, context: Context,
notificationBuilder: NotificationCompat.Builder, notificationBuilder: NotificationCompat.Builder,
playerState: PlayerState, playerState: PlayerState,
song: MusicDirectory.Entry? song: Track?
): IntArray { ): IntArray {
// Init // Init
val compactActionList = ArrayList<Int>() val compactActionList = ArrayList<Int>()

View File

@ -7,6 +7,7 @@
package org.moire.ultrasonic.service package org.moire.ultrasonic.service
import java.io.InputStream import java.io.InputStream
import org.moire.ultrasonic.domain.Album
import org.moire.ultrasonic.domain.Artist import org.moire.ultrasonic.domain.Artist
import org.moire.ultrasonic.domain.Bookmark import org.moire.ultrasonic.domain.Bookmark
import org.moire.ultrasonic.domain.ChatMessage import org.moire.ultrasonic.domain.ChatMessage
@ -21,6 +22,7 @@ import org.moire.ultrasonic.domain.PodcastsChannel
import org.moire.ultrasonic.domain.SearchCriteria import org.moire.ultrasonic.domain.SearchCriteria
import org.moire.ultrasonic.domain.SearchResult import org.moire.ultrasonic.domain.SearchResult
import org.moire.ultrasonic.domain.Share import org.moire.ultrasonic.domain.Share
import org.moire.ultrasonic.domain.Track
import org.moire.ultrasonic.domain.UserInfo import org.moire.ultrasonic.domain.UserInfo
@Suppress("TooManyFunctions") @Suppress("TooManyFunctions")
@ -57,7 +59,7 @@ interface MusicService {
fun getMusicDirectory(id: String, name: String?, refresh: Boolean): MusicDirectory fun getMusicDirectory(id: String, name: String?, refresh: Boolean): MusicDirectory
@Throws(Exception::class) @Throws(Exception::class)
fun getArtist(id: String, name: String?, refresh: Boolean): List<MusicDirectory.Album> fun getArtist(id: String, name: String?, refresh: Boolean): List<Album>
@Throws(Exception::class) @Throws(Exception::class)
fun getAlbum(id: String, name: String?, refresh: Boolean): MusicDirectory fun getAlbum(id: String, name: String?, refresh: Boolean): MusicDirectory
@ -75,7 +77,7 @@ interface MusicService {
fun getPlaylists(refresh: Boolean): List<Playlist> fun getPlaylists(refresh: Boolean): List<Playlist>
@Throws(Exception::class) @Throws(Exception::class)
fun createPlaylist(id: String?, name: String?, entries: List<MusicDirectory.Entry>) fun createPlaylist(id: String?, name: String?, tracks: List<Track>)
@Throws(Exception::class) @Throws(Exception::class)
fun deletePlaylist(id: String) fun deletePlaylist(id: String)
@ -95,7 +97,7 @@ interface MusicService {
size: Int, size: Int,
offset: Int, offset: Int,
musicFolderId: String? musicFolderId: String?
): List<MusicDirectory.Album> ): List<Album>
@Throws(Exception::class) @Throws(Exception::class)
fun getAlbumList2( fun getAlbumList2(
@ -103,7 +105,7 @@ interface MusicService {
size: Int, size: Int,
offset: Int, offset: Int,
musicFolderId: String? musicFolderId: String?
): List<MusicDirectory.Album> ): List<Album>
@Throws(Exception::class) @Throws(Exception::class)
fun getRandomSongs(size: Int): MusicDirectory fun getRandomSongs(size: Int): MusicDirectory
@ -123,7 +125,7 @@ interface MusicService {
*/ */
@Throws(Exception::class) @Throws(Exception::class)
fun getDownloadInputStream( fun getDownloadInputStream(
song: MusicDirectory.Entry, song: Track,
offset: Long, offset: Long,
maxBitrate: Int, maxBitrate: Int,
save: Boolean save: Boolean

View File

@ -23,6 +23,7 @@ import java.util.regex.Pattern
import org.koin.core.component.KoinComponent 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.Album
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.Bookmark import org.moire.ultrasonic.domain.Bookmark
@ -38,6 +39,7 @@ import org.moire.ultrasonic.domain.PodcastsChannel
import org.moire.ultrasonic.domain.SearchCriteria import org.moire.ultrasonic.domain.SearchCriteria
import org.moire.ultrasonic.domain.SearchResult import org.moire.ultrasonic.domain.SearchResult
import org.moire.ultrasonic.domain.Share import org.moire.ultrasonic.domain.Share
import org.moire.ultrasonic.domain.Track
import org.moire.ultrasonic.domain.UserInfo import org.moire.ultrasonic.domain.UserInfo
import org.moire.ultrasonic.util.AbstractFile import org.moire.ultrasonic.util.AbstractFile
import org.moire.ultrasonic.util.Constants import org.moire.ultrasonic.util.Constants
@ -126,8 +128,8 @@ class OfflineMusicService : MusicService, KoinComponent {
override fun search(criteria: SearchCriteria): SearchResult { override fun search(criteria: SearchCriteria): SearchResult {
val artists: MutableList<ArtistOrIndex> = ArrayList() val artists: MutableList<ArtistOrIndex> = ArrayList()
val albums: MutableList<MusicDirectory.Album> = ArrayList() val albums: MutableList<Album> = ArrayList()
val songs: MutableList<MusicDirectory.Entry> = ArrayList() val songs: MutableList<Track> = ArrayList()
val root = FileUtil.musicDirectory val root = FileUtil.musicDirectory
var closeness: Int var closeness: Int
for (artistFile in FileUtil.listFiles(root)) { for (artistFile in FileUtil.listFiles(root)) {
@ -227,14 +229,14 @@ class OfflineMusicService : MusicService, KoinComponent {
@Suppress("TooGenericExceptionCaught") @Suppress("TooGenericExceptionCaught")
@Throws(Exception::class) @Throws(Exception::class)
override fun createPlaylist(id: String?, name: String?, entries: List<MusicDirectory.Entry>) { override fun createPlaylist(id: String?, name: String?, tracks: List<Track>) {
val playlistFile = val playlistFile =
FileUtil.getPlaylistFile(activeServerProvider.getActiveServer().name, name) FileUtil.getPlaylistFile(activeServerProvider.getActiveServer().name, name)
val fw = FileWriter(playlistFile) val fw = FileWriter(playlistFile)
val bw = BufferedWriter(fw) val bw = BufferedWriter(fw)
try { try {
fw.write("#EXTM3U\n") fw.write("#EXTM3U\n")
for (e in entries) { for (e in tracks) {
var filePath = FileUtil.getSongFile(e) var filePath = FileUtil.getSongFile(e)
if (!Storage.isPathExists(filePath)) { if (!Storage.isPathExists(filePath)) {
val ext = FileUtil.getExtension(filePath) val ext = FileUtil.getExtension(filePath)
@ -299,7 +301,7 @@ class OfflineMusicService : MusicService, KoinComponent {
size: Int, size: Int,
offset: Int, offset: Int,
musicFolderId: String? musicFolderId: String?
): List<MusicDirectory.Album> { ): List<Album> {
throw OfflineException("Album lists not available in offline mode") throw OfflineException("Album lists not available in offline mode")
} }
@ -309,7 +311,7 @@ class OfflineMusicService : MusicService, KoinComponent {
size: Int, size: Int,
offset: Int, offset: Int,
musicFolderId: String? musicFolderId: String?
): List<MusicDirectory.Album> { ): List<Album> {
throw OfflineException("getAlbumList2 isn't available in offline mode") throw OfflineException("getAlbumList2 isn't available in offline mode")
} }
@ -455,7 +457,7 @@ class OfflineMusicService : MusicService, KoinComponent {
@Throws(OfflineException::class) @Throws(OfflineException::class)
override fun getArtist(id: String, name: String?, refresh: Boolean): override fun getArtist(id: String, name: String?, refresh: Boolean):
List<MusicDirectory.Album> { List<Album> {
throw OfflineException("getArtist isn't available in offline mode") throw OfflineException("getArtist isn't available in offline mode")
} }
@ -471,7 +473,7 @@ class OfflineMusicService : MusicService, KoinComponent {
@Throws(OfflineException::class) @Throws(OfflineException::class)
override fun getDownloadInputStream( override fun getDownloadInputStream(
song: MusicDirectory.Entry, song: Track,
offset: Long, offset: Long,
maxBitrate: Int, maxBitrate: Int,
save: Boolean save: Boolean
@ -502,14 +504,14 @@ class OfflineMusicService : MusicService, KoinComponent {
return FileUtil.getBaseName(name) return FileUtil.getBaseName(name)
} }
private fun createEntry(file: AbstractFile, name: String?): MusicDirectory.Entry { private fun createEntry(file: AbstractFile, name: String?): Track {
val entry = MusicDirectory.Entry(file.path) val entry = Track(file.path)
entry.populateWithDataFrom(file, name) entry.populateWithDataFrom(file, name)
return entry return entry
} }
private fun createAlbum(file: AbstractFile, name: String?): MusicDirectory.Album { private fun createAlbum(file: AbstractFile, name: String?): Album {
val album = MusicDirectory.Album(file.path) val album = Album(file.path)
album.populateWithDataFrom(file, name) album.populateWithDataFrom(file, name)
return album return album
} }
@ -536,7 +538,7 @@ class OfflineMusicService : MusicService, KoinComponent {
* More extensive variant of Child.populateWithDataFrom(), which also parses the ID3 tags of * More extensive variant of Child.populateWithDataFrom(), which also parses the ID3 tags of
* a given track file. * a given track file.
*/ */
private fun MusicDirectory.Entry.populateWithDataFrom(file: AbstractFile, name: String?) { private fun Track.populateWithDataFrom(file: AbstractFile, name: String?) {
(this as MusicDirectory.Child).populateWithDataFrom(file, name) (this as MusicDirectory.Child).populateWithDataFrom(file, name)
val meta = RawMetadata(null) val meta = RawMetadata(null)
@ -609,8 +611,8 @@ class OfflineMusicService : MusicService, KoinComponent {
artistName: String, artistName: String,
file: AbstractFile, file: AbstractFile,
criteria: SearchCriteria, criteria: SearchCriteria,
albums: MutableList<MusicDirectory.Album>, albums: MutableList<Album>,
songs: MutableList<MusicDirectory.Entry> songs: MutableList<Track>
) { ) {
var closeness: Int var closeness: Int
for (albumFile in FileUtil.listMediaFiles(file)) { for (albumFile in FileUtil.listMediaFiles(file)) {

View File

@ -61,7 +61,7 @@ class PlaybackStateSerializer : KoinComponent {
val state = State() val state = State()
for (downloadFile in songs) { for (downloadFile in songs) {
state.songs.add(downloadFile.song) state.songs.add(downloadFile.track)
} }
state.currentPlayingIndex = currentPlayingIndex state.currentPlayingIndex = currentPlayingIndex

View File

@ -19,6 +19,7 @@ import org.moire.ultrasonic.api.subsonic.throwOnFailure
import org.moire.ultrasonic.api.subsonic.toStreamResponse import org.moire.ultrasonic.api.subsonic.toStreamResponse
import org.moire.ultrasonic.data.ActiveServerProvider import org.moire.ultrasonic.data.ActiveServerProvider
import org.moire.ultrasonic.data.ActiveServerProvider.Companion.isOffline import org.moire.ultrasonic.data.ActiveServerProvider.Companion.isOffline
import org.moire.ultrasonic.domain.Album
import org.moire.ultrasonic.domain.Artist import org.moire.ultrasonic.domain.Artist
import org.moire.ultrasonic.domain.Bookmark import org.moire.ultrasonic.domain.Bookmark
import org.moire.ultrasonic.domain.ChatMessage import org.moire.ultrasonic.domain.ChatMessage
@ -33,6 +34,7 @@ import org.moire.ultrasonic.domain.PodcastsChannel
import org.moire.ultrasonic.domain.SearchCriteria import org.moire.ultrasonic.domain.SearchCriteria
import org.moire.ultrasonic.domain.SearchResult import org.moire.ultrasonic.domain.SearchResult
import org.moire.ultrasonic.domain.Share import org.moire.ultrasonic.domain.Share
import org.moire.ultrasonic.domain.Track
import org.moire.ultrasonic.domain.UserInfo import org.moire.ultrasonic.domain.UserInfo
import org.moire.ultrasonic.domain.toArtistList import org.moire.ultrasonic.domain.toArtistList
import org.moire.ultrasonic.domain.toDomainEntitiesList import org.moire.ultrasonic.domain.toDomainEntitiesList
@ -143,7 +145,7 @@ open class RESTMusicService(
id: String, id: String,
name: String?, name: String?,
refresh: Boolean refresh: Boolean
): List<MusicDirectory.Album> { ): List<Album> {
val response = API.getArtist(id).execute().throwOnFailure() val response = API.getArtist(id).execute().throwOnFailure()
return response.body()!!.artist.toDomainEntityList() return response.body()!!.artist.toDomainEntityList()
@ -262,14 +264,14 @@ open class RESTMusicService(
override fun createPlaylist( override fun createPlaylist(
id: String?, id: String?,
name: String?, name: String?,
entries: List<MusicDirectory.Entry> tracks: List<Track>
) { ) {
if (id == null && name == null) if (id == null && name == null)
throw IllegalArgumentException("Either id or name is required.") throw IllegalArgumentException("Either id or name is required.")
val pSongIds: MutableList<String> = ArrayList(entries.size) val pSongIds: MutableList<String> = ArrayList(tracks.size)
for ((id1) in entries) { for ((id1) in tracks) {
pSongIds.add(id1) pSongIds.add(id1)
} }
@ -350,7 +352,7 @@ open class RESTMusicService(
size: Int, size: Int,
offset: Int, offset: Int,
musicFolderId: String? musicFolderId: String?
): List<MusicDirectory.Album> { ): List<Album> {
val response = API.getAlbumList( val response = API.getAlbumList(
fromName(type), fromName(type),
size, size,
@ -370,7 +372,7 @@ open class RESTMusicService(
size: Int, size: Int,
offset: Int, offset: Int,
musicFolderId: String? musicFolderId: String?
): List<MusicDirectory.Album> { ): List<Album> {
val response = API.getAlbumList2( val response = API.getAlbumList2(
fromName(type), fromName(type),
size, size,
@ -418,7 +420,7 @@ open class RESTMusicService(
@Throws(Exception::class) @Throws(Exception::class)
override fun getDownloadInputStream( override fun getDownloadInputStream(
song: MusicDirectory.Entry, song: Track,
offset: Long, offset: Long,
maxBitrate: Int, maxBitrate: Int,
save: Boolean save: Boolean

View File

@ -8,6 +8,7 @@ import java.util.LinkedList
import org.moire.ultrasonic.R import org.moire.ultrasonic.R
import org.moire.ultrasonic.data.ActiveServerProvider.Companion.isOffline import org.moire.ultrasonic.data.ActiveServerProvider.Companion.isOffline
import org.moire.ultrasonic.domain.MusicDirectory import org.moire.ultrasonic.domain.MusicDirectory
import org.moire.ultrasonic.domain.Track
import org.moire.ultrasonic.service.MediaPlayerController import org.moire.ultrasonic.service.MediaPlayerController
import org.moire.ultrasonic.service.MusicServiceFactory.getMusicService import org.moire.ultrasonic.service.MusicServiceFactory.getMusicService
import org.moire.ultrasonic.util.Constants import org.moire.ultrasonic.util.Constants
@ -33,7 +34,7 @@ class DownloadHandler(
autoPlay: Boolean, autoPlay: Boolean,
playNext: Boolean, playNext: Boolean,
shuffle: Boolean, shuffle: Boolean,
songs: List<MusicDirectory.Entry?> songs: List<Track?>
) { ) {
val onValid = Runnable { val onValid = Runnable {
if (!append && !playNext) { if (!append && !playNext) {
@ -195,15 +196,15 @@ class DownloadHandler(
isArtist: Boolean isArtist: Boolean
) { ) {
val activity = fragment.activity as Activity val activity = fragment.activity as Activity
val task = object : ModalBackgroundTask<List<MusicDirectory.Entry>>( val task = object : ModalBackgroundTask<List<Track>>(
activity, activity,
false false
) { ) {
@Throws(Throwable::class) @Throws(Throwable::class)
override fun doInBackground(): List<MusicDirectory.Entry> { override fun doInBackground(): List<Track> {
val musicService = getMusicService() val musicService = getMusicService()
val songs: MutableList<MusicDirectory.Entry> = LinkedList() val songs: MutableList<Track> = LinkedList()
val root: MusicDirectory val root: MusicDirectory
if (!isOffline() && isArtist && Settings.shouldUseId3Tags) { if (!isOffline() && isArtist && Settings.shouldUseId3Tags) {
getSongsForArtist(id, songs) getSongsForArtist(id, songs)
@ -235,7 +236,7 @@ class DownloadHandler(
@Throws(Exception::class) @Throws(Exception::class)
private fun getSongsRecursively( private fun getSongsRecursively(
parent: MusicDirectory, parent: MusicDirectory,
songs: MutableList<MusicDirectory.Entry> songs: MutableList<Track>
) { ) {
if (songs.size > maxSongs) { if (songs.size > maxSongs) {
return return
@ -259,7 +260,7 @@ class DownloadHandler(
@Throws(Exception::class) @Throws(Exception::class)
private fun getSongsForArtist( private fun getSongsForArtist(
id: String, id: String,
songs: MutableCollection<MusicDirectory.Entry> songs: MutableCollection<Track>
) { ) {
if (songs.size > maxSongs) { if (songs.size > maxSongs) {
return return
@ -280,7 +281,7 @@ class DownloadHandler(
} }
} }
override fun done(songs: List<MusicDirectory.Entry>) { override fun done(songs: List<Track>) {
if (Settings.shouldSortByDisc) { if (Settings.shouldSortByDisc) {
Collections.sort(songs, EntryByDiscAndTrackComparator()) Collections.sort(songs, EntryByDiscAndTrackComparator())
} }

View File

@ -15,8 +15,8 @@ import java.util.Locale
import java.util.regex.Pattern import java.util.regex.Pattern
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
import org.moire.ultrasonic.R import org.moire.ultrasonic.R
import org.moire.ultrasonic.domain.MusicDirectory
import org.moire.ultrasonic.domain.Share import org.moire.ultrasonic.domain.Share
import org.moire.ultrasonic.domain.Track
import org.moire.ultrasonic.service.MusicServiceFactory.getMusicService import org.moire.ultrasonic.service.MusicServiceFactory.getMusicService
import org.moire.ultrasonic.util.BackgroundTask import org.moire.ultrasonic.util.BackgroundTask
import org.moire.ultrasonic.util.CancellationToken import org.moire.ultrasonic.util.CancellationToken
@ -44,13 +44,13 @@ class ShareHandler(val context: Context) {
fun createShare( fun createShare(
fragment: Fragment, fragment: Fragment,
entries: List<MusicDirectory.Entry?>?, tracks: List<Track?>?,
swipe: SwipeRefreshLayout?, swipe: SwipeRefreshLayout?,
cancellationToken: CancellationToken cancellationToken: CancellationToken
) { ) {
val askForDetails = Settings.shouldAskForShareDetails val askForDetails = Settings.shouldAskForShareDetails
val shareDetails = ShareDetails() val shareDetails = ShareDetails()
shareDetails.Entries = entries shareDetails.Entries = tracks
if (askForDetails) { if (askForDetails) {
showDialog(fragment, shareDetails, swipe, cancellationToken) showDialog(fragment, shareDetails, swipe, cancellationToken)
} else { } else {

View File

@ -4,7 +4,7 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import org.moire.ultrasonic.R import org.moire.ultrasonic.R
import org.moire.ultrasonic.domain.MusicDirectory import org.moire.ultrasonic.domain.Track
import org.moire.ultrasonic.service.MusicServiceFactory import org.moire.ultrasonic.service.MusicServiceFactory
import org.moire.ultrasonic.util.Util import org.moire.ultrasonic.util.Util
@ -14,14 +14,14 @@ import org.moire.ultrasonic.util.Util
@Suppress("UtilityClassWithPublicConstructor") @Suppress("UtilityClassWithPublicConstructor")
class VideoPlayer { class VideoPlayer {
companion object { companion object {
fun playVideo(context: Context, entry: MusicDirectory.Entry?) { fun playVideo(context: Context, track: Track?) {
if (!Util.isNetworkConnected() || entry == null) { if (!Util.isNetworkConnected() || track == null) {
Util.toast(context, R.string.select_album_no_network) Util.toast(context, R.string.select_album_no_network)
return return
} }
try { try {
val intent = Intent(Intent.ACTION_VIEW) val intent = Intent(Intent.ACTION_VIEW)
val url = MusicServiceFactory.getMusicService().getVideoUrl(entry.id) val url = MusicServiceFactory.getMusicService().getVideoUrl(track.id)
intent.setDataAndType( intent.setDataAndType(
Uri.parse(url), Uri.parse(url),
"video/*" "video/*"

View File

@ -2,13 +2,14 @@ package org.moire.ultrasonic.util
import java.util.Comparator import java.util.Comparator
import org.moire.ultrasonic.domain.MusicDirectory import org.moire.ultrasonic.domain.MusicDirectory
import org.moire.ultrasonic.domain.Track
class EntryByDiscAndTrackComparator : Comparator<MusicDirectory.Child> { class EntryByDiscAndTrackComparator : Comparator<MusicDirectory.Child> {
override fun compare(x: MusicDirectory.Child, y: MusicDirectory.Child): Int { override fun compare(x: MusicDirectory.Child, y: MusicDirectory.Child): Int {
val discX = x.discNumber val discX = x.discNumber
val discY = y.discNumber val discY = y.discNumber
val trackX = if (x is MusicDirectory.Entry) x.track else null val trackX = if (x is Track) x.track else null
val trackY = if (y is MusicDirectory.Entry) y.track else null val trackY = if (y is Track) y.track else null
val albumX = x.album val albumX = x.album
val albumY = y.album val albumY = y.album
val pathX = x.path val pathX = x.path

View File

@ -27,6 +27,7 @@ import java.util.TreeSet
import java.util.regex.Pattern import java.util.regex.Pattern
import org.moire.ultrasonic.app.UApp import org.moire.ultrasonic.app.UApp
import org.moire.ultrasonic.domain.MusicDirectory import org.moire.ultrasonic.domain.MusicDirectory
import org.moire.ultrasonic.domain.Track
import org.moire.ultrasonic.util.Util.safeClose import org.moire.ultrasonic.util.Util.safeClose
import timber.log.Timber import timber.log.Timber
@ -49,7 +50,7 @@ object FileUtil {
const val SUFFIX_SMALL = ".jpeg-small" const val SUFFIX_SMALL = ".jpeg-small"
private const val UNNAMED = "unnamed" private const val UNNAMED = "unnamed"
fun getSongFile(song: MusicDirectory.Entry): String { fun getSongFile(song: Track): String {
val dir = getAlbumDirectory(song) val dir = getAlbumDirectory(song)
// Do not generate new name for offline files. Offline files will have their Path as their Id. // Do not generate new name for offline files. Offline files will have their Path as their Id.

View File

@ -188,7 +188,7 @@ class MediaSessionHandler : KoinComponent {
val metadata = MediaMetadataCompat.Builder() val metadata = MediaMetadataCompat.Builder()
if (currentPlaying != null) { if (currentPlaying != null) {
try { try {
val song = currentPlaying.song val song = currentPlaying.track
val cover = BitmapUtils.getAlbumArtBitmapFromDisk( val cover = BitmapUtils.getAlbumArtBitmapFromDisk(
song, Util.getMinDisplayMetric() song, Util.getMinDisplayMetric()
) )
@ -278,7 +278,7 @@ class MediaSessionHandler : KoinComponent {
val queue = playlist.mapIndexed { id, file -> val queue = playlist.mapIndexed { id, file ->
MediaSessionCompat.QueueItem( MediaSessionCompat.QueueItem(
Util.getMediaDescriptionForEntry(file.song), Util.getMediaDescriptionForEntry(file.track),
id.toLong() id.toLong()
) )
} }

View File

@ -55,6 +55,7 @@ import org.moire.ultrasonic.domain.Bookmark
import org.moire.ultrasonic.domain.MusicDirectory import org.moire.ultrasonic.domain.MusicDirectory
import org.moire.ultrasonic.domain.PlayerState import org.moire.ultrasonic.domain.PlayerState
import org.moire.ultrasonic.domain.SearchResult import org.moire.ultrasonic.domain.SearchResult
import org.moire.ultrasonic.domain.Track
import org.moire.ultrasonic.service.DownloadFile import org.moire.ultrasonic.service.DownloadFile
import timber.log.Timber import timber.log.Timber
@ -438,9 +439,9 @@ object Util {
@JvmStatic @JvmStatic
fun getSongsFromBookmarks(bookmarks: Iterable<Bookmark>): MusicDirectory { fun getSongsFromBookmarks(bookmarks: Iterable<Bookmark>): MusicDirectory {
val musicDirectory = MusicDirectory() val musicDirectory = MusicDirectory()
var song: MusicDirectory.Entry var song: Track
for (bookmark in bookmarks) { for (bookmark in bookmarks) {
song = bookmark.entry song = bookmark.track
song.bookmarkPosition = bookmark.position song.bookmarkPosition = bookmark.position
musicDirectory.add(song) musicDirectory.add(song)
} }
@ -450,7 +451,7 @@ object Util {
/** /**
* Broadcasts the given song info as the new song being played. * Broadcasts the given song info as the new song being played.
*/ */
fun broadcastNewTrackInfo(context: Context, song: MusicDirectory.Entry?) { fun broadcastNewTrackInfo(context: Context, song: Track?) {
val intent = Intent(EVENT_META_CHANGED) val intent = Intent(EVENT_META_CHANGED)
if (song != null) { if (song != null) {
intent.putExtra("title", song.title) intent.putExtra("title", song.title)
@ -476,9 +477,9 @@ object Util {
) { ) {
if (!Settings.shouldSendBluetoothNotifications) return if (!Settings.shouldSendBluetoothNotifications) return
var song: MusicDirectory.Entry? = null var song: Track? = null
val avrcpIntent = Intent(CM_AVRCP_METADATA_CHANGED) val avrcpIntent = Intent(CM_AVRCP_METADATA_CHANGED)
if (currentPlaying != null) song = currentPlaying.song if (currentPlaying != null) song = currentPlaying.track
fillIntent(avrcpIntent, song, playerPosition, id, listSize) fillIntent(avrcpIntent, song, playerPosition, id, listSize)
@ -489,7 +490,7 @@ object Util {
fun broadcastA2dpPlayStatusChange( fun broadcastA2dpPlayStatusChange(
context: Context, context: Context,
state: PlayerState?, state: PlayerState?,
newSong: MusicDirectory.Entry?, newSong: Track?,
listSize: Int, listSize: Int,
id: Int, id: Int,
playerPosition: Int playerPosition: Int
@ -520,7 +521,7 @@ object Util {
private fun fillIntent( private fun fillIntent(
intent: Intent, intent: Intent,
song: MusicDirectory.Entry?, song: Track?,
playerPosition: Int, playerPosition: Int,
id: Int, id: Int,
listSize: Int listSize: Int
@ -776,7 +777,7 @@ object Util {
) )
fun getMediaDescriptionForEntry( fun getMediaDescriptionForEntry(
song: MusicDirectory.Entry, song: Track,
mediaId: String? = null, mediaId: String? = null,
groupNameId: Int? = null groupNameId: Int? = null
): MediaDescriptionCompat { ): MediaDescriptionCompat {
@ -809,7 +810,7 @@ object Util {
} }
@Suppress("ComplexMethod", "LongMethod") @Suppress("ComplexMethod", "LongMethod")
fun readableEntryDescription(song: MusicDirectory.Entry): ReadableEntryDescription { fun readableEntryDescription(song: Track): ReadableEntryDescription {
val artist = StringBuilder(LINE_LENGTH) val artist = StringBuilder(LINE_LENGTH)
var bitRate: String? = null var bitRate: String? = null
var trackText = "" var trackText = ""

View File

@ -27,7 +27,7 @@ class APIBookmarkConverterTest {
comment `should be equal to` entity.comment comment `should be equal to` entity.comment
created `should be equal to` entity.created?.time created `should be equal to` entity.created?.time
changed `should be equal to` entity.changed?.time changed `should be equal to` entity.changed?.time
entry `should be equal to` entity.entry.toTrackEntity() track `should be equal to` entity.entry.toTrackEntity()
} }
} }