1
0
mirror of https://github.com/ultrasonic/ultrasonic synced 2025-02-16 11:41:16 +01:00

Various fixes & cleanups

This commit is contained in:
tzugen 2021-11-30 21:50:53 +01:00
parent 2ac1ea3f89
commit f2948cd3db
No known key found for this signature in database
GPG Key ID: 61E9C34BC10EC930
19 changed files with 196 additions and 223 deletions

View File

@ -2,9 +2,9 @@ package org.moire.ultrasonic.api.subsonic
import org.amshove.kluent.`should be equal to`
import org.junit.Test
import org.moire.ultrasonic.api.subsonic.models.Album
import org.moire.ultrasonic.api.subsonic.models.AlbumListType
import org.moire.ultrasonic.api.subsonic.models.AlbumListType.BY_GENRE
import org.moire.ultrasonic.api.subsonic.models.MusicDirectoryChild
/**
* Integration tests for [SubsonicAPIDefinition] for getAlbumList call.
@ -28,8 +28,8 @@ class SubsonicApiGetAlbumListRequestTest : SubsonicAPIClientTest() {
assertResponseSuccessful(response)
with(response.body()!!.albumList) {
size `should be equal to` 2
this[1] `should be equal to` MusicDirectoryChild(
id = "9997", parent = "9996", isDir = true,
this[1] `should be equal to` Album(
id = "9997", parent = "9996",
title = "Endless Forms Most Beautiful", album = "Endless Forms Most Beautiful",
artist = "Nightwish", year = 2015, genre = "Symphonic Metal",
coverArt = "9997", playCount = 11,

View File

@ -3,6 +3,7 @@ package org.moire.ultrasonic.api.subsonic
import org.amshove.kluent.`should be equal to`
import org.amshove.kluent.`should not be`
import org.junit.Test
import org.moire.ultrasonic.api.subsonic.models.Album
import org.moire.ultrasonic.api.subsonic.models.Artist
import org.moire.ultrasonic.api.subsonic.models.MusicDirectoryChild
import org.moire.ultrasonic.api.subsonic.models.SearchTwoResult
@ -32,9 +33,8 @@ class SubsonicApiSearchTwoTest : SubsonicAPIClientTest() {
artistList.size `should be equal to` 1
artistList[0] `should be equal to` Artist(id = "522", name = "The Prodigy")
albumList.size `should be equal to` 1
albumList[0] `should be equal to` MusicDirectoryChild(
id = "8867", parent = "522",
isDir = true, title = "Always Outnumbered, Never Outgunned",
albumList[0] `should be equal to` Album(
id = "8867", parent = "522", title = "Always Outnumbered, Never Outgunned",
album = "Always Outnumbered, Never Outgunned", artist = "The Prodigy",
year = 2004, genre = "Electronic", coverArt = "8867", playCount = 0,
created = parseDate("2016-10-23T20:57:27.000Z")

View File

@ -18,6 +18,7 @@ data class Album(
val duration: Int = 0,
val year: Int = 0,
val genre: String = "",
val playCount: Int = 0,
@JsonProperty("song") val songList: List<MusicDirectoryChild> = emptyList(),
@JsonProperty("starred") val starredDate: String = ""
)

View File

@ -42,8 +42,8 @@ empty-blocks:
complexity:
active: true
TooManyFunctions:
thresholdInFiles: 20
thresholdInClasses: 20
thresholdInFiles: 25
thresholdInClasses: 25
thresholdInInterfaces: 20
thresholdInObjects: 30
LabeledExpression:

View File

@ -1,20 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<issues format="5" by="lint 4.2.2" client="gradle" variant="release" version="4.2.2">
<issue
id="ObsoleteLintCustomCheck"
message="Lint found an issue registry (`androidx.appcompat.AppCompatIssueRegistry`) which is older than the current API level; these checks may not work correctly.&#xA;&#xA;Recompile the checks against the latest version. Custom check API version is 7 (4.0), current lint API level is 8 (4.1)">
<location
file="../../../../.gradle/caches/transforms-3/2f10f1fe0ff7ab74304d702879de0789/transformed/appcompat-1.2.0/jars/lint.jar"/>
</issue>
<issue
id="ObsoleteLintCustomCheck"
message="Lint found an issue registry (`timber.lint.TimberIssueRegistry`) which is older than the current API level; these checks may not work correctly.&#xA;&#xA;Recompile the checks against the latest version. Custom check API version is 1 (3.1), current lint API level is 8 (4.1)">
<location
file="../../../../.gradle/caches/transforms-3/e9d816753daf5450613abd98ccf3b80c/transformed/jetified-timber-4.7.1/jars/lint.jar"/>
</issue>
<issue
id="ScopedStorage"
message="WRITE_EXTERNAL_STORAGE no longer provides write access when targeting Android 10, unless you use `requestLegacyExternalStorage`"
@ -26,17 +12,6 @@
column="36"/>
</issue>
<issue
id="IncludeLayoutParam"
message="Layout parameter `layout_gravity` ignored unless both `layout_width` and `layout_height` are also specified on `&lt;include>` tag"
errorLine1=" a:layout_gravity=&quot;center_vertical&quot; />"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/res/layout/video_list_item.xml"
line="17"
column="9"/>
</issue>
<issue
id="InflateParams"
message="Avoid passing `null` as the view root (needed to resolve layout parameters on the inflated layout&apos;s root element)"
@ -202,17 +177,6 @@
column="5"/>
</issue>
<issue
id="Overdraw"
message="Possible overdraw: Root element paints background `?android:attr/selectableItemBackground` with a theme that also paints a background (inferred theme is `@style/NoActionBar`)"
errorLine1=" a:background=&quot;?android:attr/selectableItemBackground&quot;"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/res/drawable/ic_baseline_info_24.xml"
line="1"
column="1"/>
</issue>
<issue
id="UnusedResources"
message="The resource `R.drawable.ic_menu_arrow` appears to be unused"
@ -1020,7 +984,62 @@
column="13"/>
<location
file="src/main/res/values-zh-rCN/strings.xml"
line="289"
line="288"
column="13"/>
</issue>
<issue
id="UnusedResources"
message="The resource `R.string.select_album_all_songs` appears to be unused"
errorLine1=" &lt;string name=&quot;select_album_all_songs&quot;>All Songs by %s&lt;/string>"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/res/values/strings.xml"
line="381"
column="13"/>
<location
file="src/main/res/values-cs/strings.xml"
line="351"
column="13"/>
<location
file="src/main/res/values-de/strings.xml"
line="346"
column="13"/>
<location
file="src/main/res/values-es/strings.xml"
line="374"
column="13"/>
<location
file="src/main/res/values-fr/strings.xml"
line="365"
column="13"/>
<location
file="src/main/res/values-hu/strings.xml"
line="363"
column="13"/>
<location
file="src/main/res/values-nl/strings.xml"
line="374"
column="13"/>
<location
file="src/main/res/values-pl/strings.xml"
line="346"
column="13"/>
<location
file="src/main/res/values-pt/strings.xml"
line="346"
column="13"/>
<location
file="src/main/res/values-pt-rBR/strings.xml"
line="367"
column="13"/>
<location
file="src/main/res/values-ru/strings.xml"
line="365"
column="13"/>
<location
file="src/main/res/values-zh-rCN/strings.xml"
line="363"
column="13"/>
</issue>
@ -1086,17 +1105,6 @@
column="14"/>
</issue>
<issue
id="UselessParent"
message="This `LinearLayout` layout or its `LinearLayout` parent is useless"
errorLine1=" &lt;LinearLayout"
errorLine2=" ~~~~~~~~~~~~">
<location
file="src/main/res/layout/video_details.xml"
line="9"
column="6"/>
</issue>
<issue
id="IconDuplicates"
message="The following unrelated icon files have identical contents: list_pressed_holo_dark.9.png, list_pressed_holo_light.9.png">
@ -1502,17 +1510,6 @@
column="10"/>
</issue>
<issue
id="ContentDescription"
message="Missing `contentDescription` attribute on image"
errorLine1=" &lt;ImageView"
errorLine2=" ~~~~~~~~~">
<location
file="src/main/res/layout/video_list_item.xml"
line="19"
column="6"/>
</issue>
<issue
id="LabelFor"
message="Missing accessibility label: provide either a view with an `android:labelFor` that references this view or provide an `android:hint`"

View File

@ -166,7 +166,6 @@ class BaseAdapter<T : Identifiable> : MultiTypeAdapter() {
fun notifyChanged() {
// When the download state of an entry was changed by an external process,
// increase the revision counter in order to update the UI
selectionRevision.postValue(selectionRevision.value!! + 1)
}

View File

@ -16,7 +16,7 @@ class DividerBinder : ItemViewBinder<DividerBinder.Divider, DividerBinder.ViewHo
// Set our layout files
val layout = R.layout.list_item_divider
val more_button = R.layout.list_item_more_button
val moreButton = R.layout.list_item_more_button
override fun onBindViewHolder(holder: ViewHolder, item: Divider) {
// Set text

View File

@ -53,6 +53,9 @@ class TrackViewBinder(
holder.imageHelper = imageHelper
// Remove observer before binding
holder.observableChecked.removeObservers(lifecycleOwner)
holder.setSong(
file = downloadFile,
checkable = checkable,
@ -65,7 +68,7 @@ class TrackViewBinder(
val popup = Utils.createPopupMenu(holder.itemView, contextMenuLayout)
popup.setOnMenuItemClickListener { menuItem ->
onContextMenuClick?.invoke(menuItem, downloadFile)
onContextMenuClick.invoke(menuItem, downloadFile)
}
} else {
// Minimize or maximize the Text view (if song title is very long)
@ -78,22 +81,22 @@ class TrackViewBinder(
}
holder.itemView.setOnClickListener {
if (!checkable) {
onItemClick(downloadFile)
} else {
if (checkable && !downloadFile.song.isVideo) {
val nowChecked = !holder.check.isChecked
holder.isChecked = nowChecked
} else {
onItemClick(downloadFile)
}
}
// Notify the adapter of selection changes
holder.observableChecked.observe(
lifecycleOwner,
{ newValue ->
if (newValue) {
diffAdapter.notifySelected(item.longId)
{ isCheckedNow ->
if (isCheckedNow) {
diffAdapter.notifySelected(holder.entry!!.longId)
} else {
diffAdapter.notifyUnselected(item.longId)
diffAdapter.notifyUnselected(holder.entry!!.longId)
}
}
)

View File

@ -28,13 +28,11 @@ import timber.log.Timber
/**
* Used to display songs and videos in a `ListView`.
* FIXME: Add video List item
* FIXME: CHECKED bug
*/
class TrackViewHolder(val view: View) : RecyclerView.ViewHolder(view), Checkable, KoinComponent {
var check: CheckedTextView = view.findViewById(R.id.song_check)
var rating: LinearLayout = view.findViewById(R.id.song_rating)
private var rating: LinearLayout = view.findViewById(R.id.song_rating)
private var fiveStar1: ImageView = view.findViewById(R.id.song_five_star_1)
private var fiveStar2: ImageView = view.findViewById(R.id.song_five_star_2)
private var fiveStar3: ImageView = view.findViewById(R.id.song_five_star_3)
@ -90,7 +88,7 @@ class TrackViewHolder(val view: View) : RecyclerView.ViewHolder(view), Checkable
}
check.isVisible = (checkable && !song.isVideo)
setCheckedSilent(isSelected)
initChecked(isSelected)
drag.isVisible = draggable
if (ActiveServerProvider.isOffline()) {
@ -109,6 +107,11 @@ class TrackViewHolder(val view: View) : RecyclerView.ViewHolder(view), Checkable
setSingleStar(entry!!.starred)
}
if (song.isVideo) {
artist.isVisible = false
progress.isVisible = false
}
RxBus.playerStateObservable.subscribe {
setPlayIcon(it.track == downloadFile)
}
@ -248,14 +251,23 @@ class TrackViewHolder(val view: View) : RecyclerView.ViewHolder(view), Checkable
}
}
private fun setCheckedSilent(newStatus: Boolean) {
/*
* Set the checked value and re-init the MutableLiveData.
* If we would post a new value, there might be a short glitch where the track is shown with its
* old selection status before the posted value has been processed.
*/
private fun initChecked(newStatus: Boolean) {
observableChecked = MutableLiveData(newStatus)
check.isChecked = newStatus
}
/*
* To be correct, this method doesn't directly set the checked status.
* It only notifies the observable. If the selection tracker accepts the selection
* (might be false for Singular SelectionTrackers) then it will cause the actual modification.
*/
override fun setChecked(newStatus: Boolean) {
observableChecked.postValue(newStatus)
// FIXME, check if working
// check.isChecked = newStatus
}
override fun isChecked(): Boolean {

View File

@ -43,10 +43,10 @@ class AlbumListFragment : EntryListFragment<MusicDirectory.Album>() {
): LiveData<List<MusicDirectory.Album>> {
if (args == null) throw IllegalArgumentException("Required arguments are missing")
val refresh = args.getBoolean(Constants.INTENT_REFRESH) || refresh
val refresh2 = args.getBoolean(Constants.INTENT_REFRESH) || refresh
val append = args.getBoolean(Constants.INTENT_APPEND)
return listModel.getAlbumList(refresh or append, refreshListView!!, args)
return listModel.getAlbumList(refresh2 or append, refreshListView!!, args)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {

View File

@ -33,8 +33,8 @@ class ArtistListFragment : EntryListFragment<ArtistOrIndex>() {
* The central function to pass a query to the model and return a LiveData object
*/
override fun getLiveData(args: Bundle?, refresh: Boolean): LiveData<List<ArtistOrIndex>> {
val refresh = args?.getBoolean(Constants.INTENT_REFRESH) ?: false || refresh
return listModel.getItems(refresh, refreshListView!!)
val refresh2 = args?.getBoolean(Constants.INTENT_REFRESH) ?: false || refresh
return listModel.getItems(refresh2, refreshListView!!)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {

View File

@ -34,7 +34,10 @@ class BookmarksFragment : TrackCollectionFragment() {
viewAdapter.selectionType = BaseAdapter.SelectionType.SINGLE
}
override fun getLiveData(args: Bundle?, refresh: Boolean): LiveData<List<MusicDirectory.Child>> {
override fun getLiveData(
args: Bundle?,
refresh: Boolean
): LiveData<List<MusicDirectory.Child>> {
listModel.viewModelScope.launch(handler) {
refreshListView?.isRefreshing = true
listModel.getBookmarks()

View File

@ -868,7 +868,8 @@ class PlayerFragment :
)
dragTouchHelper = ItemTouchHelper(object : ItemTouchHelper.SimpleCallback(
ItemTouchHelper.UP or ItemTouchHelper.DOWN, 0
ItemTouchHelper.UP or ItemTouchHelper.DOWN,
ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT
) {
override fun onMove(
@ -887,7 +888,20 @@ class PlayerFragment :
return true
}
// Swipe to delete from playlist
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
val pos = viewHolder.bindingAdapterPosition
val file = mediaPlayerController.playList[pos]
mediaPlayerController.removeFromPlaylist(file)
val songRemoved = String.format(
resources.getString(R.string.download_song_removed),
file.song.title
)
Util.toast(context, songRemoved)
viewAdapter.submitList(mediaPlayerController.playList)
viewAdapter.notifyDataSetChanged()
}
}
)

View File

@ -120,7 +120,7 @@ class Downloader(
}
@Synchronized
@Suppress("ComplexMethod")
@Suppress("ComplexMethod", "ComplexCondition")
fun checkDownloadsInternal() {
if (
!Util.isExternalStoragePresent() ||
@ -502,7 +502,7 @@ class Downloader(
/**
* Extension function
* Gathers the donwload 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 {
return getDownloadFileForSong(this).apply {

View File

@ -299,6 +299,7 @@ class MediaPlayerController(
}
@Synchronized
// TODO: If a playlist contains an item twice, this call will wrongly remove all
fun removeFromPlaylist(downloadFile: DownloadFile) {
if (downloadFile == localMediaPlayer.currentPlaying) {
reset()

View File

@ -1,74 +1,84 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:a="http://schemas.android.com/apk/res/android"
a:layout_width="0dip"
<androidx.constraintlayout.widget.ConstraintLayout xmlns:a="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
a:id="@+id/linearLayout"
a:layout_width="fill_parent"
a:layout_height="wrap_content"
a:layout_gravity="center_vertical"
a:layout_weight="1"
a:layout_marginStart="4dp"
a:orientation="vertical">
a:layout_weight="1">
<LinearLayout
a:layout_width="fill_parent"
<TextView
a:id="@+id/song_track"
a:layout_width="wrap_content"
a:layout_height="wrap_content"
a:layout_gravity="center_vertical"
a:orientation="horizontal">
a:paddingEnd="6dip"
a:textAppearance="?android:attr/textAppearanceMedium"
app:layout_constraintBottom_toTopOf="@+id/song_artist"
app:layout_constraintEnd_toStartOf="@+id/song_title"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed"
tools:text="Track" />
<TextView
a:id="@+id/song_track"
a:layout_width="wrap_content"
a:layout_height="wrap_content"
a:paddingEnd="6dip"
a:layout_gravity="left|center_vertical"
a:textAppearance="?android:attr/textAppearanceMedium"/>
<TextView
a:id="@+id/song_title"
a:layout_width="0dp"
a:layout_height="wrap_content"
a:layout_gravity="left|center_vertical"
a:layout_weight="1"
a:drawablePadding="4dip"
a:ellipsize="end"
a:paddingEnd="4dip"
a:singleLine="true"
a:textAppearance="?android:attr/textAppearanceMedium"/>
<TextView
a:id="@+id/song_status"
a:layout_width="wrap_content"
a:layout_height="wrap_content"
a:layout_gravity="right|center_vertical"
a:drawablePadding="6dip"
a:paddingEnd="12dip"/>
</LinearLayout>
<LinearLayout
a:layout_width="fill_parent"
<TextView
a:id="@+id/song_title"
a:layout_width="0dp"
a:layout_height="wrap_content"
a:layout_gravity="center_vertical"
a:orientation="horizontal">
a:drawablePadding="4dip"
a:ellipsize="end"
a:paddingEnd="4dip"
a:singleLine="true"
a:textAppearance="?android:attr/textAppearanceMedium"
app:layout_constraintBottom_toTopOf="@+id/song_artist"
app:layout_constraintEnd_toStartOf="@+id/song_duration"
app:layout_constraintStart_toEndOf="@+id/song_track"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed"
tools:text="Title" />
<TextView
a:id="@+id/song_artist"
a:layout_width="0dip"
a:layout_height="wrap_content"
a:layout_gravity="left|center_vertical"
a:layout_weight="1"
a:ellipsize="middle"
a:paddingStart="1dip"
a:paddingEnd="4dip"
a:singleLine="true"
a:textAppearance="?android:attr/textAppearanceSmall"/>
<TextView
a:id="@+id/song_status"
a:layout_width="wrap_content"
a:layout_height="wrap_content"
a:drawablePadding="6dip"
a:paddingEnd="12dip"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/song_title"
app:layout_constraintTop_toTopOf="parent"
tools:text="100%" />
<TextView
a:id="@+id/song_duration"
a:layout_width="wrap_content"
a:layout_height="wrap_content"
a:layout_gravity="right|center_vertical"
a:paddingStart="3dip"
a:paddingEnd="9dip"
a:singleLine="true"
a:textAppearance="?android:attr/textAppearanceSmall"/>
</LinearLayout>
</LinearLayout>
<TextView
a:id="@+id/song_artist"
a:layout_width="0dp"
a:layout_height="wrap_content"
a:ellipsize="middle"
a:paddingStart="1dip"
a:paddingEnd="4dip"
a:singleLine="true"
a:textAppearance="?android:attr/textAppearanceSmall"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/song_duration"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/song_track"
tools:text="Artist" />
<TextView
a:id="@+id/song_duration"
a:layout_width="wrap_content"
a:layout_height="wrap_content"
a:paddingStart="3dip"
a:paddingEnd="9dip"
a:singleLine="true"
a:textAppearance="?android:attr/textAppearanceSmall"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/song_title"
app:layout_constraintTop_toBottomOf="@+id/song_status"
tools:text="Duration" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -1,38 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:a="http://schemas.android.com/apk/res/android"
a:layout_width="0dip"
a:layout_height="wrap_content"
a:layout_gravity="center_vertical"
a:layout_weight="1"
a:orientation="vertical">
<LinearLayout
a:layout_width="fill_parent"
a:layout_height="wrap_content"
a:layout_gravity="center_vertical"
a:orientation="horizontal">
<TextView
a:id="@+id/song_title"
a:layout_width="0dip"
a:layout_height="wrap_content"
a:layout_gravity="left|center_vertical"
a:layout_weight="1"
a:ellipsize="end"
a:paddingStart="4dip"
a:paddingEnd="2dip"
a:singleLine="true"
a:textAppearance="?android:attr/textAppearanceMedium"/>
<TextView
a:id="@+id/song_duration"
a:layout_width="wrap_content"
a:layout_height="wrap_content"
a:layout_gravity="right|center_vertical"
a:paddingStart="2dip"
a:paddingEnd="6dip"
a:singleLine="true"
a:textAppearance="?android:attr/textAppearanceSmall"/>
</LinearLayout>
</LinearLayout>

View File

@ -1,29 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:a="http://schemas.android.com/apk/res/android"
a:layout_width="fill_parent"
a:layout_height="wrap_content"
a:minHeight="?android:attr/listPreferredItemHeight"
a:orientation="horizontal" >
<CheckedTextView
a:id="@+id/song_check"
a:layout_width="wrap_content"
a:layout_height="fill_parent"
a:gravity="center_vertical"
a:paddingStart="1dip"
a:visibility="gone" />
<include layout="@layout/video_details"
a:layout_gravity="center_vertical" />
<ImageView
a:id="@+id/song_star"
a:layout_width="wrap_content"
a:layout_height="fill_parent"
a:background="@android:color/transparent"
a:focusable="false"
a:gravity="center_vertical"
a:paddingEnd="8dip"
a:src="?attr/star_hollow" />
</LinearLayout>

View File

@ -42,7 +42,7 @@ class APISearchConverterTest {
fun `Should convert SearchTwoResult to domain entity`() {
val entity = SearchTwoResult(
listOf(Artist(id = "82", name = "great-artist-name")),
listOf(MusicDirectoryChild(id = "762", artist = "bzz")),
listOf(Album(id = "762", artist = "bzz")),
listOf(MusicDirectoryChild(id = "9118", parent = "112"))
)