From 8235f6a80822593b66643eff837027be65bec47d Mon Sep 17 00:00:00 2001 From: Chamika Date: Sat, 2 May 2026 12:33:14 +0100 Subject: [PATCH 1/3] Add sync library button to settings screen Exposes a 'Sync library' preference that lets users manually trigger a library sync from settings. Shows last sync date/time as the subtitle, updates it on success, and disables the preference while sync is in progress to prevent double-taps. Also fixes the sync command to return a proper error result and show a failure toast when sync fails. Co-Authored-By: Claude Sonnet 4.6 --- .../dashtune/DashTuneSessionCallback.kt | 5 +- .../dashtune/settings/SettingsFragment.kt | 60 +++++++++++++++++++ automotive/src/main/res/values/strings.xml | 4 ++ automotive/src/main/res/xml/preferences.xml | 4 ++ 4 files changed, 72 insertions(+), 1 deletion(-) diff --git a/automotive/src/main/java/com/chamika/dashtune/DashTuneSessionCallback.kt b/automotive/src/main/java/com/chamika/dashtune/DashTuneSessionCallback.kt index 3c40ec1..99db671 100644 --- a/automotive/src/main/java/com/chamika/dashtune/DashTuneSessionCallback.kt +++ b/automotive/src/main/java/com/chamika/dashtune/DashTuneSessionCallback.kt @@ -423,8 +423,11 @@ class DashTuneSessionCallback( } (session as MediaLibraryService.MediaLibrarySession).notifyChildrenChanged(ROOT_ID, 4, null) android.widget.Toast.makeText(service, R.string.library_synced, android.widget.Toast.LENGTH_SHORT).show() + SessionResult(SessionResult.RESULT_SUCCESS) + } else { + android.widget.Toast.makeText(service, R.string.sync_failed, android.widget.Toast.LENGTH_SHORT).show() + SessionResult(SessionError.ERROR_UNKNOWN) } - SessionResult(SessionResult.RESULT_SUCCESS) } } } diff --git a/automotive/src/main/java/com/chamika/dashtune/settings/SettingsFragment.kt b/automotive/src/main/java/com/chamika/dashtune/settings/SettingsFragment.kt index 97b047b..f20a986 100644 --- a/automotive/src/main/java/com/chamika/dashtune/settings/SettingsFragment.kt +++ b/automotive/src/main/java/com/chamika/dashtune/settings/SettingsFragment.kt @@ -1,23 +1,35 @@ package com.chamika.dashtune.settings import android.app.AlertDialog +import android.content.ComponentName import android.content.Intent import android.os.Bundle import android.widget.Toast import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.lifecycleScope +import androidx.media3.session.MediaController +import androidx.media3.session.SessionCommand +import androidx.media3.session.SessionResult +import androidx.media3.session.SessionToken import androidx.preference.MultiSelectListPreference import androidx.preference.Preference import androidx.preference.PreferenceFragmentCompat +import androidx.preference.PreferenceManager +import com.chamika.dashtune.DashTuneMusicService +import com.chamika.dashtune.DashTuneSessionCallback.Companion.SYNC_COMMAND import com.chamika.dashtune.R import com.chamika.dashtune.signin.SignInActivity +import com.google.common.util.concurrent.ListenableFuture import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.launch +import java.text.DateFormat +import java.util.Date @AndroidEntryPoint class SettingsFragment : PreferenceFragmentCompat() { private lateinit var viewModel: SettingsViewModel + private lateinit var controllerFuture: ListenableFuture override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { setPreferencesFromResource(R.xml.preferences, rootKey) @@ -41,6 +53,28 @@ class SettingsFragment : PreferenceFragmentCompat() { } } + val syncPref = findPreference("sync_library") + syncPref?.summary = lastSyncSummary() + syncPref?.setOnPreferenceClickListener { + syncPref.isEnabled = false + val controller = if (controllerFuture.isDone) controllerFuture.get() else null + if (controller == null) { + syncPref.isEnabled = true + return@setOnPreferenceClickListener true + } + val resultFuture = controller.sendCustomCommand( + SessionCommand(SYNC_COMMAND, Bundle.EMPTY), Bundle.EMPTY + ) + resultFuture.addListener({ + val result = resultFuture.get() + if (result.resultCode == SessionResult.RESULT_SUCCESS) { + syncPref.summary = lastSyncSummary() + } + syncPref.isEnabled = true + }, requireActivity().mainExecutor) + true + } + findPreference("sign_out")?.setOnPreferenceClickListener { AlertDialog.Builder(requireContext()) .setMessage(R.string.sign_out_confirmation) @@ -57,4 +91,30 @@ class SettingsFragment : PreferenceFragmentCompat() { true } } + + override fun onStart() { + super.onStart() + val token = SessionToken( + requireContext(), + ComponentName(requireContext(), DashTuneMusicService::class.java) + ) + controllerFuture = MediaController.Builder(requireContext(), token).buildAsync() + } + + override fun onStop() { + MediaController.releaseFuture(controllerFuture) + super.onStop() + } + + private fun lastSyncSummary(): String { + val lastSync = PreferenceManager.getDefaultSharedPreferences(requireContext()) + .getLong("last_sync_timestamp", 0L) + return if (lastSync > 0) { + val formatted = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.SHORT) + .format(Date(lastSync)) + getString(R.string.sync_library_last_synced, formatted) + } else { + getString(R.string.sync_library_never) + } + } } diff --git a/automotive/src/main/res/values/strings.xml b/automotive/src/main/res/values/strings.xml index 26ed54f..7fbe5fb 100644 --- a/automotive/src/main/res/values/strings.xml +++ b/automotive/src/main/res/values/strings.xml @@ -36,4 +36,8 @@ Select at most 4 categories Cache favourites Pre-download all favourite songs for offline playback + Sync library + Never synced + Last synced: %s + Sync failed diff --git a/automotive/src/main/res/xml/preferences.xml b/automotive/src/main/res/xml/preferences.xml index 21400ca..8f650a2 100644 --- a/automotive/src/main/res/xml/preferences.xml +++ b/automotive/src/main/res/xml/preferences.xml @@ -39,6 +39,10 @@ android:summary="@string/browse_categories_summary" android:title="@string/browse_categories" /> + + From 6c5c8cf018b5c402d160101216566a81c87100be Mon Sep 17 00:00:00 2001 From: Chamika Date: Sun, 3 May 2026 12:56:08 +0100 Subject: [PATCH 2/3] Release v1.2.1(17) Add sync library button to settings Full release notes: - Add Sync library button to settings screen - Show last synced date and time as subtitle beneath the button - Fix sync command to show a failure toast and return an error result when sync fails --- automotive/build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/automotive/build.gradle.kts b/automotive/build.gradle.kts index db8b046..51f793b 100644 --- a/automotive/build.gradle.kts +++ b/automotive/build.gradle.kts @@ -14,8 +14,8 @@ android { applicationId = "com.chamika.dashtune" minSdk = 28 targetSdk = 36 - versionCode = 16 - versionName = "1.2.0" + versionCode = 17 + versionName = "1.2.1" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } From c6197c0335bcd376ad17e017cc2fad0a4739b47e Mon Sep 17 00:00:00 2001 From: Chamika Date: Sun, 3 May 2026 13:03:13 +0100 Subject: [PATCH 3/3] Add Google Play Store badge to README Co-Authored-By: Claude Sonnet 4.6 --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 7ba7b35..d0dd773 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ A fully-featured Jellyfin music and audiobook player for Android Automotive OS (AAOS) with offline download capabilities. +[Get it on Google Play](https://play.google.com/store/apps/details?id=com.chamika.dashtune) + ## Overview DashTune brings the complete Jellyfin music library experience to your car's infotainment system. Stream your music collection, browse audiobooks, download tracks for offline playback, and enjoy seamless integration with Android Automotive OS.