Android Auto: improve media service browsing (#437)

* Add Android Auto icons and improve media service browsing

* chore: changelog and build updated for release

* add grid/list setting for playlist, podcast and radio

---------

Co-authored-by: eddyizm <eddyizm@gmail.com>
This commit is contained in:
MaFo-28 2026-02-27 06:09:49 +01:00 committed by GitHub
parent 145bb82eb0
commit 3ba2255205
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
26 changed files with 739 additions and 279 deletions

View file

@ -1,6 +1,5 @@
package com.cappielloantonio.tempo.repository;
import android.content.ContentResolver;
import android.net.Uri;
import android.view.View;
@ -69,6 +68,16 @@ public class AutomotiveRepository {
if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getAlbumList2() != null && response.body().getSubsonicResponse().getAlbumList2().getAlbums() != null) {
List<AlbumID3> albums = response.body().getSubsonicResponse().getAlbumList2().getAlbums();
// add by MFO
// Hack for artist view
if("alphabeticalByArtist".equals(type))for(AlbumID3 album : albums){
String artistName = album.getArtist();
String albumName = album.getName();
album.setName(artistName);
album.setArtist(albumName);
}
// end add by MFO
List<MediaItem> mediaItems = new ArrayList<>();
for (AlbumID3 album : albums) {

View file

@ -90,8 +90,17 @@ object Preferences {
private const val ARTIST_DISPLAY_BIOGRAPHY= "artist_display_biography"
private const val NETWORK_PING_TIMEOUT = "network_ping_timeout_base"
@JvmStatic
private const val AA_ALBUM_VIEW = "androidauto_album_view"
private const val AA_HOME_VIEW = "androidauto_home_view"
private const val AA_PLAYLIST_VIEW = "androidauto_playlist_view"
private const val AA_PODCAST_VIEW = "androidauto_podcast_view"
private const val AA_RADIO_VIEW = "androidauto_radio_view"
private const val AA_FIRST_TAB = "androidauto_first_tab"
private const val AA_SECOND_TAB = "androidauto_second_tab"
private const val AA_THIRD_TAB = "androidauto_third_tab"
private const val AA_FOURTH_TAB = "androidauto_fourth_tab"
@JvmStatic
fun getServer(): String? {
return App.getInstance().preferences.getString(SERVER, null)
}
@ -736,4 +745,50 @@ object Preferences {
fun setArtistDisplayBiography(displayBiographyEnabled: Boolean) {
App.getInstance().preferences.edit().putBoolean(ARTIST_DISPLAY_BIOGRAPHY, displayBiographyEnabled).apply()
}
@JvmStatic
fun isAndroidAutoAlbumViewEnabled(): Boolean {
return App.getInstance().preferences.getBoolean(AA_ALBUM_VIEW, true)
}
@JvmStatic
fun isAndroidAutoHomeViewEnabled(): Boolean {
return App.getInstance().preferences.getBoolean(AA_HOME_VIEW, false)
}
@JvmStatic
fun isAndroidAutoPlaylistViewEnabled(): Boolean {
return App.getInstance().preferences.getBoolean(AA_PLAYLIST_VIEW, false)
}
@JvmStatic
fun isAndroidAutoPodcastViewEnabled(): Boolean {
return App.getInstance().preferences.getBoolean(AA_PODCAST_VIEW, false)
}
@JvmStatic
fun isAndroidAutoRadioViewEnabled(): Boolean {
return App.getInstance().preferences.getBoolean(AA_RADIO_VIEW, false)
}
@JvmStatic
fun getAndroidAutoFirstTab(): Int {
return App.getInstance().preferences.getString(AA_FIRST_TAB, "0")!!.toInt()
}
@JvmStatic
fun getAndroidAutoSecondTab(): Int {
return App.getInstance().preferences.getString(AA_SECOND_TAB, "1")!!.toInt()
}
@JvmStatic
fun getAndroidAutoThirdTab(): Int {
return App.getInstance().preferences.getString(AA_THIRD_TAB, "2")!!.toInt()
}
@JvmStatic
fun getAndroidAutoFourthTab(): Int {
return App.getInstance().preferences.getString(AA_FOURTH_TAB, "3")!!.toInt()
}
}

View file

@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#FFFFFF" android:viewportHeight="960" android:viewportWidth="960" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M720,800L720,680L600,680L600,600L720,600L720,480L800,480L800,600L920,600L920,680L800,680L800,800L720,800ZM120,840Q87,840 63.5,816.5Q40,793 40,760L40,200Q40,167 63.5,143.5Q87,120 120,120L680,120Q713,120 736.5,143.5Q760,167 760,200L760,400L680,400L680,320L120,320L120,760Q120,760 120,760Q120,760 120,760L640,760L640,840L120,840ZM120,240L680,240L680,200Q680,200 680,200Q680,200 680,200L120,200Q120,200 120,200Q120,200 120,200L120,240ZM120,240L120,200Q120,200 120,200Q120,200 120,200L120,200Q120,200 120,200Q120,200 120,200L120,240Z"/>
</vector>

View file

@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#FFFFFF" android:viewportHeight="960" android:viewportWidth="960" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M200,840Q167,840 143.5,816.5Q120,793 120,760L120,200Q120,167 143.5,143.5Q167,120 200,120L760,120Q793,120 816.5,143.5Q840,167 840,200L840,468Q821,459 801,452.5Q781,446 760,443L760,200Q760,200 760,200Q760,200 760,200L200,200Q200,200 200,200Q200,200 200,200L200,760Q200,760 200,760Q200,760 200,760L442,760Q445,782 451.5,802Q458,822 467,840L200,840ZM200,720Q200,731 200,740.5Q200,750 200,760L200,760Q200,760 200,760Q200,760 200,760L200,200Q200,200 200,200Q200,200 200,200L200,200Q200,200 200,200Q200,200 200,200L200,443Q200,441 200,440.5Q200,440 200,440Q200,440 200,522Q200,604 200,720ZM280,680L443,680Q446,659 452.5,639Q459,619 467,600L280,600L280,680ZM280,520L524,520Q556,490 595.5,470Q635,450 680,443L680,440L280,440L280,520ZM280,360L680,360L680,280L280,280L280,360ZM720,920Q637,920 578.5,861.5Q520,803 520,720Q520,637 578.5,578.5Q637,520 720,520Q803,520 861.5,578.5Q920,637 920,720Q920,803 861.5,861.5Q803,920 720,920ZM700,840L740,840L740,740L840,740L840,700L740,700L740,600L700,600L700,700L600,700L600,740L700,740L700,840Z"/>
</vector>

View file

@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:viewportHeight="960"
android:viewportWidth="960"
android:width="24dp">
<path
android:fillColor="@android:color/white"
android:pathData="M480,660Q555,660 607.5,607.5Q660,555 660,480Q660,405 607.5,352.5Q555,300 480,300Q405,300 352.5,352.5Q300,405 300,480Q300,555 352.5,607.5Q405,660 480,660ZM451.5,508.5Q440,497 440,480Q440,463 451.5,451.5Q463,440 480,440Q497,440 508.5,451.5Q520,463 520,480Q520,497 508.5,508.5Q497,520 480,520Q463,520 451.5,508.5ZM480,880Q397,880 324,848.5Q251,817 197,763Q143,709 111.5,636Q80,563 80,480Q80,397 111.5,324Q143,251 197,197Q251,143 324,111.5Q397,80 480,80Q563,80 636,111.5Q709,143 763,197Q817,251 848.5,324Q880,397 880,480Q880,563 848.5,636Q817,709 763,763Q709,817 636,848.5Q563,880 480,880ZM480,800Q614,800 707,707Q800,614 800,480Q800,346 707,253Q614,160 480,160Q346,160 253,253Q160,346 160,480Q160,614 253,707Q346,800 480,800ZM480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Z"/>
</vector>

View file

@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:viewportHeight="960"
android:viewportWidth="960"
android:width="24dp">
<path
android:fillColor="@android:color/white"
android:pathData="M740,400L880,400L880,480L800,480L800,700Q800,742 771,771Q742,800 700,800Q658,800 629,771Q600,742 600,700Q600,658 629,629Q658,600 700,600Q708,600 718,601.5Q728,603 740,608L740,400ZM120,800L120,688Q120,653 137.5,625Q155,597 184,582Q246,551 310,535.5Q374,520 440,520Q482,520 523.5,526.5Q565,533 607,546Q587,558 571,575Q555,592 543,612Q517,606 491.5,603Q466,600 440,600Q383,600 328,614Q273,628 220,654Q211,659 205.5,668Q200,677 200,688L200,720L521,720Q523,740 530.5,760Q538,780 551,800L120,800ZM327,433Q280,386 280,320Q280,254 327,207Q374,160 440,160Q506,160 553,207Q600,254 600,320Q600,386 553,433Q506,480 440,480Q374,480 327,433ZM496.5,376.5Q520,353 520,320Q520,287 496.5,263.5Q473,240 440,240Q407,240 383.5,263.5Q360,287 360,320Q360,353 383.5,376.5Q407,400 440,400Q473,400 496.5,376.5ZM440,320Q440,320 440,320Q440,320 440,320Q440,320 440,320Q440,320 440,320Q440,320 440,320Q440,320 440,320Q440,320 440,320Q440,320 440,320ZM440,720L440,720L440,720Q440,720 440,720Q440,720 440,720Q440,720 440,720Q440,720 440,720Q440,720 440,720Q440,720 440,720Q440,720 440,720Q440,720 440,720Z"/>
</vector>

View file

@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#FFFFFF" android:viewportHeight="960" android:viewportWidth="960" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M120,840Q87,840 63.5,816.5Q40,793 40,760L40,240L120,240L120,760Q120,760 120,760Q120,760 120,760L800,760L800,840L120,840ZM280,680Q247,680 223.5,656.5Q200,633 200,600L200,160Q200,127 223.5,103.5Q247,80 280,80L480,80L560,160L840,160Q873,160 896.5,183.5Q920,207 920,240L920,600Q920,633 896.5,656.5Q873,680 840,680L280,680ZM280,600L840,600Q840,600 840,600Q840,600 840,600L840,240Q840,240 840,240Q840,240 840,240L527,240L447,160L280,160Q280,160 280,160Q280,160 280,160L280,600Q280,600 280,600Q280,600 280,600ZM280,600Q280,600 280,600Q280,600 280,600L280,160Q280,160 280,160Q280,160 280,160L280,160L280,240L280,240Q280,240 280,240Q280,240 280,240L280,600Q280,600 280,600Q280,600 280,600Z"/>
</vector>

View file

@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#FFFFFF" android:viewportHeight="960" android:viewportWidth="960" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M649,463.5Q737,447 800,420L800,820Q740,847 654,863.5Q568,880 480,880Q392,880 306,863.5Q220,847 160,820L160,420Q223,447 311,463.5Q399,480 480,480Q561,480 649,463.5ZM720,760L720,530Q670,544 604.5,552Q539,560 480,560Q421,560 355.5,552Q290,544 240,530L240,760Q290,778 355,789Q420,800 480,800Q540,800 605,789Q670,778 720,760ZM593,127Q640,174 640,240Q640,306 593,353Q546,400 480,400Q414,400 367,353Q320,306 320,240Q320,174 367,127Q414,80 480,80Q546,80 593,127ZM536.5,296.5Q560,273 560,240Q560,207 536.5,183.5Q513,160 480,160Q447,160 423.5,183.5Q400,207 400,240Q400,273 423.5,296.5Q447,320 480,320Q513,320 536.5,296.5ZM480,240Q480,240 480,240Q480,240 480,240Q480,240 480,240Q480,240 480,240Q480,240 480,240Q480,240 480,240Q480,240 480,240Q480,240 480,240ZM480,665Q480,665 480,665Q480,665 480,665Q480,665 480,665Q480,665 480,665L480,665Q480,665 480,665Q480,665 480,665Q480,665 480,665Q480,665 480,665Z"/>
</vector>

View file

@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#FFFFFF" android:viewportHeight="960" android:viewportWidth="960" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M240,760L360,760L360,520L600,520L600,760L720,760L720,400L480,220L240,400L240,760ZM160,840L160,360L480,120L800,360L800,840L520,840L520,600L440,600L440,840L160,840ZM480,490L480,490L480,490L480,490L480,490L480,490L480,490L480,490L480,490Z"/>
</vector>

View file

@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#FFFFFF" android:viewportHeight="960" android:viewportWidth="960" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M400,640L640,480L400,320L400,640ZM324,848.5Q251,817 197,763Q143,709 111.5,636Q80,563 80,480Q80,437 89,395.5Q98,354 115,315L177,377Q169,403 164.5,428.5Q160,454 160,480Q160,614 253,707Q346,800 480,800Q614,800 707,707Q800,614 800,480Q800,346 707,253Q614,160 480,160Q453,160 427.5,164.5Q402,169 377,177L316,116Q356,98 396,89Q436,80 480,80Q563,80 636,111.5Q709,143 763,197Q817,251 848.5,324Q880,397 880,480Q880,563 848.5,636Q817,709 763,763Q709,817 636,848.5Q563,880 480,880Q397,880 324,848.5ZM177.5,262.5Q160,245 160,220Q160,195 177.5,177.5Q195,160 220,160Q245,160 262.5,177.5Q280,195 280,220Q280,245 262.5,262.5Q245,280 220,280Q195,280 177.5,262.5ZM480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Z"/>
</vector>

View file

@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#FFFFFF" android:viewportHeight="960" android:viewportWidth="960" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M120,880L120,800L840,800L840,880L120,880ZM120,640L120,560L840,560L840,640L120,640ZM120,400L120,320L840,320L840,400L120,400ZM120,160L120,80L840,80L840,160L120,160Z"/>
</vector>

View file

@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#FFFFFF" android:viewportHeight="960" android:viewportWidth="960" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M120,640L120,560L440,560L440,640L120,640ZM120,480L120,400L600,400L600,480L120,480ZM120,320L120,240L600,240L600,320L120,320ZM640,840L640,520L880,680L640,840Z"/>
</vector>

View file

@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#FFFFFF" android:viewportHeight="960" android:viewportWidth="960" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M440,880L440,549Q422,538 411,520.5Q400,503 400,480Q400,447 423.5,423.5Q447,400 480,400Q513,400 536.5,423.5Q560,447 560,480Q560,503 549,521Q538,539 520,549L520,880L440,880ZM204,770Q147,715 113.5,640.5Q80,566 80,480Q80,397 111.5,324Q143,251 197,197Q251,143 324,111.5Q397,80 480,80Q563,80 636,111.5Q709,143 763,197Q817,251 848.5,324Q880,397 880,480Q880,566 846.5,641Q813,716 756,770L700,714Q746,670 773,609.5Q800,549 800,480Q800,346 707,253Q614,160 480,160Q346,160 253,253Q160,346 160,480Q160,549 187,609Q214,669 261,713L204,770ZM317,657Q282,624 261,578.5Q240,533 240,480Q240,380 310,310Q380,240 480,240Q580,240 650,310Q720,380 720,480Q720,533 699,579Q678,625 643,657L586,600Q611,577 625.5,546Q640,515 640,480Q640,414 593,367Q546,320 480,320Q414,320 367,367Q320,414 320,480Q320,516 334.5,546.5Q349,577 374,600L317,657Z"/>
</vector>

View file

@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#FFFFFF" android:viewportHeight="960" android:viewportWidth="960" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M160,880Q127,880 103.5,856.5Q80,833 80,800L80,320Q80,295 93.5,275Q107,255 130,246L636,40L662,106L332,240L800,240Q833,240 856.5,263.5Q880,287 880,320L880,800Q880,833 856.5,856.5Q833,880 800,880L160,880ZM160,800L800,800Q800,800 800,800Q800,800 800,800L800,520L160,520L160,800Q160,800 160,800Q160,800 160,800ZM391,731Q420,702 420,660Q420,618 391,589Q362,560 320,560Q278,560 249,589Q220,618 220,660Q220,702 249,731Q278,760 320,760Q362,760 391,731ZM160,440L640,440L640,360L720,360L720,440L800,440L800,320Q800,320 800,320Q800,320 800,320L160,320Q160,320 160,320Q160,320 160,320L160,440ZM160,800Q160,800 160,800Q160,800 160,800L160,520L160,520L160,800Q160,800 160,800Q160,800 160,800Z"/>
</vector>

View file

@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#FFFFFF" android:viewportHeight="960" android:viewportWidth="960" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M280,800L80,600L280,400L336,457L233,560L520,560L520,640L233,640L336,743L280,800ZM680,560L624,503L727,400L440,400L440,320L727,320L624,217L680,160L880,360L680,560Z"/>
</vector>

View file

@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="
M13,3c-4.97,0 -9,4.03 -9,9L1,12l3.89,3.89 0.07,0.14L9,12L6,12c0,-3.87 3.13,-7 7,-7s7,3.13 7,7 -3.13,7 -7,7c-1.93,0 -3.68,-0.79 -4.94,-2.06l-1.42,1.42C8.27,19.99 10.51,21 13,21c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9zM12,8v5l4.28,2.54 0.72,-1.21 -3.5,-2.08L13.5,8L12,8z
"/>
</vector>

View file

@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#FFFFFF" android:viewportHeight="960" android:viewportWidth="960" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M380,660L380,300L660,480L380,660ZM480,920Q372,920 277.5,870.5Q183,821 120,732L120,840L40,840L40,600L280,600L280,680L182,680Q233,755 311.5,797.5Q390,840 480,840Q595,840 688.5,774Q782,708 820,599L898,617Q853,753 738,836.5Q623,920 480,920ZM42,440Q49,373 74,311.5Q99,250 143,198L200,255Q168,296 148,342.5Q128,389 123,440L42,440ZM256,199L199,142Q252,98 313,72.5Q374,47 440,42L440,122Q389,127 343,147Q297,167 256,199ZM705,199Q664,167 617.5,147Q571,127 520,122L520,42Q587,48 648.5,73Q710,98 762,142L705,199ZM838,440Q833,389 813,342.5Q793,296 761,255L818,198Q862,250 887,311.5Q912,373 918,440L838,440Z"/>
</vector>

View file

@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#FFFFFF" android:viewportHeight="960" android:viewportWidth="960" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M320,720L480,598L640,720L580,522L740,408L544,408L480,200L416,408L220,408L380,522L320,720ZM480,880Q397,880 324,848.5Q251,817 197,763Q143,709 111.5,636Q80,563 80,480Q80,397 111.5,324Q143,251 197,197Q251,143 324,111.5Q397,80 480,80Q563,80 636,111.5Q709,143 763,197Q817,251 848.5,324Q880,397 880,480Q880,563 848.5,636Q817,709 763,763Q709,817 636,848.5Q563,880 480,880ZM480,800Q614,800 707,707Q800,614 800,480Q800,346 707,253Q614,160 480,160Q346,160 253,253Q160,346 160,480Q160,614 253,707Q346,800 480,800ZM480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Z"/>
</vector>

View file

@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#FFFFFF" android:viewportHeight="960" android:viewportWidth="960" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M489,500L580,445L671,500L647,396L727,327L622,318L580,220L538,318L433,327L513,396L489,500ZM508,760L732,760Q725,786 708,802Q691,818 664,822L228,875Q195,880 168.5,859.5Q142,839 138,806L85,369Q81,336 101,310Q121,284 154,280L200,274L200,354L164,359Q164,359 164,359Q164,359 164,359L218,796Q218,796 218,796Q218,796 218,796L508,760ZM360,680Q327,680 303.5,656.5Q280,633 280,600L280,160Q280,127 303.5,103.5Q327,80 360,80L800,80Q833,80 856.5,103.5Q880,127 880,160L880,600Q880,633 856.5,656.5Q833,680 800,680L360,680ZM360,600L800,600Q800,600 800,600Q800,600 800,600L800,160Q800,160 800,160Q800,160 800,160L360,160Q360,160 360,160Q360,160 360,160L360,600Q360,600 360,600Q360,600 360,600ZM580,380Q580,380 580,380Q580,380 580,380L580,380Q580,380 580,380Q580,380 580,380L580,380Q580,380 580,380Q580,380 580,380L580,380Q580,380 580,380Q580,380 580,380ZM218,796L218,796L218,796L218,796L218,796Q218,796 218,796Q218,796 218,796Z"/>
</vector>

View file

@ -254,4 +254,46 @@
<item>3</item>
<item>4</item>
</string-array>
<!-- Add by MFO -->
<string-array name="aa_tab_titles">
<item>Ne pas afficher</item>
<item>Accueil</item>
<item>Récent</item>
<item>Albums</item>
<item>Artists</item>
<item>Playlists</item>
<item>Podcast</item>
<item>Radio</item>
<item>Dossiers</item>
<item>Albums plus joués</item>
<!-- <item>Titres joués</item> -->
<item>Albums ajouté</item>
<!-- <item>Pour vous</item> -->
<item>Titres favoris</item>
<item>Albums favoris</item>
<item>Artistes favoris</item>
<item>Aléatoire</item>
</string-array>
<string-array name="aa_tab_values">
<item>-1</item>
<item>0</item>
<item>1</item>
<item>2</item>
<item>3</item>
<item>4</item>
<item>5</item>
<item>6</item>
<item>7</item>
<item>8</item>
<item>9</item>
<item>10</item>
<item>11</item>
<item>12</item>
<item>13</item>
<item>14</item>
<item>15</item>
</string-array>
<!-- end Add by MFO -->
</resources>

View file

@ -1,4 +1,22 @@
<resources>
<string name="aa_albums">Albums</string>
<string name="aa_album_most_played">Albums plus joués</string>
<string name="aa_album_recently_added">Albums ajoutés</string>
<string name="aa_artists">Artistes</string>
<string name="aa_home">Accueil</string>
<string name="aa_made_for_you">Pour vous</string>
<string name="aa_more">Plus</string>
<string name="aa_music_folder">Dossiers</string>
<string name="aa_playlists">Playlists</string>
<string name="aa_podcast">Podcast</string>
<string name="aa_radio">Radio</string>
<string name="aa_random">Aléatoire</string>
<string name="aa_recent_albums">Récents</string>
<string name="aa_song_recently_played">Titres joués</string>
<string name="aa_starred_albums">★ Albums</string>
<string name="aa_starred_artists">★ Artistes</string>
<string name="aa_starred_tracks">★ Titres</string>
<string name="activity_battery_optimizations_conclusion">Si vous rencontrez un problème, visitez https://dontkillmyapp.com. Des instructions pour désactiver les fonctions de sauvegarde d\'énergie qui pourrait affecter les performance de l\'app y sont disponibles.</string>
<string name="activity_battery_optimizations_summary">Veuillez désactiver les optimisations de la batterie pour permettre la lecture des médias lorsque l\'écran est éteint.</string>
<string name="activity_battery_optimizations_title">Optimisations de la batterie</string>
@ -310,6 +328,16 @@
<string name="settings_always_on_display">Toujours visible</string>
<string name="settings_allow_playlist_duplicates">Autoriser l\'ajout de doublons à une playlist</string>
<string name="settings_allow_playlist_duplicates_summary">Si activé, les doublons ne seront pas détectés à l\'ajout d\'un titre à une playlist.</string>
<string name="settings_androidauto">Android Auto</string>
<string name="settings_androidauto_album_view">Vue en grille des albums</string>
<string name="settings_androidauto_home_view">Vue en grille du menu accueil</string>
<string name="settings_androidauto_playlist_view">Vue en grille des playlists</string>
<string name="settings_androidauto_podcast_view">Vue en grille des podcasts</string>
<string name="settings_androidauto_radio_view">Vue en grille des radios</string>
<string name="settings_androidauto_first_tab">Affichage du premier onglet</string>
<string name="settings_androidauto_second_tab">Affichage du deuxième onglet</string>
<string name="settings_androidauto_third_tab">Affichage du troisième onglet</string>
<string name="settings_androidauto_fourth_tab">Affichage du quatrième onglet</string>
<string name="settings_audio_transcode_download_format">Format de transcodage</string>
<string name="settings_audio_transcode_download_priority_summary">Si activé, Tempus ne forcera pas le téléchargement de la piste avec les paramètres de transcodage ci-dessous.</string>
<string name="settings_audio_transcode_download_priority_title">Prioriser les paramètres du serveurs, utilisés pour le streaming, dans les téléchargements</string>
@ -404,7 +432,7 @@
<string name="settings_sync_starred_tracks_for_offline_use_summary">Si activé, les pistes favorites seront téléchargées pour l\'écoute hors-ligne.</string>
<string name="settings_sync_starred_tracks_for_offline_use_title">Synchronisation des pistes favorites pour écoute hors-ligne</string>
<string name="settings_theme">Thème</string>
<string name="settings_title_data">Données</string>
<string name="settings_title_data">Données</string>
<string name="settings_title_general">Géneral</string>
<string name="settings_title_playlist">Playlist</string>
<string name="settings_title_rating">Note</string>
@ -463,7 +491,7 @@
<string name="song_list_page_downloaded">Téléchargé</string>
<string name="song_list_page_most_played">Titres les plus joués</string>
<string name="song_list_page_recently_added">Titres ajoutés récemment</string>
<string name="song_list_page_recently_played">Titrés joués récemment</string>
<string name="song_list_page_recently_played">Titres joués récemment</string>
<string name="song_list_page_starred">Titres favoris</string>
<string name="song_list_page_top">Les meilleurs titres de %1$s</string>
<string name="song_list_page_year">Année %1$d</string>

View file

@ -278,4 +278,46 @@
<item>6</item>
<item>7</item>
</string-array>
<!-- Add by MFO -->
<string-array name="aa_tab_titles">
<item>Do not display</item>
<item>Home</item>
<item>Recent</item>
<item>Albums</item>
<item>Artists</item>
<item>Playlists</item>
<item>Podcast</item>
<item>Radio</item>
<item>Folder</item>
<item>Albums most played</item>
<!-- <item>Tracks played</item> -->
<item>Albums added</item>
<!-- <item>For you</item> -->
<item>Star tracks</item>
<item>Star albums</item>
<item>Star artistes</item>
<item>Random</item>
</string-array>
<string-array name="aa_tab_values">
<item>-1</item>
<item>0</item>
<item>1</item>
<item>2</item>
<item>3</item>
<item>4</item>
<item>5</item>
<item>6</item>
<item>7</item>
<item>8</item>
<item>9</item>
<item>10</item>
<item>11</item>
<item>12</item>
<item>13</item>
<item>14</item>
<item>15</item>
</string-array>
<!-- end Add by MFO -->
</resources>

View file

@ -1,4 +1,22 @@
<resources>
<string name="aa_albums">Albums</string>
<string name="aa_album_most_played">Albums most played</string>
<string name="aa_album_recently_added">Albums added</string>
<string name="aa_artists">Artists</string>
<string name="aa_home">Home</string>
<string name="aa_made_for_you">For you</string>
<string name="aa_more">More</string>
<string name="aa_music_folder">Folder</string>
<string name="aa_playlists">Playlists</string>
<string name="aa_podcast">Podcast</string>
<string name="aa_radio">Radio</string>
<string name="aa_random">Random</string>
<string name="aa_recent_albums">Recent</string>
<string name="aa_song_recently_played">Song played</string>
<string name="aa_starred_albums">★ Albums</string>
<string name="aa_starred_artists">★ Artists</string>
<string name="aa_starred_tracks">★ Tracks</string>
<string name="activity_battery_optimizations_conclusion">If in trouble visit https://dontkillmyapp.com. It provides detailed instructions on how to disable any power-saving features that may affect app\'s performance.</string>
<string name="activity_battery_optimizations_summary">Please disable battery optimizations for media playback while the screen is off.</string>
<string name="activity_battery_optimizations_title">Battery Optimizations</string>
@ -373,6 +391,16 @@
<string name="settings_podcast">Show podcast</string>
<string name="settings_podcast_summary">If enabled, show the podcast section. Restart the app for it to take full effect.</string>
<string name="settings_playlist_sort">Playlist sorting</string>
<string name="settings_androidauto">Android Auto</string>
<string name="settings_androidauto_album_view">Grid view for albums</string>
<string name="settings_androidauto_home_view">Grid view for home</string>
<string name="settings_androidauto_playlist_view">Grid view for playlists</string>
<string name="settings_androidauto_podcast_view">Grid view for podcast</string>
<string name="settings_androidauto_radio_view">Grid view for radio</string>
<string name="settings_androidauto_first_tab">First tab display</string>
<string name="settings_androidauto_second_tab">Second tab display</string>
<string name="settings_androidauto_third_tab">Third tab display</string>
<string name="settings_androidauto_fourth_tab">Fourth tab display</string>
<string name="settings_audio_quality">Show audio quality</string>
<string name="settings_audio_quality_summary">The bitrate and audio format will be shown for each audio track.</string>
<string name="settings_song_rating">Show song star rating</string>

View file

@ -146,7 +146,7 @@
android:defaultValue="false"
android:summary="@string/search_sort_summary"
android:key="sort_search_chronologically" />
<ListPreference
app:defaultValue="4"
app:dialogTitle="@string/settings_title_ui_landscape_items_per_row_dialog"
@ -155,7 +155,7 @@
app:key="landscape_items_per_row"
android:summary="@string/settings_summary_landscape_items_per_row"
app:title="@string/settings_title_ui_landscape_items_per_row" />
</PreferenceCategory>
<PreferenceCategory app:title="@string/settings_title_playlist">
@ -454,6 +454,76 @@
android:key="github_update_check" />
</PreferenceCategory>
<!-- Android Auto configuration -->
<PreferenceCategory app:title="@string/settings_androidauto">
<Preference
app:selectable="false"
app:summary="@string/home_rearrangement_dialog_subtitle" />
<SwitchPreference
android:title="@string/settings_androidauto_home_view"
android:defaultValue="false"
android:key="androidauto_home_view" />
<SwitchPreference
android:title="@string/settings_androidauto_album_view"
android:defaultValue="true"
android:key="androidauto_album_view" />
<SwitchPreference
android:title="@string/settings_androidauto_playlist_view"
android:defaultValue="false"
android:key="androidauto_playlist_view" />
<SwitchPreference
android:title="@string/settings_androidauto_radio_view"
android:defaultValue="false"
android:key="androidauto_radio_view" />
<SwitchPreference
android:title="@string/settings_androidauto_podcast_view"
android:defaultValue="false"
android:key="androidauto_podcast_view" />
<ListPreference
app:defaultValue="0"
app:dialogTitle="@string/settings_androidauto_first_tab"
app:entries="@array/aa_tab_titles"
app:entryValues="@array/aa_tab_values"
app:key="androidauto_first_tab"
app:title="@string/settings_androidauto_first_tab"
app:useSimpleSummaryProvider="true" />
<ListPreference
app:defaultValue="1"
app:dialogTitle="@string/settings_androidauto_second_tab"
app:entries="@array/aa_tab_titles"
app:entryValues="@array/aa_tab_values"
app:key="androidauto_second_tab"
app:title="@string/settings_androidauto_second_tab"
app:useSimpleSummaryProvider="true" />
<ListPreference
app:defaultValue="2"
app:dialogTitle="@string/settings_androidauto_third_tab"
app:entries="@array/aa_tab_titles"
app:entryValues="@array/aa_tab_values"
app:key="androidauto_third_tab"
app:title="@string/settings_androidauto_third_tab"
app:useSimpleSummaryProvider="true" />
<ListPreference
app:defaultValue="3"
app:dialogTitle="@string/settings_androidauto_fourth_tab"
app:entries="@array/aa_tab_titles"
app:entryValues="@array/aa_tab_values"
app:key="androidauto_fourth_tab"
app:title="@string/settings_androidauto_fourth_tab"
app:useSimpleSummaryProvider="true" />
</PreferenceCategory>
<!-- end Add by MFO -->
<PreferenceCategory app:title="@string/settings_about_title">
<Preference
app:selectable="false"

View file

@ -1,37 +1,47 @@
package com.cappielloantonio.tempo.service
import android.content.Context
import android.net.Uri
import androidx.lifecycle.LifecycleOwner
import android.os.Bundle
import androidx.media3.common.MediaItem
import androidx.media3.common.MediaItem.SubtitleConfiguration
import androidx.media3.common.MediaMetadata
import androidx.media3.session.LibraryResult
import androidx.media3.session.MediaConstants
import com.cappielloantonio.tempo.BuildConfig
import com.cappielloantonio.tempo.repository.AutomotiveRepository
import com.cappielloantonio.tempo.util.Preferences.getServerId
import com.google.common.collect.ImmutableList
import com.google.common.util.concurrent.Futures
import com.google.common.util.concurrent.ListenableFuture
import com.google.common.util.concurrent.SettableFuture
import com.cappielloantonio.tempo.R
import com.cappielloantonio.tempo.util.Preferences
object MediaBrowserTree {
private lateinit var appContext: Context
private lateinit var automotiveRepository: AutomotiveRepository
private var treeNodes: MutableMap<String, MediaItemNode> = mutableMapOf()
private var isInitialized = false
/* data class FunctionItem(
val id: String,
var isDisplayed: Boolean
)
*/
// Root
private const val ROOT_ID = "[rootID]"
// First level
// Available functions
private const val HOME_ID = "[homeID]"
private const val LIBRARY_ID = "[libraryID]"
private const val OTHER_ID = "[otherID]"
// Second level HOME_ID
private const val MOST_PLAYED_ID = "[mostPlayedID]"
private const val LAST_PLAYED_ID = "[lastPlayedID]"
private const val ALBUMS_ID = "[albumsID]"
private const val ARTISTS_ID = "[artistsID]"
private const val MOST_PLAYED_ID = "[mostPlayedID]"
private const val PLAYLIST_ID = "[playlistID]"
private const val PODCAST_ID = "[podcastID]"
private const val RADIO_ID = "[radioID]"
private const val RECENTLY_ADDED_ID = "[recentlyAddedID]"
private const val RECENT_SONGS_ID = "[recentSongsID]"
private const val MADE_FOR_YOU_ID = "[madeForYouID]"
@ -39,20 +49,17 @@ object MediaBrowserTree {
private const val STARRED_ALBUMS_ID = "[starredAlbumsID]"
private const val STARRED_ARTISTS_ID = "[starredArtistsID]"
private const val RANDOM_ID = "[randomID]"
// Second level LIBRARY_ID
private const val FOLDER_ID = "[folderID]"
// System functions
private const val INDEX_ID = "[indexID]"
private const val DIRECTORY_ID = "[directoryID]"
private const val PLAYLIST_ID = "[playlistID]"
// Second level OTHER_ID
private const val PODCAST_ID = "[podcastID]"
private const val RADIO_ID = "[radioID]"
private const val ALBUM_ID = "[albumID]"
private const val ARTIST_ID = "[artistID]"
private fun iconUri(resId: Int): Uri =
Uri.parse("android.resource://${BuildConfig.APPLICATION_ID}/$resId")
private class MediaItemNode(val item: MediaItem) {
private val children: MutableList<MediaItem> = ArrayList()
@ -71,6 +78,7 @@ object MediaBrowserTree {
}
private fun buildMediaItem(
gridView: Boolean,
title: String,
mediaId: String,
isPlayable: Boolean,
@ -83,18 +91,43 @@ object MediaBrowserTree {
sourceUri: Uri? = null,
imageUri: Uri? = null
): MediaItem {
val metadata =
MediaMetadata.Builder()
.setAlbumTitle(album)
.setTitle(title)
.setArtist(artist)
.setGenre(genre)
.setIsBrowsable(isBrowsable)
.setIsPlayable(isPlayable)
.setArtworkUri(imageUri)
.setMediaType(mediaType)
.build()
var extras = Bundle()
if( gridView ) {
extras = Bundle().apply {
putInt(
MediaConstants.EXTRAS_KEY_CONTENT_STYLE_BROWSABLE,
MediaConstants.EXTRAS_VALUE_CONTENT_STYLE_GRID_ITEM
)
putInt(
MediaConstants.EXTRAS_KEY_CONTENT_STYLE_PLAYABLE,
MediaConstants.EXTRAS_VALUE_CONTENT_STYLE_GRID_ITEM
)
}
}
else{
extras = Bundle().apply {
putInt(
MediaConstants.EXTRAS_KEY_CONTENT_STYLE_BROWSABLE,
MediaConstants.EXTRAS_VALUE_CONTENT_STYLE_LIST_ITEM
)
putInt(
MediaConstants.EXTRAS_KEY_CONTENT_STYLE_PLAYABLE,
MediaConstants.EXTRAS_VALUE_CONTENT_STYLE_LIST_ITEM
)
}
}
val metadata = MediaMetadata.Builder()
.setAlbumTitle(album)
.setTitle(title)
.setArtist(artist)
.setGenre(genre)
.setIsBrowsable(isBrowsable)
.setIsPlayable(isPlayable)
.setArtworkUri(imageUri)
.setMediaType(mediaType)
.setExtras(extras)
.build()
return MediaItem.Builder()
.setMediaId(mediaId)
.setSubtitleConfigurations(subtitleConfigurations)
@ -102,19 +135,57 @@ object MediaBrowserTree {
.setUri(sourceUri)
.build()
}
fun initialize(automotiveRepository: AutomotiveRepository) {
fun initialize(
context: Context,
automotiveRepository: AutomotiveRepository) {
this.automotiveRepository = automotiveRepository
appContext = context.applicationContext
if (isInitialized) return
isInitialized = true
}
fun buildTree() {
val albumView: Boolean = Preferences.isAndroidAutoAlbumViewEnabled()
val homeView: Boolean = Preferences.isAndroidAutoHomeViewEnabled()
val playlistView: Boolean = Preferences.isAndroidAutoPlaylistViewEnabled()
val podcastView: Boolean = Preferences.isAndroidAutoPodcastViewEnabled()
val radioView: Boolean = Preferences.isAndroidAutoRadioViewEnabled()
val tabIndex = listOf(
Preferences.getAndroidAutoFirstTab(),
Preferences.getAndroidAutoSecondTab(),
Preferences.getAndroidAutoThirdTab(),
Preferences.getAndroidAutoFourthTab()
)
// clear before rebuild
treeNodes.clear()
// This list must be exactly the same as the one in aa_tab_titles
val allFunctions = listOf(
HOME_ID,
LAST_PLAYED_ID,
ALBUMS_ID,
ARTISTS_ID,
PLAYLIST_ID,
PODCAST_ID,
RADIO_ID,
FOLDER_ID,
MOST_PLAYED_ID,
// RECENT_SONGS_ID, // => doesn't work !
RECENTLY_ADDED_ID,
// MADE_FOR_YOU_ID, // => doesn't work !
STARRED_TRACKS_ID,
STARRED_ALBUMS_ID,
STARRED_ARTISTS_ID,
RANDOM_ID
)
// Root level
treeNodes[ROOT_ID] =
MediaItemNode(
buildMediaItem(
gridView = albumView,
title = "Root Folder",
mediaId = ROOT_ID,
isPlayable = false,
@ -123,192 +194,98 @@ object MediaBrowserTree {
)
)
// First level
treeNodes[HOME_ID] =
MediaItemNode(
buildMediaItem(
title = "Home",
mediaId = HOME_ID,
isPlayable = false,
isBrowsable = true,
mediaType = MediaMetadata.MEDIA_TYPE_FOLDER_MIXED
)
)
treeNodes[LIBRARY_ID] =
MediaItemNode(
buildMediaItem(
title = "Library",
mediaId = LIBRARY_ID,
isPlayable = false,
isBrowsable = true,
mediaType = MediaMetadata.MEDIA_TYPE_FOLDER_MIXED
)
)
treeNodes[OTHER_ID] =
MediaItemNode(
buildMediaItem(
title = "Other",
mediaId = OTHER_ID,
isPlayable = false,
isBrowsable = true,
mediaType = MediaMetadata.MEDIA_TYPE_FOLDER_MIXED
)
)
treeNodes[ROOT_ID]!!.addChild(HOME_ID)
treeNodes[ROOT_ID]!!.addChild(LIBRARY_ID)
treeNodes[ROOT_ID]!!.addChild(OTHER_ID)
// Second level HOME_ID
treeNodes[MOST_PLAYED_ID] =
MediaItemNode(
buildMediaItem(
title = "Most played",
mediaId = MOST_PLAYED_ID,
isPlayable = false,
isBrowsable = true,
mediaType = MediaMetadata.MEDIA_TYPE_FOLDER_ALBUMS
)
)
// All available functions
// if HOME is in first place or no item is selected
if (tabIndex.firstOrNull() == 0 || tabIndex.all { it == -1 }){
treeNodes[HOME_ID] =
MediaItemNode(
buildMediaItem(
gridView = homeView,
title = appContext.getString(R.string.aa_home),
mediaId = HOME_ID,
isPlayable = false,
isBrowsable = true,
imageUri = iconUri(R.drawable.ic_aa_home),
mediaType = MediaMetadata.MEDIA_TYPE_FOLDER_MIXED
)
)
}
else { // More instead of Home
treeNodes[HOME_ID] =
MediaItemNode(
buildMediaItem(
gridView = homeView,
title = appContext.getString(R.string.aa_more),
mediaId = HOME_ID,
isPlayable = false,
isBrowsable = true,
imageUri = iconUri(R.drawable.ic_aa_other),
mediaType = MediaMetadata.MEDIA_TYPE_FOLDER_MIXED
)
)
}
treeNodes[LAST_PLAYED_ID] =
MediaItemNode(
buildMediaItem(
title = "Last played",
gridView = albumView,
title = appContext.getString(R.string.aa_recent_albums),
mediaId = LAST_PLAYED_ID,
isPlayable = false,
isBrowsable = true,
imageUri = iconUri(R.drawable.ic_aa_recent),
mediaType = MediaMetadata.MEDIA_TYPE_FOLDER_ALBUMS
)
)
treeNodes[RECENTLY_ADDED_ID] =
treeNodes[ALBUMS_ID] =
MediaItemNode(
buildMediaItem(
title = "Recently added",
mediaId = RECENTLY_ADDED_ID,
gridView = albumView,
title = appContext.getString(R.string.aa_albums),
mediaId = ALBUMS_ID,
isPlayable = false,
isBrowsable = true,
imageUri = iconUri(R.drawable.ic_aa_albums),
mediaType = MediaMetadata.MEDIA_TYPE_FOLDER_ALBUMS
)
)
treeNodes[RECENT_SONGS_ID] =
MediaItemNode(
buildMediaItem(
title = "Recent songs",
mediaId = RECENT_SONGS_ID,
isPlayable = false,
isBrowsable = true,
mediaType = MediaMetadata.MEDIA_TYPE_FOLDER_MIXED
)
)
treeNodes[MADE_FOR_YOU_ID] =
treeNodes[ARTISTS_ID] =
MediaItemNode(
buildMediaItem(
title = "Made for you",
mediaId = MADE_FOR_YOU_ID,
isPlayable = false,
isBrowsable = true,
mediaType = MediaMetadata.MEDIA_TYPE_FOLDER_PLAYLISTS
)
)
treeNodes[STARRED_TRACKS_ID] =
MediaItemNode(
buildMediaItem(
title = "Starred tracks",
mediaId = STARRED_TRACKS_ID,
isPlayable = false,
isBrowsable = true,
mediaType = MediaMetadata.MEDIA_TYPE_FOLDER_MIXED
)
)
treeNodes[STARRED_ALBUMS_ID] =
MediaItemNode(
buildMediaItem(
title = "Starred albums",
mediaId = STARRED_ALBUMS_ID,
gridView = albumView,
title = appContext.getString(R.string.aa_artists),
mediaId = ARTISTS_ID,
isPlayable = false,
isBrowsable = true,
imageUri = iconUri(R.drawable.ic_aa_artists),
mediaType = MediaMetadata.MEDIA_TYPE_FOLDER_ALBUMS
)
)
treeNodes[STARRED_ARTISTS_ID] =
MediaItemNode(
buildMediaItem(
title = "Starred artists",
mediaId = STARRED_ARTISTS_ID,
isPlayable = false,
isBrowsable = true,
mediaType = MediaMetadata.MEDIA_TYPE_FOLDER_ARTISTS
)
)
treeNodes[RANDOM_ID] =
MediaItemNode(
buildMediaItem(
title = "Random",
mediaId = RANDOM_ID,
isPlayable = false,
isBrowsable = true,
mediaType = MediaMetadata.MEDIA_TYPE_FOLDER_MIXED
)
)
treeNodes[HOME_ID]!!.addChild(MOST_PLAYED_ID)
treeNodes[HOME_ID]!!.addChild(LAST_PLAYED_ID)
treeNodes[HOME_ID]!!.addChild(RECENTLY_ADDED_ID)
treeNodes[HOME_ID]!!.addChild(RECENT_SONGS_ID)
treeNodes[HOME_ID]!!.addChild(MADE_FOR_YOU_ID)
treeNodes[HOME_ID]!!.addChild(STARRED_TRACKS_ID)
treeNodes[HOME_ID]!!.addChild(STARRED_ALBUMS_ID)
treeNodes[HOME_ID]!!.addChild(STARRED_ARTISTS_ID)
treeNodes[HOME_ID]!!.addChild(RANDOM_ID)
// Second level LIBRARY_ID
treeNodes[FOLDER_ID] =
MediaItemNode(
buildMediaItem(
title = "Folders",
mediaId = FOLDER_ID,
isPlayable = false,
isBrowsable = true,
mediaType = MediaMetadata.MEDIA_TYPE_FOLDER_MIXED
)
)
treeNodes[PLAYLIST_ID] =
MediaItemNode(
buildMediaItem(
title = "Playlists",
gridView = playlistView,
title = appContext.getString(R.string.aa_playlists),
mediaId = PLAYLIST_ID,
isPlayable = false,
isBrowsable = true,
imageUri = iconUri(R.drawable.ic_aa_playlist),
mediaType = MediaMetadata.MEDIA_TYPE_FOLDER_PLAYLISTS
)
)
treeNodes[LIBRARY_ID]!!.addChild(FOLDER_ID)
treeNodes[LIBRARY_ID]!!.addChild(PLAYLIST_ID)
// Second level OTHER_ID
treeNodes[PODCAST_ID] =
MediaItemNode(
buildMediaItem(
title = "Podcasts",
gridView = podcastView,
title = appContext.getString(R.string.aa_podcast),
mediaId = PODCAST_ID,
isPlayable = false,
isBrowsable = true,
imageUri = iconUri(R.drawable.ic_aa_podcasts),
mediaType = MediaMetadata.MEDIA_TYPE_FOLDER_PODCASTS
)
)
@ -316,18 +293,161 @@ object MediaBrowserTree {
treeNodes[RADIO_ID] =
MediaItemNode(
buildMediaItem(
title = "Radio stations",
gridView = radioView,
title = appContext.getString(R.string.aa_radio),
mediaId = RADIO_ID,
isPlayable = false,
isBrowsable = true,
imageUri = iconUri(R.drawable.ic_aa_radio),
mediaType = MediaMetadata.MEDIA_TYPE_FOLDER_RADIO_STATIONS
)
)
treeNodes[OTHER_ID]!!.addChild(PODCAST_ID)
treeNodes[OTHER_ID]!!.addChild(RADIO_ID)
}
treeNodes[MOST_PLAYED_ID] =
MediaItemNode(
buildMediaItem(
gridView = albumView,
title = appContext.getString(R.string.aa_album_most_played),
mediaId = MOST_PLAYED_ID,
isPlayable = false,
isBrowsable = true,
imageUri = iconUri(R.drawable.ic_aa_mostplayed),
mediaType = MediaMetadata.MEDIA_TYPE_FOLDER_ALBUMS
)
)
treeNodes[RECENTLY_ADDED_ID] =
MediaItemNode(
buildMediaItem(
gridView = albumView,
title = appContext.getString(R.string.aa_album_recently_added),
mediaId = RECENTLY_ADDED_ID,
isPlayable = false,
isBrowsable = true,
imageUri = iconUri(R.drawable.ic_aa_added_album),
mediaType = MediaMetadata.MEDIA_TYPE_FOLDER_ALBUMS
)
)
treeNodes[RECENT_SONGS_ID] =
MediaItemNode(
buildMediaItem(
gridView = albumView,
title = appContext.getString(R.string.aa_song_recently_played),
mediaId = RECENT_SONGS_ID,
isPlayable = false,
isBrowsable = true,
imageUri = iconUri(R.drawable.ic_aa_recent_title),
mediaType = MediaMetadata.MEDIA_TYPE_FOLDER_MIXED
)
)
treeNodes[MADE_FOR_YOU_ID] =
MediaItemNode(
buildMediaItem(
gridView = albumView,
title = appContext.getString(R.string.aa_made_for_you),
mediaId = MADE_FOR_YOU_ID,
isPlayable = false,
isBrowsable = true,
imageUri = iconUri(R.drawable.ic_aa_for_you),
mediaType = MediaMetadata.MEDIA_TYPE_FOLDER_PLAYLISTS
)
)
treeNodes[STARRED_TRACKS_ID] =
MediaItemNode(
buildMediaItem(
gridView = albumView,
title = appContext.getString(R.string.aa_starred_tracks),
mediaId = STARRED_TRACKS_ID,
isPlayable = false,
isBrowsable = true,
imageUri = iconUri(R.drawable.ic_aa_star_title),
mediaType = MediaMetadata.MEDIA_TYPE_FOLDER_MIXED
)
)
treeNodes[STARRED_ALBUMS_ID] =
MediaItemNode(
buildMediaItem(
gridView = albumView,
title = appContext.getString(R.string.aa_starred_albums),
mediaId = STARRED_ALBUMS_ID,
isPlayable = false,
isBrowsable = true,
imageUri = iconUri(R.drawable.ic_aa_star_album),
mediaType = MediaMetadata.MEDIA_TYPE_FOLDER_ALBUMS
)
)
treeNodes[STARRED_ARTISTS_ID] =
MediaItemNode(
buildMediaItem(
gridView = albumView,
title = appContext.getString(R.string.aa_starred_artists),
mediaId = STARRED_ARTISTS_ID,
isPlayable = false,
isBrowsable = true,
imageUri = iconUri(R.drawable.ic_aa_artists),
mediaType = MediaMetadata.MEDIA_TYPE_FOLDER_ARTISTS
)
)
treeNodes[FOLDER_ID] =
MediaItemNode(
buildMediaItem(
gridView = false,
title = appContext.getString(R.string.aa_music_folder),
mediaId = FOLDER_ID,
isPlayable = false,
isBrowsable = true,
imageUri = iconUri(R.drawable.ic_aa_folders),
mediaType = MediaMetadata.MEDIA_TYPE_FOLDER_MIXED
)
)
treeNodes[RANDOM_ID] =
MediaItemNode(
buildMediaItem(
gridView = albumView,
title = appContext.getString(R.string.aa_random),
mediaId = RANDOM_ID,
isPlayable = false,
isBrowsable = true,
imageUri = iconUri(R.drawable.ic_aa_random),
mediaType = MediaMetadata.MEDIA_TYPE_FOLDER_MIXED
)
)
val root = treeNodes[ROOT_ID]!!
val selectedIds = mutableSetOf<String>()
// First level
// add functions selected by user for the 4 tabs
tabIndex
.filter { it != -1 }
.forEach { index ->
allFunctions.getOrNull(index)?.let { function ->
if (selectedIds.add(function)) {
root.addChild(function)
}
}
}
// if no function is selected, add at least HOME_ID
if (selectedIds.isEmpty()) {
root.addChild(HOME_ID)
selectedIds.add(HOME_ID)
}
// Second level for HOME_ID even there is no HOME_ID displayed
// add all functions not previously added
allFunctions
.filter { it !in selectedIds }
.forEach { function ->
treeNodes[HOME_ID]?.addChild(function)
}
}
fun getRootItem(): MediaItem {
return treeNodes[ROOT_ID]!!.item
}
@ -337,125 +457,83 @@ object MediaBrowserTree {
): ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> {
return when (id) {
ROOT_ID -> treeNodes[ROOT_ID]?.getChildren()!!
HOME_ID -> treeNodes[HOME_ID]?.getChildren()!!
LIBRARY_ID -> treeNodes[LIBRARY_ID]?.getChildren()!!
OTHER_ID -> treeNodes[OTHER_ID]?.getChildren()!!
MOST_PLAYED_ID -> automotiveRepository.getAlbums(id, "frequent", 100)
LAST_PLAYED_ID -> automotiveRepository.getAlbums(id, "recent", 100)
RECENTLY_ADDED_ID -> automotiveRepository.getAlbums(id, "newest", 100)
RECENT_SONGS_ID -> automotiveRepository.getRecentlyPlayedSongs(getServerId(),100)
HOME_ID -> treeNodes[HOME_ID]?.getChildren()!!
LAST_PLAYED_ID -> automotiveRepository.getAlbums(id, "recent", 15)
ALBUMS_ID -> automotiveRepository.getAlbums(id, "alphabeticalByName", 500)
ARTISTS_ID -> automotiveRepository.getAlbums(id, "alphabeticalByArtist", 500)
PLAYLIST_ID -> automotiveRepository.getPlaylists(id)
PODCAST_ID -> automotiveRepository.getNewestPodcastEpisodes(100)
RADIO_ID -> automotiveRepository.internetRadioStations
FOLDER_ID -> automotiveRepository.getMusicFolders(id)
MOST_PLAYED_ID -> automotiveRepository.getAlbums(id, "frequent", 15)
RECENT_SONGS_ID -> automotiveRepository.getRecentlyPlayedSongs(getServerId(),30)
RECENTLY_ADDED_ID -> automotiveRepository.getAlbums(id, "newest", 15)
MADE_FOR_YOU_ID -> automotiveRepository.getStarredArtists(id)
STARRED_TRACKS_ID -> automotiveRepository.starredSongs
STARRED_ALBUMS_ID -> automotiveRepository.getStarredAlbums(id)
STARRED_ARTISTS_ID -> automotiveRepository.getStarredArtists(id)
RANDOM_ID -> automotiveRepository.getRandomSongs(100)
FOLDER_ID -> automotiveRepository.getMusicFolders(id)
PLAYLIST_ID -> automotiveRepository.getPlaylists(id)
PODCAST_ID -> automotiveRepository.getNewestPodcastEpisodes(100)
RADIO_ID -> automotiveRepository.internetRadioStations
else -> {
if (id.startsWith(MOST_PLAYED_ID)) {
return automotiveRepository.getAlbumTracks(
id.removePrefix(
MOST_PLAYED_ID
)
)
if (id.startsWith(LAST_PLAYED_ID)) {
return automotiveRepository.getAlbumTracks(id.removePrefix(LAST_PLAYED_ID))
}
if (id.startsWith(LAST_PLAYED_ID)) {
return automotiveRepository.getAlbumTracks(
id.removePrefix(
LAST_PLAYED_ID
)
)
if (id.startsWith(ALBUMS_ID)) {
return automotiveRepository.getAlbumTracks(id.removePrefix(ALBUMS_ID))
}
if (id.startsWith(ARTISTS_ID)) {
return automotiveRepository.getAlbumTracks(id.removePrefix(ARTISTS_ID))
}
if (id.startsWith(HOME_ID)) {
return automotiveRepository.getAlbumTracks(id.removePrefix(HOME_ID))
}
if (id.startsWith(MOST_PLAYED_ID)) {
return automotiveRepository.getAlbumTracks(id.removePrefix(MOST_PLAYED_ID))
}
if (id.startsWith(RECENTLY_ADDED_ID)) {
return automotiveRepository.getAlbumTracks(
id.removePrefix(
RECENTLY_ADDED_ID
)
)
return automotiveRepository.getAlbumTracks(id.removePrefix(RECENTLY_ADDED_ID))
}
if (id.startsWith(MADE_FOR_YOU_ID)) {
return automotiveRepository.getMadeForYou(
id.removePrefix(
MADE_FOR_YOU_ID
),
20
)
return automotiveRepository.getMadeForYou(id.removePrefix(MADE_FOR_YOU_ID),20)
}
if (id.startsWith(STARRED_ALBUMS_ID)) {
return automotiveRepository.getAlbumTracks(
id.removePrefix(
STARRED_ALBUMS_ID
)
)
return automotiveRepository.getAlbumTracks(id.removePrefix(STARRED_ALBUMS_ID))
}
if (id.startsWith(STARRED_ARTISTS_ID)) {
return automotiveRepository.getArtistAlbum(
STARRED_ALBUMS_ID,
id.removePrefix(
STARRED_ARTISTS_ID
)
)
}
if (id.startsWith(FOLDER_ID)) {
return automotiveRepository.getIndexes(
INDEX_ID,
id.removePrefix(
FOLDER_ID
)
)
}
if (id.startsWith(INDEX_ID)) {
return automotiveRepository.getDirectories(
DIRECTORY_ID,
id.removePrefix(
INDEX_ID
)
)
}
if (id.startsWith(DIRECTORY_ID)) {
return automotiveRepository.getDirectories(
DIRECTORY_ID,
id.removePrefix(
DIRECTORY_ID
)
)
return automotiveRepository.getArtistAlbum(STARRED_ALBUMS_ID,id.removePrefix(STARRED_ARTISTS_ID))
}
if (id.startsWith(PLAYLIST_ID)) {
return automotiveRepository.getPlaylistSongs(
id.removePrefix(
PLAYLIST_ID
)
)
return automotiveRepository.getPlaylistSongs(id.removePrefix(PLAYLIST_ID))
}
if (id.startsWith(ALBUM_ID)) {
return automotiveRepository.getAlbumTracks(
id.removePrefix(
ALBUM_ID
)
)
return automotiveRepository.getAlbumTracks(id.removePrefix(ALBUM_ID))
}
if (id.startsWith(ARTIST_ID)) {
return automotiveRepository.getArtistAlbum(
ALBUM_ID,
id.removePrefix(
ARTIST_ID
)
)
return automotiveRepository.getArtistAlbum(ALBUM_ID,id.removePrefix(ARTIST_ID))
}
if (id.startsWith(FOLDER_ID)) {
return automotiveRepository.getIndexes(INDEX_ID,id.removePrefix(FOLDER_ID))
}
if (id.startsWith(INDEX_ID)) {
return automotiveRepository.getDirectories(DIRECTORY_ID,id.removePrefix(INDEX_ID))
}
if (id.startsWith(DIRECTORY_ID)) {
return automotiveRepository.getDirectories(DIRECTORY_ID,id.removePrefix(DIRECTORY_ID))
}
return Futures.immediateFuture(LibraryResult.ofError(LibraryResult.RESULT_ERROR_BAD_VALUE))
@ -490,6 +568,7 @@ object MediaBrowserTree {
fun search(query: String): ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> {
return automotiveRepository.search(
query,
// ALBUM_ID,
ALBUM_ID,
ARTIST_ID
)

View file

@ -47,7 +47,8 @@ open class MediaLibrarySessionCallback(
MediaLibraryService.MediaLibrarySession.Callback {
init {
MediaBrowserTree.initialize(automotiveRepository)
// modified by MFO
MediaBrowserTree.initialize(context, automotiveRepository)
}
private val customCommandToggleShuffleModeOn = CommandButton.Builder()
@ -348,6 +349,8 @@ open class MediaLibrarySessionCallback(
browser: MediaSession.ControllerInfo,
params: MediaLibraryService.LibraryParams?
): ListenableFuture<LibraryResult<MediaItem>> {
// added by MFO
MediaBrowserTree.buildTree()
return Futures.immediateFuture(LibraryResult.ofItem(MediaBrowserTree.getRootItem(), params))
}