Skip to content

Commit e835240

Browse files
committed
wip
1 parent 6ea06f2 commit e835240

File tree

8 files changed

+93
-40
lines changed

8 files changed

+93
-40
lines changed

app/src/main/AndroidManifest.xml

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,13 @@
22
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
33
xmlns:tools="http://schemas.android.com/tools">
44

5-
<uses-feature android:name="android.hardware.faketouch" android:required="false"/>
6-
<uses-feature android:name="android.hardware.bluetooth" android:required="false"/>
5+
<uses-feature android:name="android.hardware.audio.output" android:required="false"/>
6+
<uses-feature android:name="android.hardware.audio.pro" android:required="false"/>
77
<uses-feature android:name="android.hardware.bluetooth_le" android:required="false"/>
8+
<uses-feature android:name="android.hardware.bluetooth" android:required="false"/>
9+
<uses-feature android:name="android.hardware.faketouch" android:required="false"/>
10+
<uses-feature android:name="android.hardware.type.pc" android:required="false"/>
11+
<uses-feature android:name="android.software.app_widgets" android:required="false"/>
812

913
<!-- Notifications (for error handling on MIUI) -->
1014
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"
@@ -58,6 +62,14 @@
5862
android:theme="@style/Theme.Gramophone"
5963
android:windowSoftInputMode="adjustResize"
6064
tools:targetApi="upside_down_cake">
65+
<meta-data
66+
android:name="androidx.car.app.TintableAttributionIcon"
67+
android:resource="@drawable/ic_gramophone_monochrome" />
68+
<!-- TODO uncomment to enable Android Auto when library browsing is implemented
69+
<meta-data
70+
android:name="com.google.android.gms.car.application"
71+
android:resource="@xml/automotive_app_desc"/>
72+
-->
6173
<activity
6274
android:name=".logic.ui.BugHandlerActivity"
6375
android:exported="false"

