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. 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" } 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" /> + +