mirror of
https://github.com/ultrasonic/ultrasonic
synced 2025-02-05 11:37:31 +01:00
Fix context menus.
Also cleanup files, rename layouts
This commit is contained in:
parent
b33fe2d451
commit
82d90a6aee
@ -8,16 +8,6 @@ import java.util.Date
|
||||
class MusicDirectory : ArrayList<MusicDirectory.Child>() {
|
||||
var name: String? = null
|
||||
|
||||
fun addFirst(child: Child) {
|
||||
add(0, child)
|
||||
}
|
||||
|
||||
fun addChild(child: Child) {
|
||||
add(child)
|
||||
}
|
||||
|
||||
fun findChild(id: String): GenericEntry? = lastOrNull { it.id == id }
|
||||
|
||||
@JvmOverloads
|
||||
fun getChildren(
|
||||
includeDirs: Boolean = true,
|
||||
|
@ -55,7 +55,7 @@
|
||||
errorLine2=" ^">
|
||||
<location
|
||||
file="src/main/res/values-de/strings.xml"
|
||||
line="290"
|
||||
line="289"
|
||||
column="76"/>
|
||||
</issue>
|
||||
|
||||
@ -66,29 +66,18 @@
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/values/strings.xml"
|
||||
line="132"
|
||||
line="134"
|
||||
column="5"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="PluralsCandidate"
|
||||
message="Formatting %d followed by words ("tracks"): This should probably be a plural rather than a string"
|
||||
errorLine1=" <string name="select_album.n_selected">%d tracks selected.</string>"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
errorLine1=" <string name="select_album.n_selected">%d tracks selected</string>"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/values/strings.xml"
|
||||
line="149"
|
||||
column="5"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="PluralsCandidate"
|
||||
message="Formatting %d followed by words ("tracks"): This should probably be a plural rather than a string"
|
||||
errorLine1=" <string name="select_album.n_unselected">%d tracks unselected.</string>"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/values/strings.xml"
|
||||
line="150"
|
||||
line="151"
|
||||
column="5"/>
|
||||
</issue>
|
||||
|
||||
@ -136,17 +125,6 @@
|
||||
column="10"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="ObsoleteLayoutParam"
|
||||
message="Invalid layout param in a `LinearLayout`: `layout_alignParentLeft`"
|
||||
errorLine1=" a:layout_alignParentLeft="true""
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/layout/media_buttons.xml"
|
||||
line="14"
|
||||
column="9"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="ObsoleteLayoutParam"
|
||||
message="Invalid layout param in a `LinearLayout`: `layout_above`"
|
||||
@ -230,9 +208,9 @@
|
||||
errorLine1=" a:background="?android:attr/selectableItemBackground""
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/layout/select_folder_header.xml"
|
||||
line="11"
|
||||
column="5"/>
|
||||
file="src/main/res/drawable/ic_baseline_info_24.xml"
|
||||
line="1"
|
||||
column="1"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
@ -264,7 +242,7 @@
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/values/strings.xml"
|
||||
line="112"
|
||||
line="114"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-cs/strings.xml"
|
||||
@ -323,7 +301,7 @@
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/values/strings.xml"
|
||||
line="126"
|
||||
line="128"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-cs/strings.xml"
|
||||
@ -382,7 +360,7 @@
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/values/strings.xml"
|
||||
line="131"
|
||||
line="133"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-cs/strings.xml"
|
||||
@ -441,7 +419,7 @@
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/values/strings.xml"
|
||||
line="132"
|
||||
line="134"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-cs/strings.xml"
|
||||
@ -500,7 +478,7 @@
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/values/strings.xml"
|
||||
line="133"
|
||||
line="135"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-cs/strings.xml"
|
||||
@ -559,7 +537,7 @@
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/values/strings.xml"
|
||||
line="134"
|
||||
line="136"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-cs/strings.xml"
|
||||
@ -618,7 +596,7 @@
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/values/strings.xml"
|
||||
line="139"
|
||||
line="141"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-cs/strings.xml"
|
||||
@ -677,7 +655,7 @@
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/values/strings.xml"
|
||||
line="145"
|
||||
line="147"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-cs/strings.xml"
|
||||
@ -736,55 +714,55 @@
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/values/strings.xml"
|
||||
line="158"
|
||||
line="159"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-cs/strings.xml"
|
||||
line="141"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-de/strings.xml"
|
||||
line="140"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-de/strings.xml"
|
||||
line="139"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-es/strings.xml"
|
||||
line="156"
|
||||
line="155"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-fr/strings.xml"
|
||||
line="153"
|
||||
line="152"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-hu/strings.xml"
|
||||
line="151"
|
||||
line="150"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-it/strings.xml"
|
||||
line="137"
|
||||
line="136"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-nl/strings.xml"
|
||||
line="156"
|
||||
line="155"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-pl/strings.xml"
|
||||
line="139"
|
||||
line="138"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-pt/strings.xml"
|
||||
line="139"
|
||||
line="138"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-pt-rBR/strings.xml"
|
||||
line="153"
|
||||
line="152"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-ru/strings.xml"
|
||||
line="153"
|
||||
line="152"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-zh-rCN/strings.xml"
|
||||
line="152"
|
||||
line="151"
|
||||
column="13"/>
|
||||
</issue>
|
||||
|
||||
@ -795,7 +773,7 @@
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/values/strings.xml"
|
||||
line="159"
|
||||
line="160"
|
||||
column="13"/>
|
||||
</issue>
|
||||
|
||||
@ -806,7 +784,7 @@
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/values/strings.xml"
|
||||
line="160"
|
||||
line="161"
|
||||
column="13"/>
|
||||
</issue>
|
||||
|
||||
@ -817,55 +795,55 @@
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/values/strings.xml"
|
||||
line="176"
|
||||
line="177"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-cs/strings.xml"
|
||||
line="157"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-de/strings.xml"
|
||||
line="156"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-de/strings.xml"
|
||||
line="155"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-es/strings.xml"
|
||||
line="172"
|
||||
line="171"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-fr/strings.xml"
|
||||
line="169"
|
||||
line="168"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-hu/strings.xml"
|
||||
line="167"
|
||||
line="166"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-it/strings.xml"
|
||||
line="153"
|
||||
line="152"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-nl/strings.xml"
|
||||
line="172"
|
||||
line="171"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-pl/strings.xml"
|
||||
line="155"
|
||||
line="154"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-pt/strings.xml"
|
||||
line="155"
|
||||
line="154"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-pt-rBR/strings.xml"
|
||||
line="169"
|
||||
line="168"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-ru/strings.xml"
|
||||
line="169"
|
||||
line="168"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-zh-rCN/strings.xml"
|
||||
line="168"
|
||||
line="167"
|
||||
column="13"/>
|
||||
</issue>
|
||||
|
||||
@ -876,55 +854,55 @@
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/values/strings.xml"
|
||||
line="228"
|
||||
line="229"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-cs/strings.xml"
|
||||
line="209"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-de/strings.xml"
|
||||
line="208"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-de/strings.xml"
|
||||
line="207"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-es/strings.xml"
|
||||
line="224"
|
||||
line="223"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-fr/strings.xml"
|
||||
line="221"
|
||||
line="220"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-hu/strings.xml"
|
||||
line="219"
|
||||
line="218"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-it/strings.xml"
|
||||
line="204"
|
||||
line="203"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-nl/strings.xml"
|
||||
line="224"
|
||||
line="223"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-pl/strings.xml"
|
||||
line="207"
|
||||
line="206"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-pt/strings.xml"
|
||||
line="207"
|
||||
line="206"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-pt-rBR/strings.xml"
|
||||
line="221"
|
||||
line="220"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-ru/strings.xml"
|
||||
line="221"
|
||||
line="220"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-zh-rCN/strings.xml"
|
||||
line="218"
|
||||
line="217"
|
||||
column="13"/>
|
||||
</issue>
|
||||
|
||||
@ -935,55 +913,55 @@
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/values/strings.xml"
|
||||
line="297"
|
||||
line="298"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-cs/strings.xml"
|
||||
line="274"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-de/strings.xml"
|
||||
line="273"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-de/strings.xml"
|
||||
line="272"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-es/strings.xml"
|
||||
line="293"
|
||||
line="292"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-fr/strings.xml"
|
||||
line="288"
|
||||
line="287"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-hu/strings.xml"
|
||||
line="286"
|
||||
line="285"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-it/strings.xml"
|
||||
line="267"
|
||||
line="266"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-nl/strings.xml"
|
||||
line="293"
|
||||
line="292"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-pl/strings.xml"
|
||||
line="272"
|
||||
line="271"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-pt/strings.xml"
|
||||
line="272"
|
||||
line="271"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-pt-rBR/strings.xml"
|
||||
line="290"
|
||||
line="289"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-ru/strings.xml"
|
||||
line="288"
|
||||
line="287"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-zh-rCN/strings.xml"
|
||||
line="286"
|
||||
line="285"
|
||||
column="13"/>
|
||||
</issue>
|
||||
|
||||
@ -994,51 +972,51 @@
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/values/strings.xml"
|
||||
line="300"
|
||||
line="301"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-cs/strings.xml"
|
||||
line="277"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-de/strings.xml"
|
||||
line="276"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-de/strings.xml"
|
||||
line="275"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-es/strings.xml"
|
||||
line="296"
|
||||
line="295"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-fr/strings.xml"
|
||||
line="291"
|
||||
line="290"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-hu/strings.xml"
|
||||
line="289"
|
||||
line="288"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-it/strings.xml"
|
||||
line="270"
|
||||
line="269"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-nl/strings.xml"
|
||||
line="296"
|
||||
line="295"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-pl/strings.xml"
|
||||
line="275"
|
||||
line="274"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-pt/strings.xml"
|
||||
line="275"
|
||||
line="274"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-pt-rBR/strings.xml"
|
||||
line="293"
|
||||
line="292"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-ru/strings.xml"
|
||||
line="291"
|
||||
line="290"
|
||||
column="13"/>
|
||||
<location
|
||||
file="src/main/res/values-zh-rCN/strings.xml"
|
||||
@ -1053,7 +1031,7 @@
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/values/strings.xml"
|
||||
line="397"
|
||||
line="387"
|
||||
column="13"/>
|
||||
</issue>
|
||||
|
||||
@ -1064,47 +1042,47 @@
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/values/strings.xml"
|
||||
line="476"
|
||||
line="470"
|
||||
column="14"/>
|
||||
<location
|
||||
file="src/main/res/values-cs/strings.xml"
|
||||
line="448"
|
||||
line="436"
|
||||
column="14"/>
|
||||
<location
|
||||
file="src/main/res/values-es/strings.xml"
|
||||
line="470"
|
||||
line="458"
|
||||
column="14"/>
|
||||
<location
|
||||
file="src/main/res/values-fr/strings.xml"
|
||||
line="459"
|
||||
line="447"
|
||||
column="14"/>
|
||||
<location
|
||||
file="src/main/res/values-hu/strings.xml"
|
||||
line="454"
|
||||
line="442"
|
||||
column="14"/>
|
||||
<location
|
||||
file="src/main/res/values-nl/strings.xml"
|
||||
line="470"
|
||||
line="458"
|
||||
column="14"/>
|
||||
<location
|
||||
file="src/main/res/values-pl/strings.xml"
|
||||
line="401"
|
||||
line="389"
|
||||
column="14"/>
|
||||
<location
|
||||
file="src/main/res/values-pt/strings.xml"
|
||||
line="388"
|
||||
line="376"
|
||||
column="14"/>
|
||||
<location
|
||||
file="src/main/res/values-pt-rBR/strings.xml"
|
||||
line="463"
|
||||
line="451"
|
||||
column="14"/>
|
||||
<location
|
||||
file="src/main/res/values-ru/strings.xml"
|
||||
line="471"
|
||||
line="459"
|
||||
column="14"/>
|
||||
<location
|
||||
file="src/main/res/values-zh-rCN/strings.xml"
|
||||
line="452"
|
||||
line="440"
|
||||
column="14"/>
|
||||
</issue>
|
||||
|
||||
@ -1194,17 +1172,6 @@
|
||||
column="4"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="ViewConstructor"
|
||||
message="Custom view `AlbumView` is missing constructor used by tools: `(Context)` or `(Context,AttributeSet)` or `(Context,AttributeSet,int)`"
|
||||
errorLine1="public class AlbumView extends UpdateView"
|
||||
errorLine2=" ~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/org/moire/ultrasonic/view/AlbumView.java"
|
||||
line="40"
|
||||
column="14"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="ClickableViewAccessibility"
|
||||
message="Custom view ``AutoRepeatButton`` has `setOnTouchListener` called on it but does not override `performClick`"
|
||||
@ -1216,149 +1183,6 @@
|
||||
column="3"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="ClickableViewAccessibility"
|
||||
message="`onTouch` lambda should call `View#performClick` when a click is detected"
|
||||
errorLine1=" getView().setOnTouchListener((v, event) -> handleOnTouch(event));"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/org/moire/ultrasonic/fragment/NowPlayingFragment.java"
|
||||
line="133"
|
||||
column="42"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="ContentDescription"
|
||||
message="Missing `contentDescription` attribute on image"
|
||||
errorLine1=" <ImageView"
|
||||
errorLine2=" ~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/layout/album_buttons.xml"
|
||||
line="9"
|
||||
column="6"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="ContentDescription"
|
||||
message="Missing `contentDescription` attribute on image"
|
||||
errorLine1=" <ImageView"
|
||||
errorLine2=" ~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/layout/album_buttons.xml"
|
||||
line="18"
|
||||
column="6"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="ContentDescription"
|
||||
message="Missing `contentDescription` attribute on image"
|
||||
errorLine1=" <ImageView"
|
||||
errorLine2=" ~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/layout/album_buttons.xml"
|
||||
line="27"
|
||||
column="6"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="ContentDescription"
|
||||
message="Missing `contentDescription` attribute on image"
|
||||
errorLine1=" <ImageView"
|
||||
errorLine2=" ~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/layout/album_buttons.xml"
|
||||
line="36"
|
||||
column="6"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="ContentDescription"
|
||||
message="Missing `contentDescription` attribute on image"
|
||||
errorLine1=" <ImageView"
|
||||
errorLine2=" ~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/layout/album_buttons.xml"
|
||||
line="45"
|
||||
column="6"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="ContentDescription"
|
||||
message="Missing `contentDescription` attribute on image"
|
||||
errorLine1=" <ImageView"
|
||||
errorLine2=" ~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/layout/album_buttons.xml"
|
||||
line="54"
|
||||
column="6"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="ContentDescription"
|
||||
message="Missing `contentDescription` attribute on image"
|
||||
errorLine1=" <ImageView"
|
||||
errorLine2=" ~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/layout/album_buttons.xml"
|
||||
line="63"
|
||||
column="6"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="ContentDescription"
|
||||
message="Missing `contentDescription` attribute on image"
|
||||
errorLine1=" <ImageView"
|
||||
errorLine2=" ~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/layout/album_buttons.xml"
|
||||
line="72"
|
||||
column="6"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="ContentDescription"
|
||||
message="Missing `contentDescription` attribute on image"
|
||||
errorLine1=" <ImageView"
|
||||
errorLine2=" ~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/layout/album_buttons.xml"
|
||||
line="81"
|
||||
column="6"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="ContentDescription"
|
||||
message="Missing `contentDescription` attribute on image"
|
||||
errorLine1=" <ImageView"
|
||||
errorLine2=" ~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/layout/album_list_item.xml"
|
||||
line="61"
|
||||
column="6"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="ContentDescription"
|
||||
message="Missing `contentDescription` attribute on image"
|
||||
errorLine1=" <ImageView"
|
||||
errorLine2=" ~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/layout/album_list_item_legacy.xml"
|
||||
line="8"
|
||||
column="6"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="ContentDescription"
|
||||
message="Missing `contentDescription` attribute on image"
|
||||
errorLine1=" <ImageView"
|
||||
errorLine2=" ~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/layout/album_list_item_legacy.xml"
|
||||
line="41"
|
||||
column="6"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="ContentDescription"
|
||||
message="Missing `contentDescription` attribute on image"
|
||||
@ -1568,116 +1392,6 @@
|
||||
column="6"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="ContentDescription"
|
||||
message="Missing `contentDescription` attribute on image"
|
||||
errorLine1=" <ImageView"
|
||||
errorLine2=" ~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/layout/current_playing.xml"
|
||||
line="45"
|
||||
column="22"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="ContentDescription"
|
||||
message="Missing `contentDescription` attribute on image"
|
||||
errorLine1=" <ImageView"
|
||||
errorLine2=" ~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/layout-land/current_playing.xml"
|
||||
line="46"
|
||||
column="22"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="ContentDescription"
|
||||
message="Missing `contentDescription` attribute on image"
|
||||
errorLine1=" <ImageView"
|
||||
errorLine2=" ~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/layout/current_playing.xml"
|
||||
line="57"
|
||||
column="22"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="ContentDescription"
|
||||
message="Missing `contentDescription` attribute on image"
|
||||
errorLine1=" <ImageView"
|
||||
errorLine2=" ~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/layout-land/current_playing.xml"
|
||||
line="58"
|
||||
column="22"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="ContentDescription"
|
||||
message="Missing `contentDescription` attribute on image"
|
||||
errorLine1=" <ImageView"
|
||||
errorLine2=" ~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/layout/current_playing.xml"
|
||||
line="69"
|
||||
column="22"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="ContentDescription"
|
||||
message="Missing `contentDescription` attribute on image"
|
||||
errorLine1=" <ImageView"
|
||||
errorLine2=" ~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/layout-land/current_playing.xml"
|
||||
line="70"
|
||||
column="22"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="ContentDescription"
|
||||
message="Missing `contentDescription` attribute on image"
|
||||
errorLine1=" <ImageView"
|
||||
errorLine2=" ~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/layout/current_playing.xml"
|
||||
line="81"
|
||||
column="22"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="ContentDescription"
|
||||
message="Missing `contentDescription` attribute on image"
|
||||
errorLine1=" <ImageView"
|
||||
errorLine2=" ~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/layout-land/current_playing.xml"
|
||||
line="82"
|
||||
column="22"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="ContentDescription"
|
||||
message="Missing `contentDescription` attribute on image"
|
||||
errorLine1=" <ImageView"
|
||||
errorLine2=" ~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/layout/current_playing.xml"
|
||||
line="93"
|
||||
column="22"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="ContentDescription"
|
||||
message="Missing `contentDescription` attribute on image"
|
||||
errorLine1=" <ImageView"
|
||||
errorLine2=" ~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/layout-land/current_playing.xml"
|
||||
line="94"
|
||||
column="22"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="ContentDescription"
|
||||
message="Missing `contentDescription` attribute on image"
|
||||
@ -1728,30 +1442,19 @@
|
||||
errorLine1=" <ImageView"
|
||||
errorLine2=" ~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/layout/select_folder_header.xml"
|
||||
file="src/main/res/layout/list_header_folder.xml"
|
||||
line="15"
|
||||
column="6"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="ContentDescription"
|
||||
message="Missing `contentDescription` attribute on image"
|
||||
errorLine1=" <ImageView"
|
||||
errorLine2=" ~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/layout/song_list_item.xml"
|
||||
line="9"
|
||||
column="6"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="ContentDescription"
|
||||
message="Missing `contentDescription` attribute on image"
|
||||
errorLine1=" <ImageView"
|
||||
errorLine2=" ~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/layout/song_list_item.xml"
|
||||
line="38"
|
||||
file="src/main/res/layout/list_item_track.xml"
|
||||
line="39"
|
||||
column="10"/>
|
||||
</issue>
|
||||
|
||||
@ -1761,8 +1464,8 @@
|
||||
errorLine1=" <ImageView"
|
||||
errorLine2=" ~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/layout/song_list_item.xml"
|
||||
line="48"
|
||||
file="src/main/res/layout/list_item_track.xml"
|
||||
line="49"
|
||||
column="10"/>
|
||||
</issue>
|
||||
|
||||
@ -1772,8 +1475,8 @@
|
||||
errorLine1=" <ImageView"
|
||||
errorLine2=" ~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/layout/song_list_item.xml"
|
||||
line="58"
|
||||
file="src/main/res/layout/list_item_track.xml"
|
||||
line="59"
|
||||
column="10"/>
|
||||
</issue>
|
||||
|
||||
@ -1783,8 +1486,8 @@
|
||||
errorLine1=" <ImageView"
|
||||
errorLine2=" ~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/layout/song_list_item.xml"
|
||||
line="68"
|
||||
file="src/main/res/layout/list_item_track.xml"
|
||||
line="69"
|
||||
column="10"/>
|
||||
</issue>
|
||||
|
||||
@ -1794,22 +1497,11 @@
|
||||
errorLine1=" <ImageView"
|
||||
errorLine2=" ~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/layout/song_list_item.xml"
|
||||
line="78"
|
||||
file="src/main/res/layout/list_item_track.xml"
|
||||
line="79"
|
||||
column="10"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="ContentDescription"
|
||||
message="Missing `contentDescription` attribute on image"
|
||||
errorLine1=" <ImageView"
|
||||
errorLine2=" ~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/layout/song_list_item.xml"
|
||||
line="91"
|
||||
column="6"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="ContentDescription"
|
||||
message="Missing `contentDescription` attribute on image"
|
||||
@ -1953,17 +1645,6 @@
|
||||
column="17"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="HardcodedText"
|
||||
message="Hardcoded string "A", should use `@string` resource"
|
||||
errorLine1=" a:text="A""
|
||||
errorLine2=" ~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/layout/artist_list_item.xml"
|
||||
line="20"
|
||||
column="9"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="HardcodedText"
|
||||
message="Hardcoded string "0 dB", should use `@string` resource"
|
||||
@ -1975,28 +1656,6 @@
|
||||
column="13"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="HardcodedText"
|
||||
message="Hardcoded string "0 / 0", should use `@string` resource"
|
||||
errorLine1=" a:text="0 / 0""
|
||||
errorLine2=" ~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/layout/player_media_info.xml"
|
||||
line="64"
|
||||
column="13"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="HardcodedText"
|
||||
message="Hardcoded string "Share", should use `@string` resource"
|
||||
errorLine1=" a:title="Share"/>"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/menu/select_song_context.xml"
|
||||
line="24"
|
||||
column="9"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="HardcodedText"
|
||||
message="Hardcoded string "http://", should use `@string` resource"
|
||||
@ -2015,7 +1674,7 @@
|
||||
errorLine2=" ~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/layout/player_media_info.xml"
|
||||
line="50"
|
||||
line="52"
|
||||
column="6"/>
|
||||
</issue>
|
||||
|
||||
|
@ -19,8 +19,6 @@
|
||||
package org.moire.ultrasonic.view;
|
||||
|
||||
import android.content.Context;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@ -28,6 +26,9 @@ import android.widget.ArrayAdapter;
|
||||
import android.widget.SectionIndexer;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.moire.ultrasonic.R;
|
||||
import org.moire.ultrasonic.domain.Artist;
|
||||
|
||||
@ -49,7 +50,7 @@ public class ArtistAdapter extends ArrayAdapter<Artist> implements SectionIndexe
|
||||
|
||||
public ArtistAdapter(Context context, List<Artist> artists)
|
||||
{
|
||||
super(context, R.layout.generic_text_list_item, artists);
|
||||
super(context, R.layout.list_item_generic, artists);
|
||||
|
||||
layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
|
||||
@ -81,7 +82,7 @@ public class ArtistAdapter extends ArrayAdapter<Artist> implements SectionIndexe
|
||||
) {
|
||||
View rowView = convertView;
|
||||
if (rowView == null) {
|
||||
rowView = layoutInflater.inflate(R.layout.generic_text_list_item, parent, false);
|
||||
rowView = layoutInflater.inflate(R.layout.list_item_generic, parent, false);
|
||||
}
|
||||
((TextView) rowView).setText(getItem(position).getName());
|
||||
|
||||
|
@ -48,7 +48,7 @@ public class GenreAdapter extends ArrayAdapter<Genre> implements SectionIndexer
|
||||
|
||||
public GenreAdapter(Context context, List<Genre> genres)
|
||||
{
|
||||
super(context, R.layout.generic_text_list_item, genres);
|
||||
super(context, R.layout.list_item_generic, genres);
|
||||
|
||||
layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
|
||||
@ -75,7 +75,7 @@ public class GenreAdapter extends ArrayAdapter<Genre> implements SectionIndexer
|
||||
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
|
||||
View rowView = convertView;
|
||||
if (rowView == null) {
|
||||
rowView = layoutInflater.inflate(R.layout.generic_text_list_item, parent, false);
|
||||
rowView = layoutInflater.inflate(R.layout.list_item_generic, parent, false);
|
||||
}
|
||||
|
||||
((TextView) rowView).setText(getItem(position).getName());
|
||||
|
@ -43,15 +43,15 @@ class AlbumRowBinder(
|
||||
Util.getDrawableFromAttribute(context, R.attr.star_hollow)
|
||||
|
||||
// Set our layout files
|
||||
val layout = R.layout.album_list_item
|
||||
val contextMenuLayout = R.menu.artist_context_menu
|
||||
val layout = R.layout.list_item_album
|
||||
val contextMenuLayout = R.menu.context_menu_artist
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, item: MusicDirectory.Album) {
|
||||
holder.album.text = item.title
|
||||
holder.artist.text = item.artist
|
||||
holder.details.setOnClickListener { onItemClick(item) }
|
||||
holder.details.setOnLongClickListener {
|
||||
val popup = Helper.createPopupMenu(holder.itemView)
|
||||
val popup = Utils.createPopupMenu(holder.itemView)
|
||||
|
||||
popup.setOnMenuItemClickListener { menuItem ->
|
||||
onContextMenuClick(menuItem, item)
|
||||
@ -78,7 +78,7 @@ class AlbumRowBinder(
|
||||
var album: TextView = view.findViewById(R.id.album_title)
|
||||
var artist: TextView = view.findViewById(R.id.album_artist)
|
||||
var details: LinearLayout = view.findViewById(R.id.row_album_details)
|
||||
var coverArt: ImageView = view.findViewById(R.id.album_coverart)
|
||||
var coverArt: ImageView = view.findViewById(R.id.coverart)
|
||||
var star: ImageView = view.findViewById(R.id.album_star)
|
||||
var coverArtId: String? = null
|
||||
}
|
||||
|
@ -26,7 +26,6 @@ import org.moire.ultrasonic.util.Settings
|
||||
|
||||
/**
|
||||
* Creates a Row in a RecyclerView which contains the details of an Artist
|
||||
* FIXME: On click wrong display...
|
||||
*/
|
||||
class ArtistRowBinder(
|
||||
val onItemClick: (ArtistOrIndex) -> Unit,
|
||||
@ -35,8 +34,8 @@ class ArtistRowBinder(
|
||||
private val enableSections: Boolean = true
|
||||
) : ItemViewBinder<ArtistOrIndex, ArtistRowBinder.ViewHolder>(), KoinComponent {
|
||||
|
||||
val layout = R.layout.artist_list_item
|
||||
val contextMenuLayout = R.menu.artist_context_menu
|
||||
val layout = R.layout.list_item_artist
|
||||
val contextMenuLayout = R.menu.context_menu_artist
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, item: ArtistOrIndex) {
|
||||
holder.textView.text = item.name
|
||||
@ -44,7 +43,7 @@ class ArtistRowBinder(
|
||||
holder.section.isVisible = enableSections
|
||||
holder.layout.setOnClickListener { onItemClick(item) }
|
||||
holder.layout.setOnLongClickListener {
|
||||
val popup = Helper.createPopupMenu(holder.itemView)
|
||||
val popup = Utils.createPopupMenu(holder.itemView, contextMenuLayout)
|
||||
|
||||
popup.setOnMenuItemClickListener { menuItem ->
|
||||
onContextMenuClick(menuItem, item)
|
||||
@ -106,8 +105,8 @@ class ArtistRowBinder(
|
||||
) : RecyclerView.ViewHolder(itemView) {
|
||||
var section: TextView = itemView.findViewById(R.id.row_section)
|
||||
var textView: TextView = itemView.findViewById(R.id.row_artist_name)
|
||||
var layout: RelativeLayout = itemView.findViewById(R.id.row_artist_layout)
|
||||
var coverArt: ImageView = itemView.findViewById(R.id.artist_coverart)
|
||||
var layout: RelativeLayout = itemView.findViewById(R.id.containing_layout)
|
||||
var coverArt: ImageView = itemView.findViewById(R.id.coverart)
|
||||
var coverArtId: String? = null
|
||||
}
|
||||
|
||||
|
@ -22,7 +22,7 @@ import org.moire.ultrasonic.util.BoundedTreeSet
|
||||
* The BaseAdapter which extends the MultiTypeAdapter from an external library.
|
||||
* It provides selection support as well as Diffing the submitted lists for performance.
|
||||
*
|
||||
* It should be kept generic enought that it can be used a Base for all lists in the app.
|
||||
* It should be kept generic enough that it can be used a Base for all lists in the app.
|
||||
*/
|
||||
class BaseAdapter<T : Identifiable> : MultiTypeAdapter() {
|
||||
|
||||
|
@ -15,7 +15,7 @@ import org.moire.ultrasonic.domain.Identifiable
|
||||
class DividerBinder : ItemViewBinder<DividerBinder.Divider, DividerBinder.ViewHolder>() {
|
||||
|
||||
// Set our layout files
|
||||
val layout = R.layout.row_divider
|
||||
val layout = R.layout.list_item_divider
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, item: Divider) {
|
||||
// Set text
|
||||
|
@ -29,7 +29,7 @@ class FolderSelectorBinder(context: Context) :
|
||||
private val weakContext: WeakReference<Context> = WeakReference(context)
|
||||
|
||||
// Set our layout files
|
||||
val layout = R.layout.select_album_header
|
||||
val layout = R.layout.list_header_folder
|
||||
|
||||
override fun onCreateViewHolder(inflater: LayoutInflater, parent: ViewGroup): ViewHolder {
|
||||
return ViewHolder(inflater.inflate(layout, parent, false), weakContext)
|
||||
|
@ -30,7 +30,7 @@ class HeaderViewBinder(
|
||||
private val imageLoaderProvider: ImageLoaderProvider by inject()
|
||||
|
||||
// Set our layout files
|
||||
val layout = R.layout.select_album_header
|
||||
val layout = R.layout.list_header_album
|
||||
|
||||
override fun onCreateViewHolder(inflater: LayoutInflater, parent: ViewGroup): ViewHolder {
|
||||
return ViewHolder(inflater.inflate(layout, parent, false))
|
||||
|
@ -1,22 +0,0 @@
|
||||
package org.moire.ultrasonic.adapters
|
||||
|
||||
import android.view.MenuInflater
|
||||
import android.view.View
|
||||
import android.widget.PopupMenu
|
||||
import org.moire.ultrasonic.R
|
||||
import org.moire.ultrasonic.data.ActiveServerProvider
|
||||
|
||||
object Helper {
|
||||
@JvmStatic
|
||||
fun createPopupMenu(view: View, layout: Int = R.menu.artist_context_menu): PopupMenu {
|
||||
val popup = PopupMenu(view.context, view)
|
||||
val inflater: MenuInflater = popup.menuInflater
|
||||
inflater.inflate(layout, popup.menu)
|
||||
|
||||
val downloadMenuItem = popup.menu.findItem(R.id.menu_download)
|
||||
downloadMenuItem?.isVisible = !ActiveServerProvider.isOffline()
|
||||
|
||||
popup.show()
|
||||
return popup
|
||||
}
|
||||
}
|
@ -1,47 +1,2 @@
|
||||
package org.moire.ultrasonic.adapters
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.drawable.Drawable
|
||||
import org.moire.ultrasonic.R
|
||||
import org.moire.ultrasonic.util.Settings
|
||||
import org.moire.ultrasonic.util.Util
|
||||
|
||||
/**
|
||||
* Provides cached drawables for the UI
|
||||
*/
|
||||
class ImageHelper(context: Context) {
|
||||
|
||||
lateinit var errorImage: Drawable
|
||||
lateinit var starHollowDrawable: Drawable
|
||||
lateinit var starDrawable: Drawable
|
||||
lateinit var pinImage: Drawable
|
||||
lateinit var downloadedImage: Drawable
|
||||
lateinit var downloadingImage: Drawable
|
||||
lateinit var playingImage: Drawable
|
||||
var theme: String
|
||||
|
||||
fun rebuild(context: Context, force: Boolean = false) {
|
||||
val currentTheme = Settings.theme
|
||||
val themesMatch = theme == currentTheme
|
||||
if (!themesMatch) theme = currentTheme
|
||||
|
||||
if (!themesMatch || force) {
|
||||
getDrawables(context)
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
theme = Settings.theme
|
||||
getDrawables(context)
|
||||
}
|
||||
|
||||
private fun getDrawables(context: Context) {
|
||||
starHollowDrawable = Util.getDrawableFromAttribute(context, R.attr.star_hollow)
|
||||
starDrawable = Util.getDrawableFromAttribute(context, R.attr.star_full)
|
||||
pinImage = Util.getDrawableFromAttribute(context, R.attr.pin)
|
||||
downloadedImage = Util.getDrawableFromAttribute(context, R.attr.downloaded)
|
||||
errorImage = Util.getDrawableFromAttribute(context, R.attr.error)
|
||||
downloadingImage = Util.getDrawableFromAttribute(context, R.attr.downloading)
|
||||
playingImage = Util.getDrawableFromAttribute(context, R.attr.media_play_small)
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ package org.moire.ultrasonic.adapters
|
||||
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.MenuItem
|
||||
import android.view.ViewGroup
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import com.drakeet.multitype.ItemViewBinder
|
||||
@ -15,24 +15,26 @@ import org.moire.ultrasonic.service.DownloadFile
|
||||
import org.moire.ultrasonic.service.Downloader
|
||||
|
||||
class TrackViewBinder(
|
||||
val onItemClick: (DownloadFile) -> Unit,
|
||||
val onContextMenuClick: ((MenuItem, DownloadFile) -> Boolean)? = null,
|
||||
val checkable: Boolean,
|
||||
val draggable: Boolean,
|
||||
context: Context,
|
||||
val lifecycleOwner: LifecycleOwner,
|
||||
private val onClickCallback: ((View, DownloadFile?) -> Unit)? = null
|
||||
) : ItemViewBinder<Identifiable, TrackViewHolder>(), KoinComponent {
|
||||
|
||||
// Set our layout files
|
||||
val layout = R.layout.song_list_item
|
||||
val contextMenuLayout = R.menu.artist_context_menu
|
||||
val layout = R.layout.list_item_track
|
||||
val contextMenuLayout = R.menu.context_menu_track
|
||||
|
||||
private val downloader: Downloader by inject()
|
||||
private val imageHelper: ImageHelper = ImageHelper(context)
|
||||
private val imageHelper: Utils.ImageHelper = Utils.ImageHelper(context)
|
||||
|
||||
override fun onCreateViewHolder(inflater: LayoutInflater, parent: ViewGroup): TrackViewHolder {
|
||||
return TrackViewHolder(inflater.inflate(layout, parent, false))
|
||||
}
|
||||
|
||||
@Suppress("LongMethod")
|
||||
override fun onBindViewHolder(holder: TrackViewHolder, item: Identifiable) {
|
||||
val downloadFile: DownloadFile?
|
||||
val diffAdapter = adapter as BaseAdapter<*>
|
||||
@ -58,6 +60,32 @@ class TrackViewBinder(
|
||||
diffAdapter.isSelected(item.longId)
|
||||
)
|
||||
|
||||
holder.itemView.setOnLongClickListener {
|
||||
if (onContextMenuClick != null) {
|
||||
val popup = Utils.createPopupMenu(holder.itemView, contextMenuLayout)
|
||||
|
||||
popup.setOnMenuItemClickListener { menuItem ->
|
||||
onContextMenuClick?.invoke(menuItem, downloadFile)
|
||||
}
|
||||
} else {
|
||||
// Minimize or maximize the Text view (if song title is very long)
|
||||
if (!downloadFile.song.isDirectory) {
|
||||
holder.maximizeOrMinimize()
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
holder.itemView.setOnClickListener {
|
||||
if (!checkable) {
|
||||
onItemClick(downloadFile)
|
||||
} else {
|
||||
val nowChecked = !holder.check.isChecked
|
||||
holder.isChecked = nowChecked
|
||||
}
|
||||
}
|
||||
|
||||
// Notify the adapter of selection changes
|
||||
holder.observableChecked.observe(
|
||||
lifecycleOwner,
|
||||
@ -95,7 +123,5 @@ class TrackViewBinder(
|
||||
holder.updateProgress(it)
|
||||
}
|
||||
)
|
||||
|
||||
holder.itemClickListener = onClickCallback
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import android.widget.Checkable
|
||||
import android.widget.CheckedTextView
|
||||
import android.widget.ImageView
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.RelativeLayout
|
||||
import android.widget.TextView
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
@ -29,6 +30,7 @@ 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 {
|
||||
|
||||
@ -47,8 +49,6 @@ class TrackViewHolder(val view: View) : RecyclerView.ViewHolder(view), Checkable
|
||||
var duration: TextView = view.findViewById(R.id.song_duration)
|
||||
var progress: TextView = view.findViewById(R.id.song_status)
|
||||
|
||||
var itemClickListener: ((View, DownloadFile?) -> Unit)? = null
|
||||
|
||||
var entry: MusicDirectory.Entry? = null
|
||||
private set
|
||||
var downloadFile: DownloadFile? = null
|
||||
@ -66,18 +66,7 @@ class TrackViewHolder(val view: View) : RecyclerView.ViewHolder(view), Checkable
|
||||
features.isFeatureEnabled(Feature.FIVE_STAR_RATING)
|
||||
}
|
||||
|
||||
lateinit var imageHelper: ImageHelper
|
||||
|
||||
init {
|
||||
itemView.setOnClickListener {
|
||||
if (itemClickListener != null) {
|
||||
itemClickListener?.invoke(it, downloadFile)
|
||||
} else {
|
||||
val nowChecked = !check.isChecked
|
||||
isChecked = nowChecked
|
||||
}
|
||||
}
|
||||
}
|
||||
lateinit var imageHelper: Utils.ImageHelper
|
||||
|
||||
fun setSong(
|
||||
file: DownloadFile,
|
||||
@ -85,7 +74,6 @@ class TrackViewHolder(val view: View) : RecyclerView.ViewHolder(view), Checkable
|
||||
draggable: Boolean,
|
||||
isSelected: Boolean = false
|
||||
) {
|
||||
Timber.e("BINDING %s", isSelected)
|
||||
val song = file.song
|
||||
downloadFile = file
|
||||
entry = song
|
||||
@ -125,15 +113,6 @@ class TrackViewHolder(val view: View) : RecyclerView.ViewHolder(view), Checkable
|
||||
RxBus.playerStateObservable.subscribe {
|
||||
setPlayIcon(it.track == downloadFile)
|
||||
}
|
||||
|
||||
// Minimize or maximize the Text view (if song title is very long)
|
||||
itemView.setOnLongClickListener {
|
||||
if (!song.isDirectory) {
|
||||
maximizeOrMinimize()
|
||||
true
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
private fun setPlayIcon(isPlaying: Boolean) {
|
||||
|
@ -0,0 +1,72 @@
|
||||
package org.moire.ultrasonic.adapters
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.view.MenuInflater
|
||||
import android.view.View
|
||||
import android.widget.PopupMenu
|
||||
import org.moire.ultrasonic.R
|
||||
import org.moire.ultrasonic.data.ActiveServerProvider
|
||||
import org.moire.ultrasonic.util.Settings
|
||||
import org.moire.ultrasonic.util.Util
|
||||
|
||||
object Utils {
|
||||
@JvmStatic
|
||||
fun createPopupMenu(view: View, layout: Int = R.menu.context_menu_artist): PopupMenu {
|
||||
val popup = PopupMenu(view.context, view)
|
||||
val inflater: MenuInflater = popup.menuInflater
|
||||
inflater.inflate(layout, popup.menu)
|
||||
|
||||
val downloadMenuItem = popup.menu.findItem(R.id.menu_download)
|
||||
downloadMenuItem?.isVisible = !ActiveServerProvider.isOffline()
|
||||
|
||||
var shareButton = popup.menu.findItem(R.id.menu_item_share)
|
||||
shareButton?.isVisible = !ActiveServerProvider.isOffline()
|
||||
|
||||
shareButton = popup.menu.findItem(R.id.song_menu_share)
|
||||
shareButton?.isVisible = !ActiveServerProvider.isOffline()
|
||||
|
||||
popup.show()
|
||||
return popup
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides cached drawables for the UI
|
||||
*/
|
||||
class ImageHelper(context: Context) {
|
||||
|
||||
lateinit var errorImage: Drawable
|
||||
lateinit var starHollowDrawable: Drawable
|
||||
lateinit var starDrawable: Drawable
|
||||
lateinit var pinImage: Drawable
|
||||
lateinit var downloadedImage: Drawable
|
||||
lateinit var downloadingImage: Drawable
|
||||
lateinit var playingImage: Drawable
|
||||
var theme: String
|
||||
|
||||
fun rebuild(context: Context, force: Boolean = false) {
|
||||
val currentTheme = Settings.theme
|
||||
val themesMatch = theme == currentTheme
|
||||
if (!themesMatch) theme = currentTheme
|
||||
|
||||
if (!themesMatch || force) {
|
||||
getDrawables(context)
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
theme = Settings.theme
|
||||
getDrawables(context)
|
||||
}
|
||||
|
||||
private fun getDrawables(context: Context) {
|
||||
starHollowDrawable = Util.getDrawableFromAttribute(context, R.attr.star_hollow)
|
||||
starDrawable = Util.getDrawableFromAttribute(context, R.attr.star_full)
|
||||
pinImage = Util.getDrawableFromAttribute(context, R.attr.pin)
|
||||
downloadedImage = Util.getDrawableFromAttribute(context, R.attr.downloaded)
|
||||
errorImage = Util.getDrawableFromAttribute(context, R.attr.error)
|
||||
downloadingImage = Util.getDrawableFromAttribute(context, R.attr.downloading)
|
||||
playingImage = Util.getDrawableFromAttribute(context, R.attr.media_play_small)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,3 +1,10 @@
|
||||
/*
|
||||
* AlbumListFragment.kt
|
||||
* Copyright (C) 2009-2021 Ultrasonic developers
|
||||
*
|
||||
* Distributed under terms of the GNU GPLv3 license.
|
||||
*/
|
||||
|
||||
package org.moire.ultrasonic.fragment
|
||||
|
||||
import android.os.Bundle
|
||||
@ -26,13 +33,7 @@ class AlbumListFragment : EntryListFragment<MusicDirectory.Album>() {
|
||||
/**
|
||||
* The id of the main layout
|
||||
*/
|
||||
override val mainLayout: Int = R.layout.generic_list
|
||||
|
||||
/**
|
||||
* The id of the target in the navigation graph where we should go,
|
||||
* after the user has clicked on an item
|
||||
*/
|
||||
override val itemClickTarget: Int = R.id.trackCollectionFragment
|
||||
override val mainLayout: Int = R.layout.list_layout_generic
|
||||
|
||||
/**
|
||||
* The central function to pass a query to the model and return a LiveData object
|
||||
@ -71,6 +72,8 @@ class AlbumListFragment : EntryListFragment<MusicDirectory.Album>() {
|
||||
context = requireContext()
|
||||
)
|
||||
)
|
||||
|
||||
emptyTextView.setText(R.string.select_album_empty)
|
||||
}
|
||||
|
||||
override fun onItemClick(item: MusicDirectory.Album) {
|
||||
@ -79,6 +82,6 @@ class AlbumListFragment : EntryListFragment<MusicDirectory.Album>() {
|
||||
bundle.putBoolean(Constants.INTENT_EXTRA_NAME_IS_ALBUM, item.isDirectory)
|
||||
bundle.putString(Constants.INTENT_EXTRA_NAME_NAME, item.title)
|
||||
bundle.putString(Constants.INTENT_EXTRA_NAME_PARENT_ID, item.parent)
|
||||
findNavController().navigate(itemClickTarget, bundle)
|
||||
findNavController().navigate(R.id.trackCollectionFragment, bundle)
|
||||
}
|
||||
}
|
||||
|
@ -25,13 +25,7 @@ class ArtistListFragment : EntryListFragment<ArtistOrIndex>() {
|
||||
/**
|
||||
* The id of the main layout
|
||||
*/
|
||||
override val mainLayout = R.layout.generic_list
|
||||
|
||||
/**
|
||||
* The id of the target in the navigation graph where we should go,
|
||||
* after the user has clicked on an item
|
||||
*/
|
||||
override val itemClickTarget = R.id.selectArtistToSelectAlbum
|
||||
override val mainLayout = R.layout.list_layout_generic
|
||||
|
||||
/**
|
||||
* The central function to pass a query to the model and return a LiveData object
|
||||
@ -63,6 +57,6 @@ class ArtistListFragment : EntryListFragment<ArtistOrIndex>() {
|
||||
bundle.putString(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TITLE, item.name)
|
||||
bundle.putInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_SIZE, 1000)
|
||||
bundle.putInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_OFFSET, 0)
|
||||
findNavController().navigate(itemClickTarget, bundle)
|
||||
findNavController().navigate(R.id.selectArtistToSelectAlbum, bundle)
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,6 @@ import org.moire.ultrasonic.fragment.FragmentTitle.Companion.setTitle
|
||||
*
|
||||
* Therefore this fragment allows only for singular selection and playback.
|
||||
*
|
||||
* FIXME: use restore for playback
|
||||
*/
|
||||
class BookmarksFragment : TrackCollectionFragment() {
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
|
@ -17,6 +17,7 @@ import androidx.lifecycle.LiveData
|
||||
import org.koin.core.component.inject
|
||||
import org.moire.ultrasonic.R
|
||||
import org.moire.ultrasonic.adapters.TrackViewBinder
|
||||
import org.moire.ultrasonic.domain.Identifiable
|
||||
import org.moire.ultrasonic.model.GenericListModel
|
||||
import org.moire.ultrasonic.service.DownloadFile
|
||||
import org.moire.ultrasonic.service.Downloader
|
||||
@ -26,8 +27,10 @@ import org.moire.ultrasonic.util.Util
|
||||
* Displays currently running downloads.
|
||||
* For now its a read-only view, there are no manipulations of the download list possible.
|
||||
*
|
||||
* A consideration would be to base this class on TrackCollectionFragment and thereby inheriting the
|
||||
* buttons useful to manipulate the list.
|
||||
* TODO: A consideration would be to base this class on TrackCollectionFragment and thereby inheriting the
|
||||
* buttons useful to manipulate the list.
|
||||
*
|
||||
* TODO: Add code to enable manipulation of the download list
|
||||
*/
|
||||
class DownloadsFragment : MultiListFragment<DownloadFile>() {
|
||||
|
||||
@ -36,13 +39,6 @@ class DownloadsFragment : MultiListFragment<DownloadFile>() {
|
||||
*/
|
||||
override val listModel: DownloadListModel by viewModels()
|
||||
|
||||
/**
|
||||
* The id of the target in the navigation graph where we should go,
|
||||
* after the user has clicked on an item
|
||||
*/
|
||||
// FIXME
|
||||
override val itemClickTarget: Int = R.id.trackCollectionFragment
|
||||
|
||||
/**
|
||||
* The central function to pass a query to the model and return a LiveData object
|
||||
*/
|
||||
@ -50,15 +46,6 @@ class DownloadsFragment : MultiListFragment<DownloadFile>() {
|
||||
return listModel.getList()
|
||||
}
|
||||
|
||||
override fun onContextMenuItemSelected(menuItem: MenuItem, item: DownloadFile): Boolean {
|
||||
// TODO: Add code to enable manipulation of the download list
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onItemClick(item: DownloadFile) {
|
||||
// TODO: Add code to enable manipulation of the download list
|
||||
}
|
||||
|
||||
override fun setTitle(title: String?) {
|
||||
FragmentTitle.setTitle(this, Util.appContext().getString(R.string.menu_downloads))
|
||||
}
|
||||
@ -68,6 +55,8 @@ class DownloadsFragment : MultiListFragment<DownloadFile>() {
|
||||
|
||||
viewAdapter.register(
|
||||
TrackViewBinder(
|
||||
{ },
|
||||
{ _,_ -> true },
|
||||
checkable = false,
|
||||
draggable = false,
|
||||
context = requireContext(),
|
||||
@ -82,6 +71,15 @@ class DownloadsFragment : MultiListFragment<DownloadFile>() {
|
||||
|
||||
viewAdapter.submitList(liveDataList.value)
|
||||
}
|
||||
|
||||
override fun onContextMenuItemSelected(menuItem: MenuItem, item: DownloadFile): Boolean {
|
||||
// TODO: Add code to enable manipulation of the download list
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onItemClick(item: DownloadFile) {
|
||||
// TODO: Add code to enable manipulation of the download list
|
||||
}
|
||||
}
|
||||
|
||||
class DownloadListModel(application: Application) : GenericListModel(application) {
|
||||
|
@ -3,6 +3,7 @@ package org.moire.ultrasonic.fragment
|
||||
import android.os.Bundle
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import org.moire.ultrasonic.R
|
||||
import org.moire.ultrasonic.domain.Artist
|
||||
@ -11,7 +12,6 @@ import org.moire.ultrasonic.domain.Identifiable
|
||||
import org.moire.ultrasonic.service.RxBus
|
||||
import org.moire.ultrasonic.subsonic.DownloadHandler
|
||||
import org.moire.ultrasonic.util.Constants
|
||||
import androidx.fragment.app.Fragment
|
||||
import org.moire.ultrasonic.util.Settings
|
||||
|
||||
/**
|
||||
@ -30,7 +30,6 @@ abstract class EntryListFragment<T : GenericEntry> : MultiListFragment<T>() {
|
||||
!listModel.isOffline() && !Settings.shouldUseId3Tags
|
||||
}
|
||||
|
||||
|
||||
override fun onContextMenuItemSelected(menuItem: MenuItem, item: T): Boolean {
|
||||
val isArtist = (item is Artist)
|
||||
|
||||
@ -43,7 +42,7 @@ abstract class EntryListFragment<T : GenericEntry> : MultiListFragment<T>() {
|
||||
bundle.putString(Constants.INTENT_EXTRA_NAME_NAME, item.name)
|
||||
bundle.putString(Constants.INTENT_EXTRA_NAME_PARENT_ID, item.id)
|
||||
bundle.putBoolean(Constants.INTENT_EXTRA_NAME_ARTIST, (item is Artist))
|
||||
findNavController().navigate(itemClickTarget, bundle)
|
||||
findNavController().navigate(R.id.trackCollectionFragment, bundle)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
@ -149,6 +148,7 @@ abstract class EntryListFragment<T : GenericEntry> : MultiListFragment<T>() {
|
||||
unpin = false,
|
||||
isArtist = isArtist
|
||||
)
|
||||
else -> return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
@ -76,16 +76,10 @@ abstract class MultiListFragment<T : Identifiable> : Fragment() {
|
||||
return MutableLiveData()
|
||||
}
|
||||
|
||||
/**
|
||||
* The id of the target in the navigation graph where we should go,
|
||||
* after the user has clicked on an item
|
||||
*/
|
||||
protected abstract val itemClickTarget: Int
|
||||
|
||||
/**
|
||||
* The id of the main layout
|
||||
*/
|
||||
open val mainLayout: Int = R.layout.generic_list
|
||||
open val mainLayout: Int = R.layout.list_layout_generic
|
||||
|
||||
/**
|
||||
* The ids of the swipe refresh view, the recycler view and the empty text view
|
||||
@ -95,7 +89,6 @@ abstract class MultiListFragment<T : Identifiable> : Fragment() {
|
||||
open val emptyViewId = R.id.empty_list_view
|
||||
open val emptyTextId = R.id.empty_list_text
|
||||
|
||||
|
||||
open fun setTitle(title: String?) {
|
||||
if (title == null) {
|
||||
FragmentTitle.setTitle(
|
||||
|
@ -229,7 +229,7 @@ class PlayerFragment :
|
||||
val ratingLinearLayout = view.findViewById<LinearLayout>(R.id.song_rating)
|
||||
if (!useFiveStarRating) ratingLinearLayout.isVisible = false
|
||||
hollowStar = Util.getDrawableFromAttribute(view.context, R.attr.star_hollow)
|
||||
fullStar = Util.getDrawableFromAttribute(context!!, R.attr.star_full)
|
||||
fullStar = Util.getDrawableFromAttribute(view.context, R.attr.star_full)
|
||||
|
||||
fiveStar1ImageView.setOnClickListener { setSongRating(1) }
|
||||
fiveStar2ImageView.setOnClickListener { setSongRating(2) }
|
||||
@ -561,14 +561,6 @@ class PlayerFragment :
|
||||
}
|
||||
}
|
||||
|
||||
override fun onContextItemSelected(menuItem: MenuItem): Boolean {
|
||||
val info = menuItem.menuInfo as AdapterContextMenuInfo
|
||||
val downloadFile = viewAdapter.getCurrentList()[info.position] as DownloadFile
|
||||
return menuItemSelected(menuItem.itemId, downloadFile) || super.onContextItemSelected(
|
||||
menuItem
|
||||
)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
return menuItemSelected(item.itemId, null) || super.onOptionsItemSelected(item)
|
||||
}
|
||||
@ -856,7 +848,7 @@ class PlayerFragment :
|
||||
}
|
||||
|
||||
// Create listener
|
||||
val listener: ((View, DownloadFile?) -> Unit) = { _, file ->
|
||||
val listener: ((DownloadFile) -> Unit) = { file ->
|
||||
val list = mediaPlayerController.playList
|
||||
val index = list.indexOf(file)
|
||||
mediaPlayerController.play(index)
|
||||
@ -866,11 +858,12 @@ class PlayerFragment :
|
||||
|
||||
viewAdapter.register(
|
||||
TrackViewBinder(
|
||||
onItemClick = listener,
|
||||
onContextMenuClick = {_,_ -> true},
|
||||
checkable = false,
|
||||
draggable = true,
|
||||
context = requireContext(),
|
||||
lifecycleOwner = viewLifecycleOwner,
|
||||
listener
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -3,20 +3,15 @@ package org.moire.ultrasonic.fragment
|
||||
import android.app.SearchManager
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.view.ContextMenu
|
||||
import android.view.ContextMenu.ContextMenuInfo
|
||||
import android.view.LayoutInflater
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.widget.AdapterView.AdapterContextMenuInfo
|
||||
import android.widget.ListAdapter
|
||||
import androidx.appcompat.widget.SearchView
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.navigation.Navigation
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
@ -34,6 +29,7 @@ import org.moire.ultrasonic.domain.MusicDirectory
|
||||
import org.moire.ultrasonic.domain.SearchResult
|
||||
import org.moire.ultrasonic.fragment.FragmentTitle.Companion.setTitle
|
||||
import org.moire.ultrasonic.model.SearchListModel
|
||||
import org.moire.ultrasonic.service.DownloadFile
|
||||
import org.moire.ultrasonic.service.MediaPlayerController
|
||||
import org.moire.ultrasonic.subsonic.NetworkAndStorageChecker
|
||||
import org.moire.ultrasonic.subsonic.ShareHandler
|
||||
@ -48,6 +44,8 @@ import timber.log.Timber
|
||||
|
||||
/**
|
||||
* Initiates a search on the media library and displays the results
|
||||
*
|
||||
* FIXME: Handle context click on song
|
||||
*/
|
||||
class SearchFragment : MultiListFragment<Identifiable>(), KoinComponent {
|
||||
private var moreArtistsButton: View? = null
|
||||
@ -107,18 +105,7 @@ class SearchFragment : MultiListFragment<Identifiable>(), KoinComponent {
|
||||
// expandSongs()
|
||||
// } else {
|
||||
// val item = parent.getItemAtPosition(position)
|
||||
// if (item is Artist) {
|
||||
// onArtistSelected(item)
|
||||
// } else if (item is MusicDirectory.Entry) {
|
||||
// val entry = item
|
||||
// if (entry.isDirectory) {
|
||||
// onAlbumSelected(entry, false)
|
||||
// } else if (entry.isVideo) {
|
||||
// onVideoSelected(entry)
|
||||
// } else {
|
||||
// onSongSelected(entry, true)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// }
|
||||
// })
|
||||
|
||||
@ -129,13 +116,8 @@ class SearchFragment : MultiListFragment<Identifiable>(), KoinComponent {
|
||||
// They need to be added in the order of most specific -> least specific.
|
||||
viewAdapter.register(
|
||||
ArtistRowBinder(
|
||||
onItemClick = { entry -> onItemClick(entry) },
|
||||
onContextMenuClick = { menuItem, entry ->
|
||||
onContextMenuItemSelected(
|
||||
menuItem,
|
||||
entry
|
||||
)
|
||||
},
|
||||
onItemClick = ::onItemClick,
|
||||
onContextMenuClick = ::onContextMenuItemSelected,
|
||||
imageLoader = imageLoaderProvider.getImageLoader(),
|
||||
enableSections = false
|
||||
)
|
||||
@ -143,13 +125,8 @@ class SearchFragment : MultiListFragment<Identifiable>(), KoinComponent {
|
||||
|
||||
viewAdapter.register(
|
||||
AlbumRowBinder(
|
||||
onItemClick = { entry -> onItemClick(entry) },
|
||||
onContextMenuClick = { menuItem, entry ->
|
||||
onContextMenuItemSelected(
|
||||
menuItem,
|
||||
entry
|
||||
)
|
||||
},
|
||||
onItemClick = ::onItemClick,
|
||||
onContextMenuClick = ::onContextMenuItemSelected,
|
||||
imageLoader = imageLoaderProvider.getImageLoader(),
|
||||
context = requireContext()
|
||||
)
|
||||
@ -157,6 +134,8 @@ class SearchFragment : MultiListFragment<Identifiable>(), KoinComponent {
|
||||
|
||||
viewAdapter.register(
|
||||
TrackViewBinder(
|
||||
onItemClick = ::onItemClick,
|
||||
onContextMenuClick = ::onContextMenuItemSelected,
|
||||
checkable = false,
|
||||
draggable = false,
|
||||
context = requireContext(),
|
||||
@ -193,7 +172,7 @@ class SearchFragment : MultiListFragment<Identifiable>(), KoinComponent {
|
||||
|
||||
val arguments = arguments
|
||||
val autoPlay = arguments != null &&
|
||||
arguments.getBoolean(Constants.INTENT_EXTRA_NAME_AUTOPLAY, false)
|
||||
arguments.getBoolean(Constants.INTENT_EXTRA_NAME_AUTOPLAY, false)
|
||||
val query = arguments?.getString(Constants.INTENT_EXTRA_NAME_QUERY)
|
||||
|
||||
// If started with a query, enter it to the searchView
|
||||
@ -236,200 +215,11 @@ class SearchFragment : MultiListFragment<Identifiable>(), KoinComponent {
|
||||
searchItem.expandActionView()
|
||||
}
|
||||
|
||||
// FIXME
|
||||
override fun onCreateContextMenu(menu: ContextMenu, view: View, menuInfo: ContextMenuInfo?) {
|
||||
super.onCreateContextMenu(menu, view, menuInfo)
|
||||
if (activity == null) return
|
||||
val info = menuInfo as AdapterContextMenuInfo?
|
||||
// val selectedItem = list!!.getItemAtPosition(info!!.position)
|
||||
// val isArtist = selectedItem is Artist
|
||||
// val isAlbum = selectedItem is MusicDirectory.Entry && selectedItem.isDirectory
|
||||
// val inflater = requireActivity().menuInflater
|
||||
// if (!isArtist && !isAlbum) {
|
||||
// inflater.inflate(R.menu.select_song_context, menu)
|
||||
// } else {
|
||||
// inflater.inflate(R.menu.generic_context_menu, menu)
|
||||
// }
|
||||
// val shareButton = menu.findItem(R.id.menu_item_share)
|
||||
// val downloadMenuItem = menu.findItem(R.id.menu_download)
|
||||
// if (downloadMenuItem != null) {
|
||||
// downloadMenuItem.isVisible = !isOffline()
|
||||
// }
|
||||
// if (isOffline() || isArtist) {
|
||||
// if (shareButton != null) {
|
||||
// shareButton.isVisible = false
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
// FIXME
|
||||
override fun onContextItemSelected(menuItem: MenuItem): Boolean {
|
||||
val info = menuItem.menuInfo as AdapterContextMenuInfo
|
||||
// val selectedItem = list!!.getItemAtPosition(info.position)
|
||||
// val artist = if (selectedItem is Artist) selectedItem else null
|
||||
// val entry = if (selectedItem is MusicDirectory.Entry) selectedItem else null
|
||||
// var entryId: String? = null
|
||||
// if (entry != null) {
|
||||
// entryId = entry.id
|
||||
// }
|
||||
// val id = artist?.id ?: entryId ?: return true
|
||||
// var songs: MutableList<MusicDirectory.Entry?> = ArrayList(1)
|
||||
// val itemId = menuItem.itemId
|
||||
// if (itemId == R.id.menu_play_now) {
|
||||
// downloadHandler.downloadRecursively(
|
||||
// this,
|
||||
// id,
|
||||
// false,
|
||||
// false,
|
||||
// true,
|
||||
// false,
|
||||
// false,
|
||||
// false,
|
||||
// false,
|
||||
// false
|
||||
// )
|
||||
// } else if (itemId == R.id.menu_play_next) {
|
||||
// downloadHandler.downloadRecursively(
|
||||
// this,
|
||||
// id,
|
||||
// false,
|
||||
// true,
|
||||
// false,
|
||||
// true,
|
||||
// false,
|
||||
// true,
|
||||
// false,
|
||||
// false
|
||||
// )
|
||||
// } else if (itemId == R.id.menu_play_last) {
|
||||
// downloadHandler.downloadRecursively(
|
||||
// this,
|
||||
// id,
|
||||
// false,
|
||||
// true,
|
||||
// false,
|
||||
// false,
|
||||
// false,
|
||||
// false,
|
||||
// false,
|
||||
// false
|
||||
// )
|
||||
// } else if (itemId == R.id.menu_pin) {
|
||||
// downloadHandler.downloadRecursively(
|
||||
// this,
|
||||
// id,
|
||||
// true,
|
||||
// true,
|
||||
// false,
|
||||
// false,
|
||||
// false,
|
||||
// false,
|
||||
// false,
|
||||
// false
|
||||
// )
|
||||
// } else if (itemId == R.id.menu_unpin) {
|
||||
// downloadHandler.downloadRecursively(
|
||||
// this,
|
||||
// id,
|
||||
// false,
|
||||
// false,
|
||||
// false,
|
||||
// false,
|
||||
// false,
|
||||
// false,
|
||||
// true,
|
||||
// false
|
||||
// )
|
||||
// } else if (itemId == R.id.menu_download) {
|
||||
// downloadHandler.downloadRecursively(
|
||||
// this,
|
||||
// id,
|
||||
// false,
|
||||
// false,
|
||||
// false,
|
||||
// false,
|
||||
// true,
|
||||
// false,
|
||||
// false,
|
||||
// false
|
||||
// )
|
||||
// } else if (itemId == R.id.song_menu_play_now) {
|
||||
// if (entry != null) {
|
||||
// songs = ArrayList(1)
|
||||
// songs.add(entry)
|
||||
// downloadHandler.download(this, false, false, true, false, false, songs)
|
||||
// }
|
||||
// } else if (itemId == R.id.song_menu_play_next) {
|
||||
// if (entry != null) {
|
||||
// songs = ArrayList(1)
|
||||
// songs.add(entry)
|
||||
// downloadHandler.download(this, true, false, false, true, false, songs)
|
||||
// }
|
||||
// } else if (itemId == R.id.song_menu_play_last) {
|
||||
// if (entry != null) {
|
||||
// songs = ArrayList(1)
|
||||
// songs.add(entry)
|
||||
// downloadHandler.download(this, true, false, false, false, false, songs)
|
||||
// }
|
||||
// } else if (itemId == R.id.song_menu_pin) {
|
||||
// if (entry != null) {
|
||||
// songs.add(entry)
|
||||
// toast(
|
||||
// context,
|
||||
// resources.getQuantityString(
|
||||
// R.plurals.select_album_n_songs_pinned,
|
||||
// songs.size,
|
||||
// songs.size
|
||||
// )
|
||||
// )
|
||||
// downloadBackground(true, songs)
|
||||
// }
|
||||
// } else if (itemId == R.id.song_menu_download) {
|
||||
// if (entry != null) {
|
||||
// songs.add(entry)
|
||||
// toast(
|
||||
// context,
|
||||
// resources.getQuantityString(
|
||||
// R.plurals.select_album_n_songs_downloaded,
|
||||
// songs.size,
|
||||
// songs.size
|
||||
// )
|
||||
// )
|
||||
// downloadBackground(false, songs)
|
||||
// }
|
||||
// } else if (itemId == R.id.song_menu_unpin) {
|
||||
// if (entry != null) {
|
||||
// songs.add(entry)
|
||||
// toast(
|
||||
// context,
|
||||
// resources.getQuantityString(
|
||||
// R.plurals.select_album_n_songs_unpinned,
|
||||
// songs.size,
|
||||
// songs.size
|
||||
// )
|
||||
// )
|
||||
// mediaPlayerController.unpin(songs)
|
||||
// }
|
||||
// } else if (itemId == R.id.menu_item_share) {
|
||||
// if (entry != null) {
|
||||
// songs = ArrayList(1)
|
||||
// songs.add(entry)
|
||||
// shareHandler.createShare(this, songs, searchRefresh, cancellationToken!!)
|
||||
// }
|
||||
// return super.onContextItemSelected(menuItem)
|
||||
// } else {
|
||||
// return super.onContextItemSelected(menuItem)
|
||||
// }
|
||||
return true
|
||||
}
|
||||
|
||||
// OK!
|
||||
override fun onDestroyView() {
|
||||
cancellationToken?.cancel()
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
// OK!
|
||||
private fun downloadBackground(save: Boolean, songs: List<MusicDirectory.Entry?>) {
|
||||
val onValid = Runnable {
|
||||
networkAndStorageChecker.warnIfNetworkOrStorageUnavailable()
|
||||
@ -515,13 +305,13 @@ class SearchFragment : MultiListFragment<Identifiable>(), KoinComponent {
|
||||
// mergeAdapter!!.removeAdapter(moreSongsAdapter)
|
||||
// mergeAdapter!!.notifyDataSetChanged()
|
||||
// }
|
||||
//
|
||||
// private fun onArtistSelected(artist: Artist) {
|
||||
// val bundle = Bundle()
|
||||
// bundle.putString(Constants.INTENT_EXTRA_NAME_ID, artist.id)
|
||||
// bundle.putString(Constants.INTENT_EXTRA_NAME_NAME, artist.id)
|
||||
// Navigation.findNavController(requireView()).navigate(R.id.searchToSelectAlbum, bundle)
|
||||
// }
|
||||
|
||||
private fun onArtistSelected(artist: Artist) {
|
||||
val bundle = Bundle()
|
||||
bundle.putString(Constants.INTENT_EXTRA_NAME_ID, artist.id)
|
||||
bundle.putString(Constants.INTENT_EXTRA_NAME_NAME, artist.id)
|
||||
Navigation.findNavController(requireView()).navigate(R.id.searchToSelectAlbum, bundle)
|
||||
}
|
||||
|
||||
private fun onAlbumSelected(album: MusicDirectory.Album, autoplay: Boolean) {
|
||||
val bundle = Bundle()
|
||||
@ -559,15 +349,112 @@ class SearchFragment : MultiListFragment<Identifiable>(), KoinComponent {
|
||||
var DEFAULT_SONGS = Settings.defaultSongs
|
||||
}
|
||||
|
||||
// FIXME
|
||||
override val itemClickTarget: Int = 0
|
||||
|
||||
// FIXME
|
||||
override fun onItemClick(item: Identifiable) {
|
||||
when (item) {
|
||||
is Artist -> {
|
||||
onArtistSelected(item)
|
||||
}
|
||||
is MusicDirectory.Entry -> {
|
||||
if (item.isVideo) {
|
||||
onVideoSelected(item)
|
||||
} else {
|
||||
onSongSelected(item, true)
|
||||
}
|
||||
}
|
||||
is MusicDirectory.Album -> {
|
||||
onAlbumSelected(item, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("LongMethod")
|
||||
override fun onContextMenuItemSelected(menuItem: MenuItem, item: Identifiable): Boolean {
|
||||
val isArtist = (item is Artist)
|
||||
return EntryListFragment.handleContextMenu(menuItem, item, isArtist, downloadHandler, this)
|
||||
val found = EntryListFragment.handleContextMenu(menuItem, item, isArtist, downloadHandler, this)
|
||||
|
||||
if (found || item !is DownloadFile) return true
|
||||
|
||||
val songs = mutableListOf<MusicDirectory.Entry>()
|
||||
|
||||
when (menuItem.itemId) {
|
||||
R.id.song_menu_play_now -> {
|
||||
songs.add(item.song)
|
||||
downloadHandler.download(
|
||||
fragment = this,
|
||||
append = false,
|
||||
save = false,
|
||||
autoPlay = true,
|
||||
playNext = false,
|
||||
shuffle = false,
|
||||
songs = songs
|
||||
)
|
||||
}
|
||||
R.id.song_menu_play_next -> {
|
||||
songs.add(item.song)
|
||||
downloadHandler.download(
|
||||
fragment = this,
|
||||
append = true,
|
||||
save = false,
|
||||
autoPlay = false,
|
||||
playNext = true,
|
||||
shuffle = false,
|
||||
songs = songs
|
||||
)
|
||||
}
|
||||
R.id.song_menu_play_last -> {
|
||||
songs.add(item.song)
|
||||
downloadHandler.download(
|
||||
fragment = this,
|
||||
append = true,
|
||||
save = false,
|
||||
autoPlay = false,
|
||||
playNext = false,
|
||||
shuffle = false,
|
||||
songs = songs
|
||||
)
|
||||
}
|
||||
R.id.song_menu_pin -> {
|
||||
songs.add(item.song)
|
||||
toast(
|
||||
context,
|
||||
resources.getQuantityString(
|
||||
R.plurals.select_album_n_songs_pinned,
|
||||
songs.size,
|
||||
songs.size
|
||||
)
|
||||
)
|
||||
downloadBackground(true, songs)
|
||||
}
|
||||
R.id.song_menu_download -> {
|
||||
songs.add(item.song)
|
||||
toast(
|
||||
context,
|
||||
resources.getQuantityString(
|
||||
R.plurals.select_album_n_songs_downloaded,
|
||||
songs.size,
|
||||
songs.size
|
||||
)
|
||||
)
|
||||
downloadBackground(false, songs)
|
||||
}
|
||||
R.id.song_menu_unpin -> {
|
||||
songs.add(item.song)
|
||||
toast(
|
||||
context,
|
||||
resources.getQuantityString(
|
||||
R.plurals.select_album_n_songs_unpinned,
|
||||
songs.size,
|
||||
songs.size
|
||||
)
|
||||
)
|
||||
mediaPlayerController.unpin(songs)
|
||||
}
|
||||
R.id.song_menu_share -> {
|
||||
songs.add(item.song)
|
||||
shareHandler.createShare(this, songs, searchRefresh, cancellationToken!!)
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -14,9 +14,7 @@ import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.widget.AdapterView.AdapterContextMenuInfo
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.LiveData
|
||||
@ -81,13 +79,7 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Entry>() {
|
||||
/**
|
||||
* The id of the main layout
|
||||
*/
|
||||
override val mainLayout: Int = R.layout.track_list
|
||||
|
||||
/**
|
||||
* The id of the target in the navigation graph where we should go,
|
||||
* after the user has clicked on an item
|
||||
*/
|
||||
override val itemClickTarget: Int = R.id.trackCollectionFragment
|
||||
override val mainLayout: Int = R.layout.list_layout_track
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
@ -127,6 +119,8 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Entry>() {
|
||||
|
||||
viewAdapter.register(
|
||||
TrackViewBinder(
|
||||
onItemClick = { onItemClick(it.song) },
|
||||
onContextMenuClick = { menu, id -> onContextMenuItemSelected(menu, id.song) },
|
||||
checkable = true,
|
||||
draggable = false,
|
||||
context = requireContext(),
|
||||
@ -209,78 +203,6 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Entry>() {
|
||||
getLiveData(args)
|
||||
}
|
||||
|
||||
override fun onContextItemSelected(menuItem: MenuItem): Boolean {
|
||||
Timber.d("onContextItemSelected")
|
||||
val info = menuItem.menuInfo as AdapterContextMenuInfo? ?: return true
|
||||
|
||||
val entry = viewAdapter.getCurrentList()[info.position] as MusicDirectory.Entry?
|
||||
?: return true
|
||||
|
||||
val entryId = entry.id
|
||||
|
||||
when (menuItem.itemId) {
|
||||
R.id.menu_play_now -> {
|
||||
downloadHandler.downloadRecursively(
|
||||
this, entryId, save = false, append = false,
|
||||
autoPlay = true, shuffle = false, background = false,
|
||||
playNext = false, unpin = false, isArtist = false
|
||||
)
|
||||
}
|
||||
R.id.menu_play_next -> {
|
||||
downloadHandler.downloadRecursively(
|
||||
this, entryId, save = false, append = false,
|
||||
autoPlay = false, shuffle = false, background = false,
|
||||
playNext = true, unpin = false, isArtist = false
|
||||
)
|
||||
}
|
||||
R.id.menu_play_last -> {
|
||||
downloadHandler.downloadRecursively(
|
||||
this, entryId, save = false, append = true,
|
||||
autoPlay = false, shuffle = false, background = false,
|
||||
playNext = false, unpin = false, isArtist = false
|
||||
)
|
||||
}
|
||||
R.id.menu_pin -> {
|
||||
downloadHandler.downloadRecursively(
|
||||
this, entryId, save = true, append = true,
|
||||
autoPlay = false, shuffle = false, background = false,
|
||||
playNext = false, unpin = false, isArtist = false
|
||||
)
|
||||
}
|
||||
R.id.menu_unpin -> {
|
||||
downloadHandler.downloadRecursively(
|
||||
this, entryId, save = false, append = false,
|
||||
autoPlay = false, shuffle = false, background = false,
|
||||
playNext = false, unpin = true, isArtist = false
|
||||
)
|
||||
}
|
||||
R.id.menu_download -> {
|
||||
downloadHandler.downloadRecursively(
|
||||
this, entryId, save = false, append = false,
|
||||
autoPlay = false, shuffle = false, background = true,
|
||||
playNext = false, unpin = false, isArtist = false
|
||||
)
|
||||
}
|
||||
R.id.select_album_play_all -> {
|
||||
// TODO: Why is this being handled here?!
|
||||
playAll()
|
||||
}
|
||||
R.id.menu_item_share -> {
|
||||
val entries: MutableList<MusicDirectory.Entry?> = ArrayList(1)
|
||||
entries.add(entry)
|
||||
shareHandler.createShare(
|
||||
this, entries, refreshListView,
|
||||
cancellationToken!!
|
||||
)
|
||||
return true
|
||||
}
|
||||
else -> {
|
||||
return super.onContextItemSelected(menuItem)
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onPrepareOptionsMenu(menu: Menu) {
|
||||
super.onPrepareOptionsMenu(menu)
|
||||
playAllButton = menu.findItem(R.id.select_album_play_all)
|
||||
@ -503,7 +425,7 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Entry>() {
|
||||
private val songsForGenreObserver = Observer<MusicDirectory> { musicDirectory ->
|
||||
|
||||
// Hide more button when results are less than album list size
|
||||
if (musicDirectory.getChildren().size < requireArguments().getInt(
|
||||
if (musicDirectory.size < requireArguments().getInt(
|
||||
Constants.INTENT_EXTRA_NAME_ALBUM_LIST_SIZE, 0
|
||||
)
|
||||
) {
|
||||
@ -700,12 +622,74 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Entry>() {
|
||||
return listModel.currentList
|
||||
}
|
||||
|
||||
@Suppress("LongMethod")
|
||||
override fun onContextMenuItemSelected(
|
||||
menuItem: MenuItem,
|
||||
item: MusicDirectory.Entry
|
||||
): Boolean {
|
||||
// TODO
|
||||
return false
|
||||
val entryId = item.id
|
||||
|
||||
when (menuItem.itemId) {
|
||||
R.id.menu_play_now -> {
|
||||
downloadHandler.downloadRecursively(
|
||||
this, entryId, save = false, append = false,
|
||||
autoPlay = true, shuffle = false, background = false,
|
||||
playNext = false, unpin = false, isArtist = false
|
||||
)
|
||||
}
|
||||
R.id.menu_play_next -> {
|
||||
downloadHandler.downloadRecursively(
|
||||
this, entryId, save = false, append = false,
|
||||
autoPlay = false, shuffle = false, background = false,
|
||||
playNext = true, unpin = false, isArtist = false
|
||||
)
|
||||
}
|
||||
R.id.menu_play_last -> {
|
||||
downloadHandler.downloadRecursively(
|
||||
this, entryId, save = false, append = true,
|
||||
autoPlay = false, shuffle = false, background = false,
|
||||
playNext = false, unpin = false, isArtist = false
|
||||
)
|
||||
}
|
||||
R.id.menu_pin -> {
|
||||
downloadHandler.downloadRecursively(
|
||||
this, entryId, save = true, append = true,
|
||||
autoPlay = false, shuffle = false, background = false,
|
||||
playNext = false, unpin = false, isArtist = false
|
||||
)
|
||||
}
|
||||
R.id.menu_unpin -> {
|
||||
downloadHandler.downloadRecursively(
|
||||
this, entryId, save = false, append = false,
|
||||
autoPlay = false, shuffle = false, background = false,
|
||||
playNext = false, unpin = true, isArtist = false
|
||||
)
|
||||
}
|
||||
R.id.menu_download -> {
|
||||
downloadHandler.downloadRecursively(
|
||||
this, entryId, save = false, append = false,
|
||||
autoPlay = false, shuffle = false, background = true,
|
||||
playNext = false, unpin = false, isArtist = false
|
||||
)
|
||||
}
|
||||
R.id.select_album_play_all -> {
|
||||
// TODO: Why is this being handled here?!
|
||||
playAll()
|
||||
}
|
||||
R.id.menu_item_share -> {
|
||||
val entries: MutableList<MusicDirectory.Entry?> = ArrayList(1)
|
||||
entries.add(item)
|
||||
shareHandler.createShare(
|
||||
this, entries, refreshListView,
|
||||
cancellationToken!!
|
||||
)
|
||||
return true
|
||||
}
|
||||
else -> {
|
||||
return super.onContextItemSelected(menuItem)
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onItemClick(item: MusicDirectory.Entry) {
|
||||
|
@ -118,6 +118,4 @@ open class GenericListModel(application: Application) :
|
||||
internal fun hasOnlyFolders(musicDirectory: MusicDirectory) =
|
||||
musicDirectory.getChildren(includeDirs = true, includeFiles = false).size ==
|
||||
musicDirectory.getChildren(includeDirs = true, includeFiles = true).size
|
||||
|
||||
internal val allSongsId = "-1"
|
||||
}
|
||||
|
@ -9,7 +9,6 @@ package org.moire.ultrasonic.model
|
||||
|
||||
import android.app.Application
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import java.util.LinkedList
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.moire.ultrasonic.domain.MusicDirectory
|
||||
@ -38,33 +37,15 @@ class TrackCollectionModel(application: Application) : GenericListModel(applicat
|
||||
withContext(Dispatchers.IO) {
|
||||
|
||||
val service = MusicServiceFactory.getMusicService()
|
||||
val musicDirectory = service.getMusicDirectory(id, name, refresh)
|
||||
|
||||
var root = MusicDirectory()
|
||||
|
||||
if (allSongsId == id && parentId != null) {
|
||||
val musicDirectory = service.getMusicDirectory(
|
||||
parentId, name, refresh
|
||||
)
|
||||
|
||||
val songs: MutableList<MusicDirectory.Entry> = LinkedList()
|
||||
getSongsRecursively(musicDirectory, songs)
|
||||
|
||||
for (song in songs) {
|
||||
if (!song.isDirectory) {
|
||||
root.addChild(song)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val musicDirectory = service.getMusicDirectory(id, name, refresh)
|
||||
root = musicDirectory
|
||||
}
|
||||
|
||||
currentDirectory.postValue(root)
|
||||
updateList(root)
|
||||
currentDirectory.postValue(musicDirectory)
|
||||
updateList(musicDirectory)
|
||||
}
|
||||
}
|
||||
|
||||
// Given a Music directory "songs" it recursively adds all children to "songs"
|
||||
@Suppress("unused")
|
||||
private fun getSongsRecursively(
|
||||
parent: MusicDirectory,
|
||||
songs: MutableList<MusicDirectory.Entry>
|
||||
@ -78,13 +59,8 @@ class TrackCollectionModel(application: Application) : GenericListModel(applicat
|
||||
}
|
||||
|
||||
for ((id1, _, _, title) in parent.getAlbums()) {
|
||||
var root: MusicDirectory
|
||||
|
||||
if (allSongsId != id1) {
|
||||
root = service.getMusicDirectory(id1, title, false)
|
||||
|
||||
getSongsRecursively(root, songs)
|
||||
}
|
||||
val root: MusicDirectory = service.getMusicDirectory(id1, title, false)
|
||||
getSongsRecursively(root, songs)
|
||||
}
|
||||
}
|
||||
|
||||
@ -93,39 +69,7 @@ class TrackCollectionModel(application: Application) : GenericListModel(applicat
|
||||
withContext(Dispatchers.IO) {
|
||||
|
||||
val service = MusicServiceFactory.getMusicService()
|
||||
|
||||
val musicDirectory: MusicDirectory
|
||||
|
||||
if (allSongsId == id && parentId != null) {
|
||||
val root = MusicDirectory()
|
||||
|
||||
val songs: MutableCollection<MusicDirectory.Entry> = LinkedList()
|
||||
val artist = service.getArtist(parentId, "", false)
|
||||
|
||||
// FIXME is still working?
|
||||
for ((id1) in artist) {
|
||||
if (allSongsId != id1) {
|
||||
val albumDirectory = service.getAlbum(
|
||||
id1, "", false
|
||||
)
|
||||
|
||||
for (song in albumDirectory.getTracks()) {
|
||||
if (!song.isVideo) {
|
||||
songs.add(song)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (song in songs) {
|
||||
if (!song.isDirectory) {
|
||||
root.addChild(song)
|
||||
}
|
||||
}
|
||||
musicDirectory = root
|
||||
} else {
|
||||
musicDirectory = service.getAlbum(id, name, refresh)
|
||||
}
|
||||
val musicDirectory: MusicDirectory = service.getAlbum(id, name, refresh)
|
||||
|
||||
currentDirectory.postValue(musicDirectory)
|
||||
updateList(musicDirectory)
|
||||
@ -217,7 +161,7 @@ class TrackCollectionModel(application: Application) : GenericListModel(applicat
|
||||
for (share in shares) {
|
||||
if (share.id == shareId) {
|
||||
for (entry in share.getEntries()) {
|
||||
musicDirectory.addChild(entry)
|
||||
musicDirectory.add(entry)
|
||||
}
|
||||
break
|
||||
}
|
||||
|
@ -584,7 +584,7 @@ class AutoMediaBrowserService : MediaBrowserServiceCompat() {
|
||||
)
|
||||
}
|
||||
|
||||
if (albums?.getChildren()?.count() ?: 0 >= DISPLAY_LIMIT)
|
||||
if (albums?.size ?: 0 >= DISPLAY_LIMIT)
|
||||
mediaItems.add(
|
||||
R.string.search_more,
|
||||
listOf(MEDIA_ALBUM_PAGE_ID, type.typeName, (page ?: 0) + 1).joinToString("|"),
|
||||
@ -626,7 +626,7 @@ class AutoMediaBrowserService : MediaBrowserServiceCompat() {
|
||||
val content = callWithErrorHandling { musicService.getPlaylist(id, name) }
|
||||
|
||||
if (content != null) {
|
||||
if (content.getChildren().count() > 1)
|
||||
if (content.size > 1)
|
||||
mediaItems.addPlayAllItem(
|
||||
listOf(MEDIA_PLAYLIST_ITEM, id, name).joinToString("|")
|
||||
)
|
||||
@ -928,7 +928,7 @@ class AutoMediaBrowserService : MediaBrowserServiceCompat() {
|
||||
val songs = callWithErrorHandling { musicService.getRandomSongs(DISPLAY_LIMIT) }
|
||||
|
||||
if (songs != null) {
|
||||
if (songs.getChildren().count() > 1)
|
||||
if (songs.size > 1)
|
||||
mediaItems.addPlayAllItem(listOf(MEDIA_SONG_RANDOM_ID).joinToString("|"))
|
||||
|
||||
// TODO: Paging is not implemented for songs, is it necessary at all?
|
||||
|
@ -109,7 +109,7 @@ class OfflineMusicService : MusicService, KoinComponent {
|
||||
val filename = getName(file)
|
||||
if (filename != null && !seen.contains(filename)) {
|
||||
seen.add(filename)
|
||||
result.addChild(createEntry(file, filename))
|
||||
result.add(createEntry(file, filename))
|
||||
}
|
||||
}
|
||||
|
||||
@ -207,7 +207,7 @@ class OfflineMusicService : MusicService, KoinComponent {
|
||||
val entryFile = File(line)
|
||||
val entryName = getName(entryFile)
|
||||
if (entryFile.exists() && entryName != null) {
|
||||
playlist.addChild(createEntry(entryFile, entryName))
|
||||
playlist.add(createEntry(entryFile, entryName))
|
||||
}
|
||||
}
|
||||
playlist
|
||||
@ -260,7 +260,7 @@ class OfflineMusicService : MusicService, KoinComponent {
|
||||
val finalSize: Int = children.size.coerceAtMost(size)
|
||||
for (i in 0 until finalSize) {
|
||||
val file = children[i % children.size]
|
||||
result.addChild(createEntry(file, getName(file)))
|
||||
result.add(createEntry(file, getName(file)))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
@ -319,7 +319,7 @@ open class RESTMusicService(
|
||||
) {
|
||||
val entry = podcastEntry.toDomainEntity()
|
||||
entry.track = null
|
||||
musicDirectory.addChild(entry)
|
||||
musicDirectory.add(entry)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -219,7 +219,7 @@ class DownloadHandler(
|
||||
for (share in shares) {
|
||||
if (share.id == id) {
|
||||
for (entry in share.getEntries()) {
|
||||
root.addChild(entry)
|
||||
root.add(entry)
|
||||
}
|
||||
break
|
||||
}
|
||||
|
@ -519,7 +519,7 @@ object Util {
|
||||
fun getSongsFromSearchResult(searchResult: SearchResult): MusicDirectory {
|
||||
val musicDirectory = MusicDirectory()
|
||||
for (entry in searchResult.songs) {
|
||||
musicDirectory.addChild(entry)
|
||||
musicDirectory.add(entry)
|
||||
}
|
||||
return musicDirectory
|
||||
}
|
||||
@ -531,7 +531,7 @@ object Util {
|
||||
for (bookmark in bookmarks) {
|
||||
song = bookmark.entry
|
||||
song.bookmarkPosition = bookmark.position
|
||||
musicDirectory.addChild(song)
|
||||
musicDirectory.add(song)
|
||||
}
|
||||
return musicDirectory
|
||||
}
|
||||
|
@ -1,10 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM13,17h-2v-6h2v6zM13,9h-2L11,7h2v2z"/>
|
||||
</vector>
|
@ -1,51 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:a="http://schemas.android.com/apk/res/android"
|
||||
a:orientation="horizontal"
|
||||
a:layout_width="fill_parent"
|
||||
a:layout_height="wrap_content"
|
||||
a:minHeight="?android:attr/listPreferredItemHeight">
|
||||
|
||||
<ImageView
|
||||
a:id="@+id/album_coverart"
|
||||
a:layout_width="64dp"
|
||||
a:layout_height="64dp"
|
||||
a:layout_gravity="start|center_vertical"
|
||||
a:paddingStart="3dip" />
|
||||
|
||||
<LinearLayout
|
||||
a:orientation="vertical"
|
||||
a:layout_width="0dip"
|
||||
a:layout_height="wrap_content"
|
||||
a:layout_weight="1"
|
||||
a:layout_gravity="start|center_vertical"
|
||||
a:paddingStart="6dip"
|
||||
a:paddingEnd="3dip">
|
||||
|
||||
<TextView
|
||||
a:id="@+id/album_title"
|
||||
a:layout_width="wrap_content"
|
||||
a:layout_height="wrap_content"
|
||||
a:textAppearance="?android:attr/textAppearanceMedium"
|
||||
a:singleLine="true"
|
||||
a:ellipsize="marquee" />
|
||||
|
||||
<TextView
|
||||
a:id="@+id/album_artist"
|
||||
a:layout_width="wrap_content"
|
||||
a:layout_height="wrap_content"
|
||||
a:textAppearance="?android:attr/textAppearanceSmall"
|
||||
a:singleLine="true" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
a:id="@+id/album_star"
|
||||
a:layout_width="38dp"
|
||||
a:layout_height="fill_parent"
|
||||
a:gravity="center_vertical"
|
||||
a:background="@android:color/transparent"
|
||||
a:src="?attr/star_hollow"
|
||||
a:focusable="false"
|
||||
a:paddingEnd="3dip" />
|
||||
|
||||
</LinearLayout>
|
@ -2,7 +2,7 @@
|
||||
<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/row_artist_layout"
|
||||
a:id="@+id/containing_layout"
|
||||
a:layout_width="match_parent"
|
||||
a:layout_height="wrap_content"
|
||||
a:background="?android:attr/selectableItemBackground"
|
||||
@ -10,7 +10,7 @@
|
||||
a:focusable="true">
|
||||
|
||||
<com.google.android.material.imageview.ShapeableImageView
|
||||
a:id="@+id/album_coverart"
|
||||
a:id="@+id/coverart"
|
||||
a:layout_width="64dp"
|
||||
a:layout_height="64dp"
|
||||
a:layout_gravity="center_horizontal|center_vertical"
|
||||
@ -35,8 +35,8 @@
|
||||
a:paddingEnd="3dip"
|
||||
a:textAppearance="?android:attr/textAppearanceMedium"
|
||||
app:layout_constraintEnd_toStartOf="@+id/album_star"
|
||||
app:layout_constraintLeft_toRightOf="@+id/album_coverart"
|
||||
app:layout_constraintStart_toEndOf="@+id/album_coverart"
|
||||
app:layout_constraintLeft_toRightOf="@+id/coverart"
|
||||
app:layout_constraintStart_toEndOf="@+id/coverart"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<TextView
|
||||
@ -72,6 +72,7 @@
|
||||
app:layout_constraintLeft_toRightOf="@+id/row_album_details"
|
||||
app:layout_constraintStart_toEndOf="@+id/row_album_details"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:src="@drawable/ic_star_hollow_dark" />
|
||||
tools:src="@drawable/ic_star_hollow_dark"
|
||||
a:contentDescription="@string/download.menu_star" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -1,9 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:a="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
a:id="@+id/row_artist_layout"
|
||||
a:layout_height="wrap_content"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
a:id="@+id/containing_layout"
|
||||
a:layout_width="match_parent"
|
||||
a:layout_height="wrap_content"
|
||||
a:background="?android:attr/selectableItemBackground"
|
||||
a:clickable="true"
|
||||
a:focusable="true">
|
||||
@ -17,17 +18,17 @@
|
||||
a:minHeight="56dip"
|
||||
a:paddingStart="8dip"
|
||||
a:paddingEnd="8dip"
|
||||
a:text="A"
|
||||
a:textAppearance="?android:attr/textAppearanceLarge"
|
||||
a:textColor="@color/cyan" />
|
||||
a:textColor="@color/cyan"
|
||||
tools:text="A" />
|
||||
|
||||
<com.google.android.material.imageview.ShapeableImageView
|
||||
a:id="@+id/artist_coverart"
|
||||
a:id="@+id/coverart"
|
||||
a:layout_width="40dp"
|
||||
a:layout_height="40dp"
|
||||
a:layout_gravity="center_horizontal|center_vertical"
|
||||
a:layout_marginTop="8dp"
|
||||
a:layout_marginStart="2dp"
|
||||
a:layout_marginTop="8dp"
|
||||
a:layout_marginEnd="10dp"
|
||||
a:layout_toEndOf="@+id/row_section"
|
||||
a:scaleType="fitCenter"
|
||||
@ -38,13 +39,13 @@
|
||||
a:id="@+id/row_artist_name"
|
||||
a:layout_width="fill_parent"
|
||||
a:layout_height="wrap_content"
|
||||
a:layout_toEndOf="@+id/artist_coverart"
|
||||
a:layout_marginEnd="12dp"
|
||||
a:layout_toEndOf="@+id/coverart"
|
||||
a:drawablePadding="6dip"
|
||||
a:gravity="center_vertical"
|
||||
a:minHeight="56dip"
|
||||
a:paddingStart="3dip"
|
||||
a:paddingEnd="3dip"
|
||||
a:layout_marginEnd="12dp"
|
||||
|
||||
a:textAppearance="?android:attr/textAppearanceMedium" />
|
||||
|
||||
</RelativeLayout>
|
@ -26,7 +26,7 @@
|
||||
a:gravity="center_vertical"
|
||||
a:paddingEnd="4dip"/>
|
||||
|
||||
<include layout="@layout/song_details" />
|
||||
<include layout="@layout/list_item_track_details" />
|
||||
|
||||
<LinearLayout
|
||||
a:id="@+id/song_rating"
|
@ -4,7 +4,7 @@
|
||||
a:layout_height="fill_parent"
|
||||
a:orientation="vertical">
|
||||
|
||||
<include layout="@layout/empty_view" />
|
||||
<include layout="@layout/recycler_view" />
|
||||
<include layout="@layout/list_parts_empty_view" />
|
||||
<include layout="@layout/list_parts_recycler" />
|
||||
|
||||
</LinearLayout>
|
@ -4,8 +4,8 @@
|
||||
a:layout_height="fill_parent"
|
||||
a:orientation="vertical" >
|
||||
|
||||
<include layout="@layout/empty_view" />
|
||||
<include layout="@layout/recycler_view" />
|
||||
<include layout="@layout/list_parts_empty_view" />
|
||||
<include layout="@layout/list_parts_recycler" />
|
||||
<include layout="@layout/album_buttons" />
|
||||
|
||||
</LinearLayout>
|
@ -5,7 +5,7 @@
|
||||
a:layout_height="match_parent"
|
||||
a:orientation="vertical">
|
||||
|
||||
<include layout="@layout/empty_view" />
|
||||
<include layout="@layout/list_parts_empty_view" />
|
||||
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
a:id="@+id/swipe_refresh_view"
|
||||
|
26
ultrasonic/src/main/res/menu/context_menu_track.xml
Normal file
26
ultrasonic/src/main/res/menu/context_menu_track.xml
Normal file
@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:a="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item
|
||||
a:id="@+id/song_menu_play_now"
|
||||
a:title="@string/common.play_now" />
|
||||
<item
|
||||
a:id="@+id/song_menu_play_next"
|
||||
a:title="@string/common.play_next" />
|
||||
<item
|
||||
a:id="@+id/song_menu_play_last"
|
||||
a:title="@string/common.play_last" />
|
||||
<item
|
||||
a:id="@+id/song_menu_pin"
|
||||
a:title="@string/common.pin" />
|
||||
<item
|
||||
a:id="@+id/song_menu_unpin"
|
||||
a:title="@string/common.unpin" />
|
||||
<item
|
||||
a:id="@+id/song_menu_download"
|
||||
a:title="@string/common.download" />
|
||||
<item
|
||||
a:id="@+id/song_menu_share"
|
||||
a:title="@string/menu.share" />
|
||||
|
||||
</menu>
|
@ -1,26 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:a="http://schemas.android.com/apk/res/android" >
|
||||
|
||||
<item
|
||||
a:id="@+id/menu_play_now"
|
||||
a:title="@string/common.play_now"/>
|
||||
<item
|
||||
a:id="@+id/album_menu_play_next"
|
||||
a:title="@string/common.play_next"/>
|
||||
<item
|
||||
a:id="@+id/menu_play_last"
|
||||
a:title="@string/common.play_last"/>
|
||||
<item
|
||||
a:id="@+id/menu_pin"
|
||||
a:title="@string/common.pin"/>
|
||||
<item
|
||||
a:id="@+id/menu_unpin"
|
||||
a:title="@string/common.unpin"/>
|
||||
<item
|
||||
a:id="@+id/menu_download"
|
||||
a:title="@string/common.download"/>
|
||||
<item
|
||||
a:id="@+id/menu_item_share"
|
||||
a:title="@string/menu.share"/>
|
||||
|
||||
</menu>
|
@ -1,26 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:a="http://schemas.android.com/apk/res/android" >
|
||||
|
||||
<item
|
||||
a:id="@+id/song_menu_play_now"
|
||||
a:title="@string/common.play_now"/>
|
||||
<item
|
||||
a:id="@+id/song_menu_play_next"
|
||||
a:title="@string/common.play_next"/>
|
||||
<item
|
||||
a:id="@+id/song_menu_play_last"
|
||||
a:title="@string/common.play_last"/>
|
||||
<item
|
||||
a:id="@+id/song_menu_pin"
|
||||
a:title="@string/common.pin"/>
|
||||
<item
|
||||
a:id="@+id/song_menu_unpin"
|
||||
a:title="@string/common.unpin"/>
|
||||
<item
|
||||
a:id="@+id/song_menu_download"
|
||||
a:title="@string/common.download"/>
|
||||
<item
|
||||
a:id="@+id/menu_item_share"
|
||||
a:title="Share"/>
|
||||
|
||||
</menu>
|
@ -50,8 +50,8 @@ class APIAlbumConverterTest {
|
||||
|
||||
with(convertedEntity) {
|
||||
name `should be equal to` null
|
||||
getChildren().size `should be equal to` entity.songList.size
|
||||
getChildren()[0] `should be equal to` entity.songList[0].toDomainEntity()
|
||||
size `should be equal to` entity.songList.size
|
||||
this[0] `should be equal to` entity.songList[0].toDomainEntity()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -24,7 +24,7 @@ class APIMusicDirectoryConverterTest {
|
||||
|
||||
with(convertedEntity) {
|
||||
name `should be equal to` entity.name
|
||||
getChildren().size `should be equal to` entity.childList.size
|
||||
size `should be equal to` entity.childList.size
|
||||
getChildren() `should be equal to` entity.childList
|
||||
.map { it.toDomainEntity() }.toMutableList()
|
||||
}
|
||||
|
@ -26,9 +26,9 @@ class APIPlaylistConverterTest {
|
||||
|
||||
with(convertedEntity) {
|
||||
name `should be equal to` entity.name
|
||||
getChildren().size `should be equal to` entity.entriesList.size
|
||||
getChildren()[0] `should be equal to` entity.entriesList[0].toDomainEntity()
|
||||
getChildren()[1] `should be equal to` entity.entriesList[1].toDomainEntity()
|
||||
size `should be equal to` entity.entriesList.size
|
||||
this[0] `should be equal to` entity.entriesList[0].toDomainEntity()
|
||||
this[1] `should be equal to` entity.entriesList[1].toDomainEntity()
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user