app/src/main/kotlin/org/akanework/gramophone/logic/GramophoneApplication.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ class GramophoneApplication : Application(), SingletonImageLoader.Factory,
113113
StrictMode.setVmPolicy(
114114
VmPolicy.Builder()
115115
.detectAll()
116-
.penaltyLog().penaltyDeath().build()
116+
.penaltyLog().build()
117117
)
118118
}
119119
reader = FlowReader(

app/src/main/kotlin/org/akanework/gramophone/logic/GramophoneExtensions.kt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,17 @@ fun MediaItem.getFile(): File? {
102102
return getUri()?.toFile()
103103
}
104104

105+
fun MediaItem.getMediaStoreId(): Long? {
106+
return if (mediaId.startsWith("MediaStore:"))
107+
mediaId.substring("MediaStore:".length).toLongOrNull()
108+
else null
109+
}
110+
111+
fun MediaItem.requireMediaStoreId(): Long {
112+
return getMediaStoreId()
113+
?: throw IllegalArgumentException("Media item with ID $mediaId doesn't appear to be media store item")
114+
}
115+
105116
fun MediaItem.getBitrate(): Long? {
106117
val retriever = MediaMetadataRetriever()
107118
return try {

app/src/main/kotlin/org/akanework/gramophone/logic/GramophonePlaybackService.kt

Lines changed: 41 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ import android.media.audiofx.AudioEffect
3333
import android.net.Uri
3434
import android.os.Build
3535
import android.os.Bundle
36-
import android.os.Bundle.EMPTY
3736
import android.os.Handler
3837
import android.os.HandlerThread
3938
import android.os.Looper
@@ -243,6 +242,7 @@ class GramophonePlaybackService : MediaLibraryService(), MediaSessionService.Lis
243242
}
244243

245244
override fun onCreate() {
245+
Log.i(TAG, "onCreate()")
246246
super.onCreate()
247247
instanceForWidgetAndLyricsOnly = this
248248
internalPlaybackThread.start()
@@ -316,8 +316,8 @@ class GramophonePlaybackService : MediaLibraryService(), MediaSessionService.Lis
316316
afFormatTracker = AfFormatTracker(this, playbackHandler, handler)
317317
afFormatTracker.formatChangedCallback = {
318318
mediaSession?.broadcastCustomCommand(
319-
SessionCommand(SERVICE_GET_AUDIO_FORMAT, EMPTY),
320-
EMPTY
319+
SessionCommand(SERVICE_GET_AUDIO_FORMAT, Bundle.EMPTY),
320+
Bundle.EMPTY
321321
)
322322
}
323323
val player = EndedWorkaroundPlayer(
@@ -450,8 +450,8 @@ class GramophonePlaybackService : MediaLibraryService(), MediaSessionService.Lis
450450
Log.d(TAG, "first bluetooth codec config $btInfo")
451451
btInfo = it
452452
mediaSession?.broadcastCustomCommand(
453-
SessionCommand(SERVICE_GET_AUDIO_FORMAT, EMPTY),
454-
EMPTY
453+
SessionCommand(SERVICE_GET_AUDIO_FORMAT, Bundle.EMPTY),
454+
Bundle.EMPTY
455455
)
456456
}
457457
}
@@ -495,6 +495,7 @@ class GramophonePlaybackService : MediaLibraryService(), MediaSessionService.Lis
495495
// When destroying, we should release server side player
496496
// alongside with the mediaSession.
497497
override fun onDestroy() {
498+
Log.i(TAG, "onDestroy()")
498499
instanceForWidgetAndLyricsOnly = null
499500
unregisterReceiver(headSetReceiver)
500501
unregisterReceiver(seekReceiver)
@@ -551,8 +552,8 @@ class GramophonePlaybackService : MediaLibraryService(), MediaSessionService.Lis
551552
Bundle.EMPTY
552553
)
553554
mediaSession?.broadcastCustomCommand(
554-
SessionCommand(SERVICE_GET_AUDIO_FORMAT, EMPTY),
555-
EMPTY
555+
SessionCommand(SERVICE_GET_AUDIO_FORMAT, Bundle.EMPTY),
556+
Bundle.EMPTY
556557
)
557558
}
558559

@@ -701,13 +702,33 @@ class GramophonePlaybackService : MediaLibraryService(), MediaSessionService.Lis
701702
settable.setException(
702703
IndexOutOfBoundsException(
703704
"LastPlayedManager restored empty MediaItemsWithStartPosition"
704-
).also { Log.e(TAG, Log.getStackTraceString(it)) }
705+
)
705706
)
706707
}
707708
}
708709
return settable
709710
}
710711

712+
/*override fun onGetLibraryRoot(
713+
session: MediaLibrarySession,
714+
browser: MediaSession.ControllerInfo,
715+
params: LibraryParams?
716+
): ListenableFuture<LibraryResult<MediaItem>> {
717+
val outParams = LibraryParams.Builder()
718+
.setOffline(true)
719+
.setSuggested(false)
720+
.setRecent(false)
721+
.build()
722+
val item = MediaItem.Builder()
723+
.setMediaId("root")
724+
.setMediaMetadata(MediaMetadata.Builder()
725+
.setIsBrowsable(true)
726+
.setIsPlayable(false)
727+
.build())
728+
.build()
729+
return Futures.immediateFuture(LibraryResult.ofItem(item, outParams))
730+
}*/
731+
711732
override fun onTracksChanged(eventTime: AnalyticsListener.EventTime, tracks: Tracks) {
712733
val mediaItem = controller!!.currentMediaItem
713734

@@ -780,8 +801,8 @@ class GramophonePlaybackService : MediaLibraryService(), MediaSessionService.Lis
780801
audioTrackInfoCounter++
781802
audioTrackInfo = AudioTrackInfo.fromMedia3AudioTrackConfig(audioTrackConfig)
782803
mediaSession?.broadcastCustomCommand(
783-
SessionCommand(SERVICE_GET_AUDIO_FORMAT, EMPTY),
784-
EMPTY
804+
SessionCommand(SERVICE_GET_AUDIO_FORMAT, Bundle.EMPTY),
805+
Bundle.EMPTY
785806
)
786807
}
787808

