From f0e418687e2ad64de558eb66cd312a9dcddec809 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jaime=20Garc=C3=ADa?=
<55400857+jaime-grj@users.noreply.github.com>
Date: Wed, 8 Oct 2025 20:41:47 +0200
Subject: [PATCH 1/6] chore(i18n): Update Spanish translation
---
app/src/main/res/values-es-rES/strings.xml | 39 ++++++++++++++++++++++
1 file changed, 39 insertions(+)
diff --git a/app/src/main/res/values-es-rES/strings.xml b/app/src/main/res/values-es-rES/strings.xml
index 68706766..3cd3c840 100644
--- a/app/src/main/res/values-es-rES/strings.xml
+++ b/app/src/main/res/values-es-rES/strings.xml
@@ -69,6 +69,7 @@
Descargar
Se descargarán todas las pistas de esta carpeta. Las pistas en las subcarpetas no se descargarán.
Descargar las pistas
+ Indicar dónde se descarga la música
Una vez que descargues una pista, la encontrarás aquí
No hay descargas
%1$s • %2$s elementos
@@ -79,7 +80,10 @@
Selecciona una opción de almacenamiento
Externo
Interno
+ Directorio
Descargas
+ No se han encontrado descargas que falten
+ Actualizar descargas
Añadir a la cola
Reproducir siguiente
Eliminar
@@ -88,6 +92,8 @@
Obligatorio
Se necesita un prefijo http o https
Descargas
+ Añadir a favoritos
+ Cargando…
Selecciona dos o más filtros
Filtrar
Filtrar artistas
@@ -118,6 +124,7 @@
Descargar estas pistas usará una gran cantidad de datos
Parece que hay algunas pistas destacadas para sincronizar
Los álbumes marcados como favoritos estarán disponibles en el modo sin conexión.
+ Has destacado artistas con música que no has descargado
Lo mejor de
Descubrir
Todo en aleatorio
@@ -170,6 +177,7 @@
Descargado
Álbum
Artista
+ Soporte al usuario
Resolución de la imagen
Idioma
Idioma del sistema
@@ -201,6 +209,7 @@
%1$.2fx
Limpiar la cola de reproducción
Cola de reproducción guardada
+ La letra no se puede descargar
Prioridad del servidor
Formato desconocido
Transcodificando
@@ -212,6 +221,7 @@
Crear
Añadir a una lista de reproducción
Error al añadir a la lista
+ Todas las pistas se han descartado porque están repetidas
%1$d pistas • %2$s
Duración • %1$s
Pulsación larga para eliminar
@@ -280,6 +290,7 @@
Tempo es un cliente de música Subsonic ligero y de código abierto, diseñado nativamente para Android.
Acerca de
Pantalla siempre activa
+ Si está habilitada, no se comprobará si hay pistas repetidas cuando se añadan a la lista.
Formato de transcodificación
Si está habilitada, Tempo no descargará la pista con las opciones de transcodificación que aparecen a continuación.
Dar prioridad a las opciones del servidor usadas para el streaming en las descargas
@@ -316,7 +327,9 @@
Si está habilitada, muestra la valoración de la pista como barra de 5 estrellas en la página del control de reproducción.\n\n*Requiere reiniciar la aplicación
Mostrar valoración de los elementos
Sincronizar cola de reproducción para este usuario
+ Si está habilitada, muestra el botón de reproducción aleatoria y oculta el botón de «Favoritos» en el minirreproductor
Mostrar emisoras de radio
+ Descargar las letras automáticamente cuando estén disponibles para que se puedan mostrar cuando no hay conexión.
Configurar el modo de ganancia de reproducción
Esquinas redondeadas
Tamaño de las esquinas
@@ -368,6 +381,7 @@
Tema
Datos
General
+ Lista de reproducción
Valoraciones
Ganancia de reproducción
Rastreo de música (scrobble)
@@ -433,7 +447,9 @@
Se ha añadido a la lista
Mostrar valoración de las pistas
Sincronizar álbumes favoritos
+ Sincronizar artistas destacados para uso sin conexión
Si está habilitada, los álbumes favoritos se descargarán para uso sin conexión.
+ Sincronizar artistas destacados
Descargar los álbumes favoritos puede consumir una gran cantidad de datos.
Ecualizador
Restablecer
@@ -441,4 +457,27 @@
No disponible en este dispositivo
Ecualizador
Abrir el ecualizador integrado
+ Widget de Tempo
+ En pausa
+ Abrir Tempo
+ 0:00
+ Portada del álbum
+ Reproducir o pausar
+ Siguiente pista
+ Cambiar modo de repetición
+ Activar/desactivar aleatorio
+ Pista anterior
+ Establece una carpeta de descarga para actualizar tus descargas
+ Sincronizar artistas destacados
+ Descargar letras para uso sin conexión
+ Letras descargadas para uso sin conexión
+ Letra guardada para uso sin conexión
+ Permitir añadir pistas repetidas a la lista
+ Participa en las discusiones y el soporte de la comunidad
+ Mostrar el botón «Aleatorio»
+ Descargar automáticamente las letras
+ Descargar los artistas destacados podría consumir una gran cantidad de datos.
+ Si está habilitada, los artistas destacados se descargarán para uso sin conexión.
+ 0:00
+ Eliminar de favoritos
\ No newline at end of file
From 233bc9987e7cbecb9be0b2b47380aaa91706bfcb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jaime=20Garc=C3=ADa?=
<55400857+jaime-grj@users.noreply.github.com>
Date: Wed, 8 Oct 2025 20:41:47 +0200
Subject: [PATCH 2/6] chore(i18n): Update Spanish translation
---
app/src/main/res/values-es-rES/strings.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/src/main/res/values-es-rES/strings.xml b/app/src/main/res/values-es-rES/strings.xml
index 3cd3c840..4e757c88 100644
--- a/app/src/main/res/values-es-rES/strings.xml
+++ b/app/src/main/res/values-es-rES/strings.xml
@@ -69,7 +69,7 @@
Descargar
Se descargarán todas las pistas de esta carpeta. Las pistas en las subcarpetas no se descargarán.
Descargar las pistas
- Indicar dónde se descarga la música
+ Indicar ubicación de descarga
Una vez que descargues una pista, la encontrarás aquí
No hay descargas
%1$s • %2$s elementos
From ca5a0698bb93744baa4c7eaf63d372726a92ff4d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jaime=20Garc=C3=ADa?=
<55400857+jaime-grj@users.noreply.github.com>
Date: Thu, 9 Oct 2025 04:32:22 +0200
Subject: [PATCH 3/6] feat: Show sampling rate and bit depth in downloads
---
.../main/java/com/cappielloantonio/tempo/model/Download.kt | 2 ++
.../tempo/ui/adapter/DownloadHorizontalAdapter.java | 2 +-
.../java/com/cappielloantonio/tempo/util/MappingUtil.java | 7 +++++++
3 files changed, 10 insertions(+), 1 deletion(-)
diff --git a/app/src/main/java/com/cappielloantonio/tempo/model/Download.kt b/app/src/main/java/com/cappielloantonio/tempo/model/Download.kt
index d5518adc..e5cc34b3 100644
--- a/app/src/main/java/com/cappielloantonio/tempo/model/Download.kt
+++ b/app/src/main/java/com/cappielloantonio/tempo/model/Download.kt
@@ -40,6 +40,8 @@ class Download(@PrimaryKey override val id: String) : Child(id) {
transcodedSuffix = child.transcodedSuffix
duration = child.duration
bitrate = child.bitrate
+ samplingRate = child.samplingRate
+ bitDepth = child.bitDepth
path = child.path
isVideo = child.isVideo
userRating = child.userRating
diff --git a/app/src/main/java/com/cappielloantonio/tempo/ui/adapter/DownloadHorizontalAdapter.java b/app/src/main/java/com/cappielloantonio/tempo/ui/adapter/DownloadHorizontalAdapter.java
index 5c065691..ff06f27a 100644
--- a/app/src/main/java/com/cappielloantonio/tempo/ui/adapter/DownloadHorizontalAdapter.java
+++ b/app/src/main/java/com/cappielloantonio/tempo/ui/adapter/DownloadHorizontalAdapter.java
@@ -191,7 +191,7 @@ public class DownloadHorizontalAdapter extends RecyclerView.Adapter
Date: Thu, 9 Oct 2025 04:52:40 +0200
Subject: [PATCH 4/6] fix: Re-add new equalizer settings that got lost
---
app/src/main/res/xml/global_preferences.xml | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/app/src/main/res/xml/global_preferences.xml b/app/src/main/res/xml/global_preferences.xml
index b558e20e..ad15349d 100644
--- a/app/src/main/res/xml/global_preferences.xml
+++ b/app/src/main/res/xml/global_preferences.xml
@@ -3,10 +3,16 @@
+
+
From a97a2d5b50f46661c5d57757cf0693114d276c33 Mon Sep 17 00:00:00 2001
From: eddyizm
Date: Wed, 8 Oct 2025 21:10:45 -0700
Subject: [PATCH 5/6] chore: removed play variant
---
.../tempo/service/MediaBrowserTree.kt | 497 ------------------
.../service/MediaLibraryServiceCallback.kt | 202 -------
.../tempo/service/MediaService.kt | 363 -------------
.../tempo/ui/fragment/ToolbarFragment.java | 67 ---
.../cappielloantonio/tempo/util/Flavors.java | 14 -
5 files changed, 1143 deletions(-)
delete mode 100644 app/src/play/java/com/cappielloantonio/tempo/service/MediaBrowserTree.kt
delete mode 100644 app/src/play/java/com/cappielloantonio/tempo/service/MediaLibraryServiceCallback.kt
delete mode 100644 app/src/play/java/com/cappielloantonio/tempo/service/MediaService.kt
delete mode 100644 app/src/play/java/com/cappielloantonio/tempo/ui/fragment/ToolbarFragment.java
delete mode 100644 app/src/play/java/com/cappielloantonio/tempo/util/Flavors.java
diff --git a/app/src/play/java/com/cappielloantonio/tempo/service/MediaBrowserTree.kt b/app/src/play/java/com/cappielloantonio/tempo/service/MediaBrowserTree.kt
deleted file mode 100644
index f88f9b6b..00000000
--- a/app/src/play/java/com/cappielloantonio/tempo/service/MediaBrowserTree.kt
+++ /dev/null
@@ -1,497 +0,0 @@
-package com.cappielloantonio.tempo.service
-
-import android.net.Uri
-import androidx.lifecycle.LifecycleOwner
-import androidx.media3.common.MediaItem
-import androidx.media3.common.MediaItem.SubtitleConfiguration
-import androidx.media3.common.MediaMetadata
-import androidx.media3.session.LibraryResult
-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
-
-object MediaBrowserTree {
-
- private lateinit var automotiveRepository: AutomotiveRepository
-
- private var treeNodes: MutableMap = mutableMapOf()
-
- private var isInitialized = false
-
- // Root
- private const val ROOT_ID = "[rootID]"
-
- // First level
- 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 RECENTLY_ADDED_ID = "[recentlyAddedID]"
- private const val RECENT_SONGS_ID = "[recentSongsID]"
- private const val MADE_FOR_YOU_ID = "[madeForYouID]"
- private const val STARRED_TRACKS_ID = "[starredTracksID]"
- 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]"
- 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 class MediaItemNode(val item: MediaItem) {
- private val children: MutableList = ArrayList()
-
- fun addChild(childID: String) {
- this.children.add(treeNodes[childID]!!.item)
- }
-
- fun getChildren(): ListenableFuture>> {
- val listenableFuture = SettableFuture.create>>()
- val libraryResult = LibraryResult.ofItemList(children, null)
-
- listenableFuture.set(libraryResult)
-
- return listenableFuture
- }
- }
-
- private fun buildMediaItem(
- title: String,
- mediaId: String,
- isPlayable: Boolean,
- isBrowsable: Boolean,
- mediaType: @MediaMetadata.MediaType Int,
- subtitleConfigurations: List = mutableListOf(),
- album: String? = null,
- artist: String? = null,
- genre: String? = null,
- 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()
-
- return MediaItem.Builder()
- .setMediaId(mediaId)
- .setSubtitleConfigurations(subtitleConfigurations)
- .setMediaMetadata(metadata)
- .setUri(sourceUri)
- .build()
- }
-
- fun initialize(automotiveRepository: AutomotiveRepository) {
- this.automotiveRepository = automotiveRepository
-
- if (isInitialized) return
-
- isInitialized = true
-
- // Root level
-
- treeNodes[ROOT_ID] =
- MediaItemNode(
- buildMediaItem(
- title = "Root Folder",
- mediaId = ROOT_ID,
- isPlayable = false,
- isBrowsable = true,
- mediaType = MediaMetadata.MEDIA_TYPE_FOLDER_MIXED
- )
- )
-
- // 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
- )
- )
-
- treeNodes[LAST_PLAYED_ID] =
- MediaItemNode(
- buildMediaItem(
- title = "Last played",
- mediaId = LAST_PLAYED_ID,
- isPlayable = false,
- isBrowsable = true,
- mediaType = MediaMetadata.MEDIA_TYPE_FOLDER_ALBUMS
- )
- )
-
- treeNodes[RECENTLY_ADDED_ID] =
- MediaItemNode(
- buildMediaItem(
- title = "Recently added",
- mediaId = RECENTLY_ADDED_ID,
- isPlayable = false,
- isBrowsable = true,
- 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] =
- 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,
- isPlayable = false,
- isBrowsable = true,
- 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",
- mediaId = PLAYLIST_ID,
- isPlayable = false,
- isBrowsable = true,
- 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",
- mediaId = PODCAST_ID,
- isPlayable = false,
- isBrowsable = true,
- mediaType = MediaMetadata.MEDIA_TYPE_FOLDER_PODCASTS
- )
- )
-
- treeNodes[RADIO_ID] =
- MediaItemNode(
- buildMediaItem(
- title = "Radio stations",
- mediaId = RADIO_ID,
- isPlayable = false,
- isBrowsable = true,
- mediaType = MediaMetadata.MEDIA_TYPE_FOLDER_RADIO_STATIONS
- )
- )
-
- treeNodes[OTHER_ID]!!.addChild(PODCAST_ID)
- treeNodes[OTHER_ID]!!.addChild(RADIO_ID)
- }
-
- fun getRootItem(): MediaItem {
- return treeNodes[ROOT_ID]!!.item
- }
-
- fun getChildren(
- id: String
- ): ListenableFuture>> {
- 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)
- 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(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
- )
- }
-
- if (id.startsWith(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
- )
- )
- }
-
- if (id.startsWith(PLAYLIST_ID)) {
- return automotiveRepository.getPlaylistSongs(
- id.removePrefix(
- PLAYLIST_ID
- )
- )
- }
-
- if (id.startsWith(ALBUM_ID)) {
- return automotiveRepository.getAlbumTracks(
- id.removePrefix(
- ALBUM_ID
- )
- )
- }
-
- if (id.startsWith(ARTIST_ID)) {
- return automotiveRepository.getArtistAlbum(
- ALBUM_ID,
- id.removePrefix(
- ARTIST_ID
- )
- )
- }
-
- return Futures.immediateFuture(LibraryResult.ofError(LibraryResult.RESULT_ERROR_BAD_VALUE))
- }
- }
- }
-
- // https://github.com/androidx/media/issues/156
- fun getItems(mediaItems: List): List {
- val updatedMediaItems = ArrayList()
-
- mediaItems.forEach {
- if (it.localConfiguration?.uri != null) {
- updatedMediaItems.add(it)
- } else {
- val sessionMediaItem = automotiveRepository.getSessionMediaItem(it.mediaId)
-
- if (sessionMediaItem != null) {
- var toAdd = automotiveRepository.getMetadatas(sessionMediaItem.timestamp!!)
- val index = toAdd.indexOfFirst { mediaItem -> mediaItem.mediaId == it.mediaId }
-
- toAdd = toAdd.subList(index, toAdd.size)
-
- updatedMediaItems.addAll(toAdd)
- }
- }
- }
-
- return updatedMediaItems
- }
-
- fun search(query: String): ListenableFuture>> {
- return automotiveRepository.search(
- query,
- ALBUM_ID,
- ARTIST_ID
- )
- }
-}
diff --git a/app/src/play/java/com/cappielloantonio/tempo/service/MediaLibraryServiceCallback.kt b/app/src/play/java/com/cappielloantonio/tempo/service/MediaLibraryServiceCallback.kt
deleted file mode 100644
index 099ae672..00000000
--- a/app/src/play/java/com/cappielloantonio/tempo/service/MediaLibraryServiceCallback.kt
+++ /dev/null
@@ -1,202 +0,0 @@
-package com.cappielloantonio.tempo.service
-
-import android.content.Context
-import android.os.Bundle
-import androidx.annotation.OptIn
-import androidx.media3.common.MediaItem
-import androidx.media3.common.Player
-import androidx.media3.common.util.UnstableApi
-import androidx.media3.session.CommandButton
-import androidx.media3.session.LibraryResult
-import androidx.media3.session.MediaLibraryService
-import androidx.media3.session.MediaSession
-import androidx.media3.session.SessionCommand
-import androidx.media3.session.SessionResult
-import com.cappielloantonio.tempo.R
-import com.cappielloantonio.tempo.repository.AutomotiveRepository
-import com.google.common.collect.ImmutableList
-import com.google.common.util.concurrent.Futures
-import com.google.common.util.concurrent.ListenableFuture
-
-open class MediaLibrarySessionCallback(
- context: Context,
- automotiveRepository: AutomotiveRepository
-) :
- MediaLibraryService.MediaLibrarySession.Callback {
-
- init {
- MediaBrowserTree.initialize(automotiveRepository)
- }
-
- private val shuffleCommandButtons: List = listOf(
- CommandButton.Builder()
- .setDisplayName(context.getString(R.string.exo_controls_shuffle_on_description))
- .setSessionCommand(
- SessionCommand(
- CUSTOM_COMMAND_TOGGLE_SHUFFLE_MODE_ON, Bundle.EMPTY
- )
- ).setIconResId(R.drawable.exo_icon_shuffle_off).build(),
-
- CommandButton.Builder()
- .setDisplayName(context.getString(R.string.exo_controls_shuffle_off_description))
- .setSessionCommand(
- SessionCommand(
- CUSTOM_COMMAND_TOGGLE_SHUFFLE_MODE_OFF, Bundle.EMPTY
- )
- ).setIconResId(R.drawable.exo_icon_shuffle_on).build()
- )
-
- private val repeatCommandButtons: List = listOf(
- CommandButton.Builder()
- .setDisplayName(context.getString(R.string.exo_controls_repeat_off_description))
- .setSessionCommand(SessionCommand(CUSTOM_COMMAND_TOGGLE_REPEAT_MODE_OFF, Bundle.EMPTY))
- .setIconResId(R.drawable.exo_icon_repeat_off)
- .build(),
- CommandButton.Builder()
- .setDisplayName(context.getString(R.string.exo_controls_repeat_one_description))
- .setSessionCommand(SessionCommand(CUSTOM_COMMAND_TOGGLE_REPEAT_MODE_ONE, Bundle.EMPTY))
- .setIconResId(R.drawable.exo_icon_repeat_one)
- .build(),
- CommandButton.Builder()
- .setDisplayName(context.getString(R.string.exo_controls_repeat_all_description))
- .setSessionCommand(SessionCommand(CUSTOM_COMMAND_TOGGLE_REPEAT_MODE_ALL, Bundle.EMPTY))
- .setIconResId(R.drawable.exo_icon_repeat_all)
- .build()
- )
-
- private val customLayoutCommandButtons: List =
- shuffleCommandButtons + repeatCommandButtons
-
- @OptIn(UnstableApi::class)
- val mediaNotificationSessionCommands =
- MediaSession.ConnectionResult.DEFAULT_SESSION_AND_LIBRARY_COMMANDS.buildUpon()
- .also { builder ->
- (shuffleCommandButtons + repeatCommandButtons).forEach { commandButton ->
- commandButton.sessionCommand?.let { builder.add(it) }
- }
- }.build()
-
- fun buildCustomLayout(player: Player): ImmutableList {
- val shuffle = shuffleCommandButtons[if (player.shuffleModeEnabled) 1 else 0]
- val repeat = when (player.repeatMode) {
- Player.REPEAT_MODE_ONE -> repeatCommandButtons[1]
- Player.REPEAT_MODE_ALL -> repeatCommandButtons[2]
- else -> repeatCommandButtons[0]
- }
- return ImmutableList.of(shuffle, repeat)
- }
-
- @OptIn(UnstableApi::class)
- override fun onConnect(
- session: MediaSession, controller: MediaSession.ControllerInfo
- ): MediaSession.ConnectionResult {
- if (session.isMediaNotificationController(controller) || session.isAutomotiveController(
- controller
- ) || session.isAutoCompanionController(controller)
- ) {
- val customLayout = buildCustomLayout(session.player)
-
- return MediaSession.ConnectionResult.AcceptedResultBuilder(session)
- .setAvailableSessionCommands(mediaNotificationSessionCommands)
- .setCustomLayout(customLayout).build()
- }
-
- return MediaSession.ConnectionResult.AcceptedResultBuilder(session).build()
- }
-
- @OptIn(UnstableApi::class)
- override fun onCustomCommand(
- session: MediaSession,
- controller: MediaSession.ControllerInfo,
- customCommand: SessionCommand,
- args: Bundle
- ): ListenableFuture {
- when (customCommand.customAction) {
- CUSTOM_COMMAND_TOGGLE_SHUFFLE_MODE_ON -> session.player.shuffleModeEnabled = true
- CUSTOM_COMMAND_TOGGLE_SHUFFLE_MODE_OFF -> session.player.shuffleModeEnabled = false
- CUSTOM_COMMAND_TOGGLE_REPEAT_MODE_OFF,
- CUSTOM_COMMAND_TOGGLE_REPEAT_MODE_ALL,
- CUSTOM_COMMAND_TOGGLE_REPEAT_MODE_ONE -> {
- val nextMode = when (session.player.repeatMode) {
- Player.REPEAT_MODE_ONE -> Player.REPEAT_MODE_ALL
- Player.REPEAT_MODE_OFF -> Player.REPEAT_MODE_ONE
- else -> Player.REPEAT_MODE_OFF
- }
- session.player.repeatMode = nextMode
- }
- else -> return Futures.immediateFuture(SessionResult(SessionResult.RESULT_ERROR_NOT_SUPPORTED))
- }
-
- session.setCustomLayout(
- session.mediaNotificationControllerInfo!!,
- buildCustomLayout(session.player)
- )
-
- return Futures.immediateFuture(SessionResult(SessionResult.RESULT_SUCCESS))
- }
-
- override fun onGetLibraryRoot(
- session: MediaLibraryService.MediaLibrarySession,
- browser: MediaSession.ControllerInfo,
- params: MediaLibraryService.LibraryParams?
- ): ListenableFuture> {
- return Futures.immediateFuture(LibraryResult.ofItem(MediaBrowserTree.getRootItem(), params))
- }
-
- override fun onGetChildren(
- session: MediaLibraryService.MediaLibrarySession,
- browser: MediaSession.ControllerInfo,
- parentId: String,
- page: Int,
- pageSize: Int,
- params: MediaLibraryService.LibraryParams?
- ): ListenableFuture>> {
- return MediaBrowserTree.getChildren(parentId)
- }
-
- override fun onAddMediaItems(
- mediaSession: MediaSession,
- controller: MediaSession.ControllerInfo,
- mediaItems: List
- ): ListenableFuture> {
- return super.onAddMediaItems(
- mediaSession,
- controller,
- MediaBrowserTree.getItems(mediaItems)
- )
- }
-
- override fun onSearch(
- session: MediaLibraryService.MediaLibrarySession,
- browser: MediaSession.ControllerInfo,
- query: String,
- params: MediaLibraryService.LibraryParams?
- ): ListenableFuture> {
- session.notifySearchResultChanged(browser, query, 60, params)
- return Futures.immediateFuture(LibraryResult.ofVoid())
- }
-
- override fun onGetSearchResult(
- session: MediaLibraryService.MediaLibrarySession,
- browser: MediaSession.ControllerInfo,
- query: String,
- page: Int,
- pageSize: Int,
- params: MediaLibraryService.LibraryParams?
- ): ListenableFuture>> {
- return MediaBrowserTree.search(query)
- }
-
- companion object {
- private const val CUSTOM_COMMAND_TOGGLE_SHUFFLE_MODE_ON =
- "android.media3.session.demo.SHUFFLE_ON"
- private const val CUSTOM_COMMAND_TOGGLE_SHUFFLE_MODE_OFF =
- "android.media3.session.demo.SHUFFLE_OFF"
- private const val CUSTOM_COMMAND_TOGGLE_REPEAT_MODE_OFF =
- "android.media3.session.demo.REPEAT_OFF"
- private const val CUSTOM_COMMAND_TOGGLE_REPEAT_MODE_ONE =
- "android.media3.session.demo.REPEAT_ONE"
- private const val CUSTOM_COMMAND_TOGGLE_REPEAT_MODE_ALL =
- "android.media3.session.demo.REPEAT_ALL"
- }
-}
\ No newline at end of file
diff --git a/app/src/play/java/com/cappielloantonio/tempo/service/MediaService.kt b/app/src/play/java/com/cappielloantonio/tempo/service/MediaService.kt
deleted file mode 100644
index a73a7c33..00000000
--- a/app/src/play/java/com/cappielloantonio/tempo/service/MediaService.kt
+++ /dev/null
@@ -1,363 +0,0 @@
-package com.cappielloantonio.tempo.service
-
-import android.app.PendingIntent.FLAG_IMMUTABLE
-import android.app.PendingIntent.FLAG_UPDATE_CURRENT
-import android.app.TaskStackBuilder
-import android.content.Intent
-import android.os.Binder
-import android.os.IBinder
-import android.os.Handler
-import android.os.Looper
-import androidx.media3.cast.CastPlayer
-import androidx.media3.cast.SessionAvailabilityListener
-import androidx.media3.common.AudioAttributes
-import androidx.media3.common.C
-import androidx.media3.common.MediaItem
-import androidx.media3.common.Player
-import androidx.media3.common.Tracks
-import androidx.media3.common.util.UnstableApi
-import androidx.media3.exoplayer.DefaultLoadControl
-import androidx.media3.exoplayer.ExoPlayer
-import androidx.media3.session.MediaLibraryService
-import androidx.media3.session.MediaSession.ControllerInfo
-import com.cappielloantonio.tempo.repository.AutomotiveRepository
-import com.cappielloantonio.tempo.ui.activity.MainActivity
-import com.cappielloantonio.tempo.util.Constants
-import com.cappielloantonio.tempo.util.DownloadUtil
-import com.cappielloantonio.tempo.util.DynamicMediaSourceFactory
-import com.cappielloantonio.tempo.util.Preferences
-import com.cappielloantonio.tempo.util.ReplayGainUtil
-import com.cappielloantonio.tempo.widget.WidgetUpdateManager
-import com.google.android.gms.cast.framework.CastContext
-import com.google.android.gms.common.ConnectionResult
-import com.google.android.gms.common.GoogleApiAvailability
-
-@UnstableApi
-class MediaService : MediaLibraryService(), SessionAvailabilityListener {
- private lateinit var automotiveRepository: AutomotiveRepository
- private lateinit var player: ExoPlayer
- private lateinit var castPlayer: CastPlayer
- private lateinit var mediaLibrarySession: MediaLibrarySession
- private lateinit var librarySessionCallback: MediaLibrarySessionCallback
- lateinit var equalizerManager: EqualizerManager
-
- inner class LocalBinder : Binder() {
- fun getEqualizerManager(): EqualizerManager {
- return this@MediaService.equalizerManager
- }
- }
-
- private val binder = LocalBinder()
-
- companion object {
- const val ACTION_BIND_EQUALIZER = "com.cappielloantonio.tempo.service.BIND_EQUALIZER"
- }
- private val widgetUpdateHandler = Handler(Looper.getMainLooper())
- private var widgetUpdateScheduled = false
- private val widgetUpdateRunnable = object : Runnable {
- override fun run() {
- if (!player.isPlaying) {
- widgetUpdateScheduled = false
- return
- }
- updateWidget()
- widgetUpdateHandler.postDelayed(this, WIDGET_UPDATE_INTERVAL_MS)
- }
- }
-
- override fun onCreate() {
- super.onCreate()
-
- initializeRepository()
- initializePlayer()
- initializeCastPlayer()
- initializeMediaLibrarySession()
- initializePlayerListener()
- initializeEqualizerManager()
-
- setPlayer(
- null,
- if (this::castPlayer.isInitialized && castPlayer.isCastSessionAvailable) castPlayer else player
- )
- }
-
- override fun onGetSession(controllerInfo: ControllerInfo): MediaLibrarySession {
- return mediaLibrarySession
- }
-
- override fun onTaskRemoved(rootIntent: Intent?) {
- val player = mediaLibrarySession.player
-
- if (!player.playWhenReady || player.mediaItemCount == 0) {
- stopSelf()
- }
- }
-
- override fun onDestroy() {
- equalizerManager.release()
- stopWidgetUpdates()
- releasePlayer()
- super.onDestroy()
- }
-
- override fun onBind(intent: Intent?): IBinder? {
- // Check if the intent is for our custom equalizer binder
- if (intent?.action == ACTION_BIND_EQUALIZER) {
- return binder
- }
- // Otherwise, handle it as a normal MediaLibraryService connection
- return super.onBind(intent)
- }
-
- private fun initializeRepository() {
- automotiveRepository = AutomotiveRepository()
- }
-
- private fun initializePlayer() {
- player = ExoPlayer.Builder(this)
- .setRenderersFactory(getRenderersFactory())
- .setMediaSourceFactory(DynamicMediaSourceFactory(this))
- .setAudioAttributes(AudioAttributes.DEFAULT, true)
- .setHandleAudioBecomingNoisy(true)
- .setWakeMode(C.WAKE_MODE_NETWORK)
- .setLoadControl(initializeLoadControl())
- .build()
-
- player.shuffleModeEnabled = Preferences.isShuffleModeEnabled()
- player.repeatMode = Preferences.getRepeatMode()
- }
-
- private fun initializeEqualizerManager() {
- equalizerManager = EqualizerManager()
- val audioSessionId = player.audioSessionId
- if (equalizerManager.attachToSession(audioSessionId)) {
- val enabled = Preferences.isEqualizerEnabled()
- equalizerManager.setEnabled(enabled)
-
- val bands = equalizerManager.getNumberOfBands()
- val savedLevels = Preferences.getEqualizerBandLevels(bands)
- for (i in 0 until bands) {
- equalizerManager.setBandLevel(i.toShort(), savedLevels[i])
- }
- }
- }
-
- private fun initializeCastPlayer() {
- if (GoogleApiAvailability.getInstance()
- .isGooglePlayServicesAvailable(this) == ConnectionResult.SUCCESS
- ) {
- castPlayer = CastPlayer(CastContext.getSharedInstance(this))
- castPlayer.setSessionAvailabilityListener(this)
- }
- }
-
- private fun initializeMediaLibrarySession() {
- val sessionActivityPendingIntent =
- TaskStackBuilder.create(this).run {
- addNextIntent(Intent(this@MediaService, MainActivity::class.java))
- getPendingIntent(0, FLAG_IMMUTABLE or FLAG_UPDATE_CURRENT)
- }
-
- librarySessionCallback = createLibrarySessionCallback()
- mediaLibrarySession =
- MediaLibrarySession.Builder(this, player, librarySessionCallback)
- .setSessionActivity(sessionActivityPendingIntent)
- .build()
- }
-
- private fun createLibrarySessionCallback(): MediaLibrarySessionCallback {
- return MediaLibrarySessionCallback(this, automotiveRepository)
- }
-
- private fun initializePlayerListener() {
- player.addListener(object : Player.Listener {
- override fun onMediaItemTransition(mediaItem: MediaItem?, reason: Int) {
- if (mediaItem == null) return
-
- if (reason == Player.MEDIA_ITEM_TRANSITION_REASON_SEEK || reason == Player.MEDIA_ITEM_TRANSITION_REASON_AUTO) {
- MediaManager.setLastPlayedTimestamp(mediaItem)
- }
- updateWidget()
- }
-
- override fun onTracksChanged(tracks: Tracks) {
- ReplayGainUtil.setReplayGain(player, tracks)
- val currentMediaItem = player.currentMediaItem
- if (currentMediaItem != null && currentMediaItem.mediaMetadata.extras != null) {
- MediaManager.scrobble(currentMediaItem, false)
- }
-
- if (player.currentMediaItemIndex + 1 == player.mediaItemCount)
- MediaManager.continuousPlay(player.currentMediaItem)
- }
-
- override fun onIsPlayingChanged(isPlaying: Boolean) {
- if (!isPlaying) {
- MediaManager.setPlayingPausedTimestamp(
- player.currentMediaItem,
- player.currentPosition
- )
- } else {
- MediaManager.scrobble(player.currentMediaItem, false)
- }
- if (isPlaying) {
- scheduleWidgetUpdates()
- } else {
- stopWidgetUpdates()
- }
- updateWidget()
- }
-
- override fun onPlaybackStateChanged(playbackState: Int) {
- super.onPlaybackStateChanged(playbackState)
-
- if (!player.hasNextMediaItem() &&
- playbackState == Player.STATE_ENDED &&
- player.mediaMetadata.extras?.getString("type") == Constants.MEDIA_TYPE_MUSIC
- ) {
- MediaManager.scrobble(player.currentMediaItem, true)
- MediaManager.saveChronology(player.currentMediaItem)
- }
- updateWidget()
- }
-
- override fun onPositionDiscontinuity(
- oldPosition: Player.PositionInfo,
- newPosition: Player.PositionInfo,
- reason: Int
- ) {
- super.onPositionDiscontinuity(oldPosition, newPosition, reason)
-
- if (reason == Player.DISCONTINUITY_REASON_AUTO_TRANSITION) {
- if (oldPosition.mediaItem?.mediaMetadata?.extras?.getString("type") == Constants.MEDIA_TYPE_MUSIC) {
- MediaManager.scrobble(oldPosition.mediaItem, true)
- MediaManager.saveChronology(oldPosition.mediaItem)
- }
-
- if (newPosition.mediaItem?.mediaMetadata?.extras?.getString("type") == Constants.MEDIA_TYPE_MUSIC) {
- MediaManager.setLastPlayedTimestamp(newPosition.mediaItem)
- }
- }
- }
-
- override fun onShuffleModeEnabledChanged(shuffleModeEnabled: Boolean) {
- Preferences.setShuffleModeEnabled(shuffleModeEnabled)
- mediaLibrarySession.setCustomLayout(
- librarySessionCallback.buildCustomLayout(player)
- )
- }
-
- override fun onRepeatModeChanged(repeatMode: Int) {
- Preferences.setRepeatMode(repeatMode)
- mediaLibrarySession.setCustomLayout(
- librarySessionCallback.buildCustomLayout(player)
- )
- }
- })
- if (player.isPlaying) {
- scheduleWidgetUpdates()
- }
- }
-
- private fun updateWidget() {
- val mi = player.currentMediaItem
- val title = mi?.mediaMetadata?.title?.toString()
- ?: mi?.mediaMetadata?.extras?.getString("title")
- val artist = mi?.mediaMetadata?.artist?.toString()
- ?: mi?.mediaMetadata?.extras?.getString("artist")
- val album = mi?.mediaMetadata?.albumTitle?.toString()
- ?: mi?.mediaMetadata?.extras?.getString("album")
- val coverId = mi?.mediaMetadata?.extras?.getString("coverArtId")
-
- val position = player.currentPosition.takeIf { it != C.TIME_UNSET } ?: 0L
- val duration = player.duration.takeIf { it != C.TIME_UNSET } ?: 0L
- WidgetUpdateManager.updateFromState(
- this,
- title ?: "",
- artist ?: "",
- album ?: "",
- coverId,
- player.isPlaying,
- player.shuffleModeEnabled,
- player.repeatMode,
- position,
- duration
- )
- }
-
- private fun scheduleWidgetUpdates() {
- if (widgetUpdateScheduled) return
- widgetUpdateHandler.postDelayed(widgetUpdateRunnable, WIDGET_UPDATE_INTERVAL_MS)
- widgetUpdateScheduled = true
- }
-
- private fun stopWidgetUpdates() {
- if (!widgetUpdateScheduled) return
- widgetUpdateHandler.removeCallbacks(widgetUpdateRunnable)
- widgetUpdateScheduled = false
- }
-
- private fun initializeLoadControl(): DefaultLoadControl {
- return DefaultLoadControl.Builder()
- .setBufferDurationsMs(
- (DefaultLoadControl.DEFAULT_MIN_BUFFER_MS * Preferences.getBufferingStrategy()).toInt(),
- (DefaultLoadControl.DEFAULT_MAX_BUFFER_MS * Preferences.getBufferingStrategy()).toInt(),
- DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_MS,
- DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS
- )
- .build()
- }
-
- private fun getQueueFromPlayer(player: Player): List {
- val queue = mutableListOf()
- for (i in 0 until player.mediaItemCount) {
- queue.add(player.getMediaItemAt(i))
- }
- return queue
- }
-
- private fun setPlayer(oldPlayer: Player?, newPlayer: Player) {
- if (oldPlayer === newPlayer) return
-
- oldPlayer?.stop()
- mediaLibrarySession.player = newPlayer
- }
-
- private fun releasePlayer() {
- if (this::castPlayer.isInitialized) castPlayer.setSessionAvailabilityListener(null)
- if (this::castPlayer.isInitialized) castPlayer.release()
- player.release()
- mediaLibrarySession.release()
- automotiveRepository.deleteMetadata()
- }
-
- private fun getRenderersFactory() = DownloadUtil.buildRenderersFactory(this, false)
-
- override fun onCastSessionAvailable() {
- val currentQueue = getQueueFromPlayer(player)
- val currentIndex = player.currentMediaItemIndex
- val currentPosition = player.currentPosition
- val isPlaying = player.playWhenReady
-
- setPlayer(player, castPlayer)
-
- castPlayer.setMediaItems(currentQueue, currentIndex, currentPosition)
- castPlayer.playWhenReady = isPlaying
- castPlayer.prepare()
- }
-
- override fun onCastSessionUnavailable() {
- val currentQueue = getQueueFromPlayer(castPlayer)
- val currentIndex = castPlayer.currentMediaItemIndex
- val currentPosition = castPlayer.currentPosition
- val isPlaying = castPlayer.playWhenReady
-
- setPlayer(castPlayer, player)
-
- player.setMediaItems(currentQueue, currentIndex, currentPosition)
- player.playWhenReady = isPlaying
- player.prepare()
- }
-}
-
-private const val WIDGET_UPDATE_INTERVAL_MS = 1000L
diff --git a/app/src/play/java/com/cappielloantonio/tempo/ui/fragment/ToolbarFragment.java b/app/src/play/java/com/cappielloantonio/tempo/ui/fragment/ToolbarFragment.java
deleted file mode 100644
index d3f36bb7..00000000
--- a/app/src/play/java/com/cappielloantonio/tempo/ui/fragment/ToolbarFragment.java
+++ /dev/null
@@ -1,67 +0,0 @@
-package com.cappielloantonio.tempo.ui.fragment;
-
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.fragment.app.Fragment;
-import androidx.media3.common.util.UnstableApi;
-
-import com.cappielloantonio.tempo.R;
-import com.cappielloantonio.tempo.databinding.FragmentToolbarBinding;
-import com.cappielloantonio.tempo.ui.activity.MainActivity;
-import com.google.android.gms.cast.framework.CastButtonFactory;
-
-@UnstableApi
-public class ToolbarFragment extends Fragment {
- private static final String TAG = "ToolbarFragment";
-
- private FragmentToolbarBinding bind;
- private MainActivity activity;
-
- public ToolbarFragment() {
- // Required empty public constructor
- }
-
- @Override
- public void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setHasOptionsMenu(true);
- }
-
- @Override
- public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
- super.onCreateOptionsMenu(menu, inflater);
- inflater.inflate(R.menu.main_page_menu, menu);
- CastButtonFactory.setUpMediaRouteButton(requireContext(), menu, R.id.media_route_menu_item);
- }
-
- @Override
- public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- activity = (MainActivity) getActivity();
-
- bind = FragmentToolbarBinding.inflate(inflater, container, false);
- View view = bind.getRoot();
-
- return view;
- }
-
- @Override
- public boolean onOptionsItemSelected(@NonNull MenuItem item) {
- if (item.getItemId() == R.id.action_search) {
- activity.navController.navigate(R.id.searchFragment);
- return true;
- } else if (item.getItemId() == R.id.action_settings) {
- activity.navController.navigate(R.id.settingsFragment);
- return true;
- }
-
- return false;
- }
-}
\ No newline at end of file
diff --git a/app/src/play/java/com/cappielloantonio/tempo/util/Flavors.java b/app/src/play/java/com/cappielloantonio/tempo/util/Flavors.java
deleted file mode 100644
index 4bed2921..00000000
--- a/app/src/play/java/com/cappielloantonio/tempo/util/Flavors.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package com.cappielloantonio.tempo.util;
-
-import android.content.Context;
-
-import com.google.android.gms.cast.framework.CastContext;
-import com.google.android.gms.common.ConnectionResult;
-import com.google.android.gms.common.GoogleApiAvailability;
-
-public class Flavors {
- public static void initializeCastContext(Context context) {
- if (GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(context) == ConnectionResult.SUCCESS)
- CastContext.getSharedInstance(context);
- }
-}
From 9f61d70fca072642bb5e4e8394c21a30a574b03b Mon Sep 17 00:00:00 2001
From: eddyizm
Date: Wed, 8 Oct 2025 21:26:06 -0700
Subject: [PATCH 6/6] fix: updating release workflow to account for the 32/64
bit builds and degoogled variant
---
.github/workflows/github_release.yml | 62 +++++++++++++++++++++++-----
1 file changed, 51 insertions(+), 11 deletions(-)
diff --git a/.github/workflows/github_release.yml b/.github/workflows/github_release.yml
index 9bc2d851..7591281f 100644
--- a/.github/workflows/github_release.yml
+++ b/.github/workflows/github_release.yml
@@ -35,12 +35,18 @@ jobs:
echo "BUILD_TOOL_VERSION=$BUILD_TOOL_VERSION" >> $GITHUB_ENV
echo Last build tool version is: $BUILD_TOOL_VERSION
- - name: Build APK
+ - name: Build All APKs
id: build
- run: bash ./gradlew assembleTempoRelease
+ run: |
+ # Build release variants
+ bash ./gradlew assembleTempoRelease
+ bash ./gradlew assembleNotquitemyRelease
+ # Build debug variants
+ bash ./gradlew assembleTempoDebug
+ bash ./gradlew assembleNotquitemyDebug
- - name: Sign APK
- id: sign_apk
+ - name: Sign Tempo Release APKs
+ id: sign_tempo_release
uses: r0adkll/sign-android-release@v1
with:
releaseDirectory: app/build/outputs/apk/tempo/release
@@ -51,11 +57,17 @@ jobs:
env:
BUILD_TOOLS_VERSION: ${{ env.BUILD_TOOL_VERSION }}
- - name: Make artifact
- uses: actions/upload-artifact@v4
+ - name: Sign NotQuiteMy Release APKs
+ id: sign_notquitemy_release
+ uses: r0adkll/sign-android-release@v1
with:
- name: app-release-signed
- path: ${{steps.sign_apk.outputs.signedReleaseFile}}
+ releaseDirectory: app/build/outputs/apk/notquitemy/release
+ signingKeyBase64: ${{ secrets.KEYSTORE_BASE64 }}
+ alias: ${{ secrets.KEY_ALIAS_GITHUB }}
+ keyStorePassword: ${{ secrets.KEYSTORE_PASSWORD }}
+ keyPassword: ${{ secrets.KEY_PASSWORD_GITHUB }}
+ env:
+ BUILD_TOOLS_VERSION: ${{ env.BUILD_TOOL_VERSION }}
- name: Create Release
id: create_release
@@ -67,12 +79,40 @@ jobs:
env:
GITHUB_TOKEN: ${{ github.token }}
- - name: Upload APK
+ - name: Upload Release APKs
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ github.token }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
- asset_path: ${{steps.sign_apk.outputs.signedReleaseFile}}
+ asset_path: ${{steps.sign_tempo_release.outputs.signedReleaseFile}}
asset_name: app-tempo-release.apk
- asset_content_type: application/zip
+ asset_content_type: application/vnd.android.package-archive
+
+ - name: Upload NotQuiteMy Release APK
+ uses: actions/upload-release-asset@v1
+ env:
+ GITHUB_TOKEN: ${{ github.token }}
+ with:
+ upload_url: ${{ steps.create_release.outputs.upload_url }}
+ asset_path: ${{steps.sign_notquitemy_release.outputs.signedReleaseFile}}
+ asset_name: app-notquitemy-release.apk
+ asset_content_type: application/vnd.android.package-archive
+
+ - name: Upload Debug APKs as artifacts
+ uses: actions/upload-artifact@v4
+ with:
+ name: debug-apks
+ path: |
+ app/build/outputs/apk/tempo/debug/
+ app/build/outputs/apk/notquitemy/debug/
+ retention-days: 30
+
+ - name: Upload Release APKs as artifacts
+ uses: actions/upload-artifact@v4
+ with:
+ name: release-apks
+ path: |
+ ${{steps.sign_tempo_release.outputs.signedReleaseFile}}
+ ${{steps.sign_notquitemy_release.outputs.signedReleaseFile}}
+ retention-days: 30
\ No newline at end of file