Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

A fully-featured Jellyfin music and audiobook player for Android Automotive OS (AAOS) with offline download capabilities.

[<img src="https://play.google.com/intl/en_us/badges/static/images/badges/en_badge_web_generic.png" alt="Get it on Google Play" height="80">](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.
Expand Down
4 changes: 2 additions & 2 deletions automotive/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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<MediaController>

override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.preferences, rootKey)
Expand All @@ -41,6 +53,28 @@ class SettingsFragment : PreferenceFragmentCompat() {
}
}

val syncPref = findPreference<Preference>("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<Preference>("sign_out")?.setOnPreferenceClickListener {
AlertDialog.Builder(requireContext())
.setMessage(R.string.sign_out_confirmation)
Expand All @@ -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)
}
}
}
4 changes: 4 additions & 0 deletions automotive/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,8 @@
<string name="max_categories_warning">Select at most 4 categories</string>
<string name="cache_favourites">Cache favourites</string>
<string name="cache_favourites_summary">Pre-download all favourite songs for offline playback</string>
<string name="sync_library">Sync library</string>
<string name="sync_library_never">Never synced</string>
<string name="sync_library_last_synced">Last synced: %s</string>
<string name="sync_failed">Sync failed</string>
</resources>
4 changes: 4 additions & 0 deletions automotive/src/main/res/xml/preferences.xml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@
android:summary="@string/browse_categories_summary"
android:title="@string/browse_categories" />

<Preference
android:key="sync_library"
android:title="@string/sync_library" />

<Preference
android:key="version"
android:title="@string/app_version" />
Expand Down
Loading