@@ -794,8 +815,8 @@ class GramophonePlaybackService : MediaLibraryService(), MediaSessionService.Lis
794815
if (++audioTrackReleaseCounter == audioTrackInfoCounter) {
795816
audioTrackInfo = null
796817
mediaSession?.broadcastCustomCommand(
797-
SessionCommand(SERVICE_GET_AUDIO_FORMAT, EMPTY),
798-
EMPTY
818+
SessionCommand(SERVICE_GET_AUDIO_FORMAT, Bundle.EMPTY),
819+
Bundle.EMPTY
799820
)
800821
}
801822
}
@@ -806,25 +827,25 @@ class GramophonePlaybackService : MediaLibraryService(), MediaSessionService.Lis
806827
) {
807828
downstreamFormat = mediaLoadData.trackFormat
808829
mediaSession?.broadcastCustomCommand(
809-
SessionCommand(SERVICE_GET_AUDIO_FORMAT, EMPTY),
810-
EMPTY
830+
SessionCommand(SERVICE_GET_AUDIO_FORMAT, Bundle.EMPTY),
831+
Bundle.EMPTY
811832
)
812833
}
813834

814835
private fun onAudioSinkInputFormatChanged(inputFormat: Format?) {
815836
audioSinkInputFormat = inputFormat
816837
mediaSession?.broadcastCustomCommand(
817-
SessionCommand(SERVICE_GET_AUDIO_FORMAT, EMPTY),
818-
EMPTY
838+
SessionCommand(SERVICE_GET_AUDIO_FORMAT, Bundle.EMPTY),
839+
Bundle.EMPTY
819840
)
820841
}
821842

822843
override fun onPlaybackStateChanged(eventTime: AnalyticsListener.EventTime, state: Int) {
823844
if (state == Player.STATE_IDLE) {
824845
downstreamFormat = null
825846
mediaSession?.broadcastCustomCommand(
826-
SessionCommand(SERVICE_GET_AUDIO_FORMAT, EMPTY),
827-
EMPTY
847+
SessionCommand(SERVICE_GET_AUDIO_FORMAT, Bundle.EMPTY),
848+
Bundle.EMPTY
828849
)
829850
}
830851
}
@@ -838,8 +859,8 @@ class GramophonePlaybackService : MediaLibraryService(), MediaSessionService.Lis
838859
bitrateFetcher.launch {
839860
bitrate = mediaItem?.getBitrate() // TODO subtract cover size
840861
this@GramophonePlaybackService.mediaSession?.broadcastCustomCommand(
841-
SessionCommand(SERVICE_GET_AUDIO_FORMAT, EMPTY),
842-
EMPTY
862+
SessionCommand(SERVICE_GET_AUDIO_FORMAT, Bundle.EMPTY),
863+
Bundle.EMPTY
843864
)
844865
}
845866
lyrics = null

app/src/main/kotlin/org/akanework/gramophone/logic/utils/LastPlayedManager.kt

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,8 @@ class LastPlayedManager(
107107
data.mediaItems.map {
108108
val b = SafeDelimitedStringConcat(":")
109109
// add new entries at the bottom and remember they are null for upgrade path
110-
b.writeStringUnsafe(it.mediaId)
110+
b.writeStringUnsafe("ver_" + 1)
111+
b.writeStringSafe(it.mediaId)
111112
b.writeUri(it.localConfiguration?.uri)
112113
b.writeStringSafe(it.localConfiguration?.mimeType)
113114
b.writeStringSafe(it.mediaMetadata.title)
@@ -130,11 +131,8 @@ class LastPlayedManager(
130131
b.writeInt(it.mediaMetadata.recordingMonth)
131132
b.writeLong(it.mediaMetadata.artistId)
132133
b.writeLong(it.mediaMetadata.albumId)
133-
b.skip() // used to be GenreId
134134
b.writeStringSafe(it.mediaMetadata.author)
135-
b.skip() // used to be CdTrackNumber
136135
b.writeLong(it.mediaMetadata.durationMs)
137-
b.skip() // used to be Path
138136
b.writeLong(it.mediaMetadata.modifiedDate)
139137
b.writeStringSafe(it.mediaMetadata.cdTrackNumber)
140138
b.toString()
@@ -192,7 +190,15 @@ class LastPlayedManager(
192190
.map {
193191
val b = SafeDelimitedStringDecat(":", it)
194192
// add new entries at the bottom and remember they are null for upgrade path
195-
val mediaId = b.readStringUnsafe()
193+
val versionStr = b.readStringUnsafe()
194+
val version = versionStr.let {
195+
if (it?.startsWith("ver_") == true)
196+
it.substring("ver_".length).toInt()
197+
else 0
198+
}
199+
val mediaId = if (version == 0) {
200+
"MediaStore:$versionStr" // used to be mediaId
201+
} else b.readStringSafe()
196202
val uri = b.readUri()
197203
val mimeType = b.readStringSafe()
198204
val title = b.readStringSafe()
@@ -215,11 +221,14 @@ class LastPlayedManager(
215221
val recordingMonth = b.readInt()
216222
val artistId = b.readLong()
217223
val albumId = b.readLong()
218-
b.skip() // used to be GenreId
224+
if (version < 1)
225+
b.skip() // used to be GenreId
219226
val author = b.readStringSafe()
220-
b.skip() // used to be CdTrackNumber
227+
if (version < 1)
228+
b.skip() // used to be CdTrackNumber
221229
val duration = b.readLong()
222-
b.skip() // used to be Path
230+
if (version < 1)
231+
b.skip() // used to be Path
223232
val modifiedDate = b.readLong()
224233
val cdTrackNumber = b.readStringSafe()
225234
MediaItem.Builder()

app/src/main/kotlin/org/akanework/gramophone/ui/adapters/SongAdapter.kt

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,21 +25,15 @@ import androidx.appcompat.app.AlertDialog
2525
import androidx.appcompat.widget.PopupMenu
2626
import androidx.core.app.ShareCompat
2727
import androidx.core.content.FileProvider
28-
import androidx.core.net.toFile
2928
import androidx.fragment.app.Fragment
3029
import androidx.fragment.app.activityViewModels
3130
import androidx.media3.common.C
3231
import androidx.media3.common.MediaItem
3332
import androidx.media3.common.Player
34-
import java.io.File
35-
import java.util.GregorianCalendar
36-
import kotlinx.coroutines.CoroutineScope
37-
import kotlinx.coroutines.Dispatchers
3833
import kotlinx.coroutines.flow.Flow
39-
import kotlinx.coroutines.launch
40-
import kotlinx.coroutines.withContext
4134
import org.akanework.gramophone.R
4235
import org.akanework.gramophone.logic.getFile
36+
import org.akanework.gramophone.logic.requireMediaStoreId
4337
import org.akanework.gramophone.ui.MainActivity
4438
import org.akanework.gramophone.ui.MediaControllerViewModel
4539
import org.akanework.gramophone.ui.components.NowPlayingDrawable
@@ -51,6 +45,8 @@ import uk.akane.libphonograph.items.albumId
5145
import uk.akane.libphonograph.items.artistId
5246
import uk.akane.libphonograph.items.modifiedDate
5347
import uk.akane.libphonograph.manipulator.ItemManipulator
48+
import java.io.File
49+
import java.util.GregorianCalendar
5450

5551

5652
/**
@@ -204,7 +200,7 @@ class SongAdapter(
204200
}
205201

206202
R.id.delete -> {
207-
val res = ItemManipulator.deleteSong(context, item.mediaId.toLong())
203+
val res = ItemManipulator.deleteSong(context, item.requireMediaStoreId())
208204
if (res.continueDelete != null) {
209205
AlertDialog.Builder(context)
210206
.setTitle(R.string.delete)
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<automotiveApp>
3+
<uses name="media"/>
4+
</automotiveApp>

libphonograph

0 commit comments

Comments
 (0)