From 66443ba0180fa882a9e628f7c9dea208691495f2 Mon Sep 17 00:00:00 2001 From: tzugen Date: Tue, 7 Dec 2021 10:23:11 +0100 Subject: [PATCH 01/13] Remove Jacoco --- .circleci/config.yml | 3 - build.gradle | 3 - core/domain/build.gradle | 6 -- core/subsonic-api/build.gradle | 8 -- gradle/libs.versions.toml | 2 - .../android-module-bootstrap.gradle | 12 --- gradle_scripts/jacoco.gradle | 92 ------------------- gradle_scripts/kotlin-module-bootstrap.gradle | 29 ------ ultrasonic/build.gradle | 34 ------- 9 files changed, 189 deletions(-) delete mode 100644 gradle_scripts/jacoco.gradle diff --git a/.circleci/config.yml b/.circleci/config.yml index 810495d9..0b4c865b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -41,7 +41,6 @@ jobs: name: unit-tests command: | ./gradlew ciTest testDebugUnitTest - ./gradlew jacocoFullReport - run: name: lint command: ./gradlew :ultrasonic:lintRelease @@ -61,8 +60,6 @@ jobs: - store_artifacts: path: subsonic-api/build/reports destination: reports - - store_artifacts: - path: build/reports/jacoco/jacocoFullReport/ push_translations: docker: - image: cimg/python:3.6 diff --git a/build.gradle b/build.gradle index 925569fa..c44d7f84 100644 --- a/build.gradle +++ b/build.gradle @@ -17,7 +17,6 @@ buildscript { classpath libs.kotlin classpath libs.ktlintGradle classpath libs.detekt - classpath libs.jacoco } } @@ -44,8 +43,6 @@ allprojects { } } -apply from: 'gradle_scripts/jacoco.gradle' - wrapper { gradleVersion(libs.versions.gradle.get()) distributionType("all") diff --git a/core/domain/build.gradle b/core/domain/build.gradle index 0b0ab96c..3ed1e681 100644 --- a/core/domain/build.gradle +++ b/core/domain/build.gradle @@ -1,12 +1,6 @@ apply from: bootstrap.androidModule apply plugin: 'kotlin-kapt' -ext { - jacocoExclude = [ - '**/domain/**' - ] -} - dependencies { implementation libs.roomRuntime implementation libs.roomKtx diff --git a/core/subsonic-api/build.gradle b/core/subsonic-api/build.gradle index 9ad09193..6db3ffcb 100644 --- a/core/subsonic-api/build.gradle +++ b/core/subsonic-api/build.gradle @@ -20,11 +20,3 @@ dependencies { testImplementation libs.mockWebServer testImplementation libs.apacheCodecs } - -ext { - // Excluding data classes - jacocoExclude = [ - '**/models/**', - '**/di/**' - ] -} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 59ea094a..f5edc6ce 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -8,7 +8,6 @@ androidxcore = "1.6.0" ktlint = "0.43.2" ktlintGradle = "10.2.0" detekt = "1.19.0" -jacoco = "0.8.7" preferences = "1.1.1" media = "1.3.1" media3 = "1.0.0-alpha03" @@ -49,7 +48,6 @@ gradle = { module = "com.android.tools.build:gradle", version.r kotlin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" } ktlintGradle = { module = "org.jlleitschuh.gradle:ktlint-gradle", version.ref = "ktlintGradle" } detekt = { module = "io.gitlab.arturbosch.detekt:detekt-gradle-plugin", version.ref = "detekt" } -jacoco = { module = "org.jacoco:org.jacoco.core", version.ref = "jacoco" } core = { module = "androidx.core:core-ktx", version.ref = "androidxcore" } support = { module = "androidx.legacy:legacy-support-v4", version.ref = "androidLegacySupport" } diff --git a/gradle_scripts/android-module-bootstrap.gradle b/gradle_scripts/android-module-bootstrap.gradle index 84aa4e41..70a8a479 100644 --- a/gradle_scripts/android-module-bootstrap.gradle +++ b/gradle_scripts/android-module-bootstrap.gradle @@ -3,7 +3,6 @@ */ apply plugin: 'com.android.library' apply plugin: 'kotlin-android' -apply plugin: 'jacoco' apply from: "${project.rootDir}/gradle_scripts/code_quality.gradle" android { @@ -48,10 +47,6 @@ android { tasks.withType(Test) { useJUnitPlatform() - jacoco { - includeNoLocationClasses = true - excludes += jacocoExclude - } } dependencies { @@ -61,11 +56,4 @@ dependencies { testRuntimeOnly libs.junitVintage } -jacoco { - toolVersion(libs.versions.jacoco.get()) -} - -ext { - jacocoExclude = ['jdk.internal.*'] -} diff --git a/gradle_scripts/jacoco.gradle b/gradle_scripts/jacoco.gradle deleted file mode 100644 index c8ba2c07..00000000 --- a/gradle_scripts/jacoco.gradle +++ /dev/null @@ -1,92 +0,0 @@ -apply plugin: 'jacoco' - -jacoco { - toolVersion(libs.versions.jacoco.get()) -} - -def mergedJacocoExec = file("${project.buildDir}/jacoco/jacocoMerged.exec") - -def merge = tasks.register('jacocoMergeReports', JacocoMerge) { - group = "Reporting" - description = "Merge all jacoco reports from projects into one." - - ListProperty jacocoFiles = project.objects.listProperty(File.class) - project.subprojects { subproject -> - subproject.plugins.withId("jacoco") { - project.logger.info("${subproject.name} has Jacoco plugin applied") - subproject.tasks.withType(Test) { task -> - File destFile = task.extensions.getByType(JacocoTaskExtension.class).destinationFile - if (destFile.exists() && !task.name.contains("Release")) { - jacocoFiles.add(destFile) - } - } - } - } - - executionData(jacocoFiles) - destinationFile(mergedJacocoExec) -} - -tasks.register('jacocoFullReport', JacocoReport) { - dependsOn merge - group = "Reporting" - description = "Generate full Jacoco coverage report including all modules." - - getClassDirectories().setFrom(files()) - getSourceDirectories().setFrom(files()) - getExecutionData().setFrom(files()) - - reports { - xml.enabled = true - html.enabled = true - csv.enabled = false - } - - // Always run merging, as all input calculation is done in doFirst {} - outputs.upToDateWhen { false } - // Task will run anyway even if initial inputs are empty - onlyIf = { true } - - project.subprojects { subproject -> - subproject.plugins.withId("jacoco") { - project.logger.info("${subproject.name} has Jacoco plugin applied") - subproject.plugins.withId("kotlin-android") { - project.logger.info("${subproject.name} is android project") - def mainSources = subproject.extensions.findByName("android").sourceSets['main'] - project.logger.info("Android sources: ${mainSources.java.srcDirs}") - mainSources.java.srcDirs.forEach { - additionalSourceDirs(it) - } - project.logger.info("Subproject exclude: ${subproject.jacocoExclude}") - additionalClassDirs(fileTree( - dir: "${subproject.buildDir}/tmp/kotlin-classes/debug", - excludes: subproject.jacocoExclude - )) - } - subproject.plugins.withId("kotlin") { plugin -> - project.logger.info("${subproject.name} is common kotlin project") - SourceDirectorySet mainSources = subproject.extensions.getByName("kotlin") - .sourceSets[SourceSet.MAIN_SOURCE_SET_NAME] - .kotlin - mainSources.srcDirs.forEach { - project.logger.debug("Adding sources: $it") - additionalSourceDirs(it) - } - project.logger.info("Subproject exclude: ${subproject.jacocoExclude}") - additionalClassDirs(fileTree( - dir: "${subproject.buildDir}/classes/kotlin/main", - excludes: subproject.jacocoExclude - )) - } - - subproject.tasks.withType(Test) { task -> - File destFile = task.extensions.getByType(JacocoTaskExtension.class).destinationFile - if (destFile.exists() && !task.name.contains("Release")) { - project.logger.info("Adding execution data: $destFile") - executionData(destFile) - } - } - } - } - -} diff --git a/gradle_scripts/kotlin-module-bootstrap.gradle b/gradle_scripts/kotlin-module-bootstrap.gradle index 4a17c80f..d440dec5 100644 --- a/gradle_scripts/kotlin-module-bootstrap.gradle +++ b/gradle_scripts/kotlin-module-bootstrap.gradle @@ -3,7 +3,6 @@ */ apply plugin: 'kotlin' apply plugin: 'kotlin-kapt' -apply plugin: 'jacoco' apply from: "${project.rootDir}/gradle_scripts/code_quality.gradle" sourceSets { @@ -21,36 +20,8 @@ dependencies { testRuntimeOnly libs.junitVintage } -jacoco { - toolVersion(libs.versions.jacoco.get()) -} - -ext { - // override it in the module - jacocoExclude = ['jdk.internal.*'] -} - -jacocoTestReport { - reports { - html.required = true - xml.required = false - csv.required = false - } - - afterEvaluate { - getClassDirectories().setFrom(files(classDirectories.files.collect { - fileTree(dir: it, excludes: jacocoExclude) - })) - } -} - tasks.named("test").configure { useJUnitPlatform() - jacoco { - excludes += jacocoExclude - includeNoLocationClasses = true - } - finalizedBy jacocoTestReport } tasks.register("ciTest") { diff --git a/ultrasonic/build.gradle b/ultrasonic/build.gradle index 95196a9c..986ebf0e 100644 --- a/ultrasonic/build.gradle +++ b/ultrasonic/build.gradle @@ -1,7 +1,6 @@ apply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply plugin: 'kotlin-kapt' -apply plugin: 'jacoco' apply from: "../gradle_scripts/code_quality.gradle" android { @@ -135,36 +134,3 @@ dependencies { implementation libs.timber } - -jacoco { - toolVersion(libs.versions.jacoco.get()) -} - -// Excluding all java classes and stuff that should not be covered -ext { - jacocoExclude = [ - '**/activity/**', - '**/audiofx/**', - '**/fragment/**', - '**/provider/**', - '**/receiver/**', - '**/service/**', - '**/Test/**', - '**/util/**', - '**/view/**', - '**/R$*.class', - '**/R.class', - '**/BuildConfig.class', - '**/di/**', - 'jdk.internal.*' - ] -} - -jacoco { - toolVersion(libs.versions.jacoco.get()) -} - -tasks.withType(Test) { - jacoco.includeNoLocationClasses = true - jacoco.excludes += jacocoExclude -} From 9961213f0994cdca06db36bff5ebe423ce38fa2f Mon Sep 17 00:00:00 2001 From: Maxence G Date: Sun, 19 Jun 2022 18:21:33 +0200 Subject: [PATCH 02/13] Upgrade to media3-beta01 --- gradle/libs.versions.toml | 3 +- ultrasonic/src/main/AndroidManifest.xml | 1 + .../playback/AutoMediaBrowserCallback.kt | 43 +++++-------------- .../playback/LegacyPlaylistManager.kt | 6 +-- .../playback/MediaNotificationProvider.kt | 7 ++- .../ultrasonic/playback/PlaybackService.kt | 19 ++------ .../service/MediaPlayerController.kt | 8 +++- 7 files changed, 32 insertions(+), 55 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f5edc6ce..dd18430b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -10,7 +10,7 @@ ktlintGradle = "10.2.0" detekt = "1.19.0" preferences = "1.1.1" media = "1.3.1" -media3 = "1.0.0-alpha03" +media3 = "1.0.0-beta01" androidSupport = "28.0.0" androidLegacySupport = "1.0.0" @@ -101,4 +101,3 @@ kluentAndroid = { module = "org.amshove.kluent:kluent-android", versio mockWebServer = { module = "com.squareup.okhttp3:mockwebserver", version.ref = "okhttp" } apacheCodecs = { module = "commons-codec:commons-codec", version.ref = "apacheCodecs" } robolectric = { module = "org.robolectric:robolectric", version.ref = "robolectric" } - diff --git a/ultrasonic/src/main/AndroidManifest.xml b/ultrasonic/src/main/AndroidManifest.xml index d8ed9a5f..ab6384c3 100644 --- a/ultrasonic/src/main/AndroidManifest.xml +++ b/ultrasonic/src/main/AndroidManifest.xml @@ -67,6 +67,7 @@ diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/AutoMediaBrowserCallback.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/AutoMediaBrowserCallback.kt index e2abb257..f1c8e41d 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/AutoMediaBrowserCallback.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/AutoMediaBrowserCallback.kt @@ -21,7 +21,6 @@ import androidx.media3.common.Player import androidx.media3.session.LibraryResult import androidx.media3.session.MediaLibraryService import androidx.media3.session.MediaSession -import androidx.media3.session.SessionResult import com.google.common.collect.ImmutableList import com.google.common.util.concurrent.Futures import com.google.common.util.concurrent.ListenableFuture @@ -89,7 +88,7 @@ private const val SEARCH_QUERY_PREFIX = "androidx://media3-session/setMediaUri" */ @Suppress("TooManyFunctions", "LargeClass", "UnusedPrivateMember") class AutoMediaBrowserCallback(var player: Player) : - MediaLibraryService.MediaLibrarySession.MediaLibrarySessionCallback, KoinComponent { + MediaLibraryService.MediaLibrarySession.Callback, KoinComponent { private val mediaPlayerController by inject() private val activeServerProvider: ActiveServerProvider by inject() @@ -181,39 +180,19 @@ class AutoMediaBrowserCallback(var player: Player) : return onLoadChildren(parentId) } - private fun setMediaItemFromSearchQuery(query: String) { - // Only accept query with pattern "play [Title]" or "[Title]" - // Where [Title]: must be exactly matched - // If no media with exact name found, play a random media instead - val mediaTitle = - if (query.startsWith("play ", ignoreCase = true)) { - query.drop(5) - } else { - query - } - - playFromMediaId(mediaTitle) - } - - override fun onSetMediaUri( - session: MediaSession, + // https://stackoverflow.com/questions/70096715/adding-mediaitem-when-using-the-media3-library-caused-an-error + override fun onAddMediaItems( + mediaSession: MediaSession, controller: MediaSession.ControllerInfo, - uri: Uri, - extras: Bundle - ): Int { + mediaItems: MutableList + ): ListenableFuture> { - if (uri.toString().startsWith(SEARCH_QUERY_PREFIX) || - uri.toString().startsWith(SEARCH_QUERY_PREFIX_COMPAT) - ) { - val searchQuery = - uri.getQueryParameter("query") - ?: return SessionResult.RESULT_ERROR_NOT_SUPPORTED - setMediaItemFromSearchQuery(searchQuery) - - return SessionResult.RESULT_SUCCESS - } else { - return SessionResult.RESULT_ERROR_NOT_SUPPORTED + val updatedMediaItems = mediaItems.map { mediaItem -> + mediaItem.buildUpon() + .setUri(mediaItem.requestMetadata.mediaUri) + .build() } + return Futures.immediateFuture(updatedMediaItems.toMutableList()) } @Suppress("ReturnCount", "ComplexMethod") diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/LegacyPlaylistManager.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/LegacyPlaylistManager.kt index 88d5dc13..77013e45 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/LegacyPlaylistManager.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/LegacyPlaylistManager.kt @@ -50,7 +50,7 @@ class LegacyPlaylistManager : KoinComponent { for (i in 0 until n) { val item = controller.getMediaItemAt(i) - val file = mediaItemCache[item.mediaMetadata.mediaUri.toString()] + val file = mediaItemCache[item.requestMetadata.toString()] if (file != null) _playlist.add(file) } @@ -59,11 +59,11 @@ class LegacyPlaylistManager : KoinComponent { } fun addToCache(item: MediaItem, file: DownloadFile) { - mediaItemCache.put(item.mediaMetadata.mediaUri.toString(), file) + mediaItemCache.put(item.requestMetadata.toString(), file) } fun updateCurrentPlaying(item: MediaItem?) { - currentPlaying = mediaItemCache[item?.mediaMetadata?.mediaUri.toString()] + currentPlaying = mediaItemCache[item?.requestMetadata.toString()] } @Synchronized diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/MediaNotificationProvider.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/MediaNotificationProvider.kt index 5ecb4fe4..2bc6e2c8 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/MediaNotificationProvider.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/MediaNotificationProvider.kt @@ -7,6 +7,7 @@ package org.moire.ultrasonic.playback +/* import android.app.Notification import android.app.NotificationChannel import android.app.NotificationManager @@ -24,14 +25,17 @@ import androidx.media3.session.MediaController import androidx.media3.session.MediaNotification import androidx.media3.session.MediaNotification.ActionFactory import org.moire.ultrasonic.R +*/ /* * This is a copy of DefaultMediaNotificationProvider.java with some small changes * I have opened a bug https://github.com/androidx/media/issues/65 to make it easier to customize * the icons and actions without creating our own copy of this class.. */ -@UnstableApi +//@UnstableApi /* package */ +// Disabled while getting updated +/* internal class MediaNotificationProvider(context: Context) : MediaNotification.Provider { private val context: Context = context.applicationContext @@ -148,3 +152,4 @@ internal class MediaNotificationProvider(context: Context) : } } } +*/ diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/PlaybackService.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/PlaybackService.kt index 8404d0f9..7fee1310 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/PlaybackService.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/PlaybackService.kt @@ -13,11 +13,11 @@ import androidx.media3.common.AudioAttributes import androidx.media3.common.C import androidx.media3.common.C.CONTENT_TYPE_MUSIC import androidx.media3.common.C.USAGE_MEDIA -import androidx.media3.common.MediaItem import androidx.media3.datasource.DataSource import androidx.media3.exoplayer.DefaultRenderersFactory import androidx.media3.exoplayer.ExoPlayer import androidx.media3.exoplayer.source.DefaultMediaSourceFactory +import androidx.media3.session.DefaultMediaNotificationProvider import androidx.media3.session.MediaLibraryService import androidx.media3.session.MediaSession import io.reactivex.rxjava3.disposables.CompositeDisposable @@ -38,7 +38,7 @@ class PlaybackService : MediaLibraryService(), KoinComponent { private lateinit var mediaLibrarySession: MediaLibrarySession private lateinit var apiDataSource: APIDataSource.Factory - private lateinit var librarySessionCallback: MediaLibrarySession.MediaLibrarySessionCallback + private lateinit var librarySessionCallback: MediaLibrarySession.Callback private var rxBusSubscription = CompositeDisposable() @@ -48,18 +48,6 @@ class PlaybackService : MediaLibraryService(), KoinComponent { * For some reason the LocalConfiguration of MediaItem are stripped somewhere in ExoPlayer, * and thereby customarily it is required to rebuild it.. */ - private class CustomMediaItemFiller : MediaSession.MediaItemFiller { - override fun fillInLocalConfiguration( - session: MediaSession, - controller: MediaSession.ControllerInfo, - mediaItem: MediaItem - ): MediaItem { - // Again, set the Uri, so that it will get a LocalConfiguration - return mediaItem.buildUpon() - .setUri(mediaItem.mediaMetadata.mediaUri) - .build() - } - } override fun onCreate() { Timber.i("onCreate called") @@ -102,7 +90,7 @@ class PlaybackService : MediaLibraryService(), KoinComponent { private fun initializeSessionAndPlayer() { if (isStarted) return - setMediaNotificationProvider(MediaNotificationProvider(UApp.applicationContext())) + setMediaNotificationProvider(DefaultMediaNotificationProvider(UApp.applicationContext())) val subsonicAPIClient: SubsonicAPIClient by inject() @@ -134,7 +122,6 @@ class PlaybackService : MediaLibraryService(), KoinComponent { // This will need to use the AutoCalls mediaLibrarySession = MediaLibrarySession.Builder(this, player, librarySessionCallback) - .setMediaItemFiller(CustomMediaItemFiller()) .setSessionActivity(getPendingIntentForContent()) .build() diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerController.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerController.kt index 749e30e0..72003cfd 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerController.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerController.kt @@ -659,16 +659,22 @@ fun Track.toMediaItem(): MediaItem { val bitrate = Settings.maxBitRate val uri = "$id|$bitrate|$filePath" + + val rmd = MediaItem.RequestMetadata.Builder() + .setMediaUri(uri.toUri()) + .build() + val metadata = MediaMetadata.Builder() metadata.setTitle(title) .setArtist(artist) .setAlbumTitle(album) - .setMediaUri(uri.toUri()) .setAlbumArtist(artist) + .build() val mediaItem = MediaItem.Builder() .setUri(uri) .setMediaId(id) + .setRequestMetadata(rmd) .setMediaMetadata(metadata.build()) return mediaItem.build() From cf367ead9247ec9527d816658a5bf7d34f392275 Mon Sep 17 00:00:00 2001 From: Maxence G Date: Sun, 19 Jun 2022 18:40:04 +0200 Subject: [PATCH 03/13] Add back notification icon --- .../main/res/drawable/media3_notification_small_icon.xml | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 ultrasonic/src/main/res/drawable/media3_notification_small_icon.xml diff --git a/ultrasonic/src/main/res/drawable/media3_notification_small_icon.xml b/ultrasonic/src/main/res/drawable/media3_notification_small_icon.xml new file mode 100644 index 00000000..81e0ed96 --- /dev/null +++ b/ultrasonic/src/main/res/drawable/media3_notification_small_icon.xml @@ -0,0 +1,9 @@ + + + From 545b65921ea4c9f0aff6b344b85a84b4d7e28b66 Mon Sep 17 00:00:00 2001 From: Maxence G Date: Sun, 19 Jun 2022 19:43:55 +0200 Subject: [PATCH 04/13] Put Previous/Play/Next in compact notification --- .../playback/MediaNotificationProvider.kt | 153 +++--------------- .../ultrasonic/playback/PlaybackService.kt | 3 +- .../service/MediaPlayerController.kt | 1 - 3 files changed, 24 insertions(+), 133 deletions(-) diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/MediaNotificationProvider.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/MediaNotificationProvider.kt index 2bc6e2c8..2aabdc45 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/MediaNotificationProvider.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/MediaNotificationProvider.kt @@ -7,149 +7,42 @@ package org.moire.ultrasonic.playback -/* -import android.app.Notification -import android.app.NotificationChannel -import android.app.NotificationManager import android.content.Context -import android.graphics.BitmapFactory -import android.os.Build -import android.os.Bundle import androidx.core.app.NotificationCompat -import androidx.core.graphics.drawable.IconCompat import androidx.media3.common.Player -import androidx.media3.common.util.Assertions import androidx.media3.common.util.UnstableApi -import androidx.media3.common.util.Util +import androidx.media3.common.util.Util.getAvailableCommands +import androidx.media3.session.CommandButton +import androidx.media3.session.DefaultMediaNotificationProvider import androidx.media3.session.MediaController import androidx.media3.session.MediaNotification -import androidx.media3.session.MediaNotification.ActionFactory -import org.moire.ultrasonic.R -*/ +import androidx.media3.session.MediaSession -/* -* This is a copy of DefaultMediaNotificationProvider.java with some small changes -* I have opened a bug https://github.com/androidx/media/issues/65 to make it easier to customize -* the icons and actions without creating our own copy of this class.. - */ -//@UnstableApi -/* package */ -// Disabled while getting updated -/* -internal class MediaNotificationProvider(context: Context) : - MediaNotification.Provider { - private val context: Context = context.applicationContext - private val notificationManager: NotificationManager = Assertions.checkStateNotNull( - context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager - ) - @Suppress("LongMethod") - override fun createNotification( - mediaController: MediaController, - actionFactory: ActionFactory, - onNotificationChangedCallback: MediaNotification.Provider.Callback - ): MediaNotification { - ensureNotificationChannel() - val builder: NotificationCompat.Builder = NotificationCompat.Builder( - context, - NOTIFICATION_CHANNEL_ID - ) - // Skip to previous action. - builder.addAction( - actionFactory.createMediaAction( - IconCompat.createWithResource( - context, - R.drawable.media3_notification_seek_to_previous - ), - context.getString(R.string.media3_controls_seek_to_previous_description), - ActionFactory.COMMAND_SKIP_TO_PREVIOUS - ) - ) - if (mediaController.playbackState == Player.STATE_ENDED || - !mediaController.playWhenReady - ) { - // Play action. - builder.addAction( - actionFactory.createMediaAction( - IconCompat.createWithResource(context, R.drawable.media3_notification_play), - context.getString(R.string.media3_controls_play_description), - ActionFactory.COMMAND_PLAY - ) - ) - } else { - // Pause action. - builder.addAction( - actionFactory.createMediaAction( - IconCompat.createWithResource(context, R.drawable.media3_notification_pause), - context.getString(R.string.media3_controls_pause_description), - ActionFactory.COMMAND_PAUSE - ) - ) - } - // Skip to next action. - builder.addAction( - actionFactory.createMediaAction( - IconCompat.createWithResource(context, R.drawable.media3_notification_seek_to_next), - context.getString(R.string.media3_controls_seek_to_next_description), - ActionFactory.COMMAND_SKIP_TO_NEXT - ) - ) +@UnstableApi +class MediaNotificationProvider(context: Context) : DefaultMediaNotificationProvider(context) { - // Set metadata info in the notification. - val metadata = mediaController.mediaMetadata - builder.setContentTitle(metadata.title).setContentText(metadata.artist) - if (metadata.artworkData != null) { - val artworkBitmap = - BitmapFactory.decodeByteArray(metadata.artworkData, 0, metadata.artworkData!!.size) - builder.setLargeIcon(artworkBitmap) - } - val mediaStyle = androidx.media.app.NotificationCompat.MediaStyle() - .setShowActionsInCompactView(0, 1, 2) - val notification: Notification = builder - .setContentIntent(mediaController.sessionActivity) - .setOnlyAlertOnce(true) - .setSmallIcon(getSmallIconResId()) - .setStyle(mediaStyle) - .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) - .setOngoing(false) - .build() - return MediaNotification( - NOTIFICATION_ID, - notification - ) + override fun addNotificationActions( + mediaSession: MediaSession, + mediaButtons: MutableList, + builder: NotificationCompat.Builder, + actionFactory: MediaNotification.ActionFactory + ): IntArray { + return super.addNotificationActions(mediaSession, mediaButtons, builder, actionFactory) } - override fun handleCustomAction( - mediaController: MediaController, - action: String, - extras: Bundle - ) { - // We don't handle custom commands. - } + override fun getMediaButtons( + playerCommands: Player.Commands, + customLayout: MutableList, + playWhenReady: Boolean + ): MutableList { + val commands = super.getMediaButtons(playerCommands, customLayout, playWhenReady) - private fun ensureNotificationChannel() { - if (Util.SDK_INT < Build.VERSION_CODES.O || - notificationManager.getNotificationChannel(NOTIFICATION_CHANNEL_ID) != null - ) { - return + commands.forEachIndexed { index, command -> + command.extras.putInt(COMMAND_KEY_COMPACT_VIEW_INDEX, index) } - val channel = NotificationChannel( - NOTIFICATION_CHANNEL_ID, - NOTIFICATION_CHANNEL_NAME, - NotificationManager.IMPORTANCE_LOW - ) - channel.setShowBadge(false) - notificationManager.createNotificationChannel(channel) - } - - companion object { - private const val NOTIFICATION_CHANNEL_ID = "org.moire.ultrasonic" - private const val NOTIFICATION_CHANNEL_NAME = "Ultrasonic background service" - private const val NOTIFICATION_ID = 3032 - private fun getSmallIconResId(): Int { - return R.drawable.ic_stat_ultrasonic - } + return commands } } -*/ + diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/PlaybackService.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/PlaybackService.kt index 7fee1310..1cdde030 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/PlaybackService.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/PlaybackService.kt @@ -17,7 +17,6 @@ import androidx.media3.datasource.DataSource import androidx.media3.exoplayer.DefaultRenderersFactory import androidx.media3.exoplayer.ExoPlayer import androidx.media3.exoplayer.source.DefaultMediaSourceFactory -import androidx.media3.session.DefaultMediaNotificationProvider import androidx.media3.session.MediaLibraryService import androidx.media3.session.MediaSession import io.reactivex.rxjava3.disposables.CompositeDisposable @@ -90,7 +89,7 @@ class PlaybackService : MediaLibraryService(), KoinComponent { private fun initializeSessionAndPlayer() { if (isStarted) return - setMediaNotificationProvider(DefaultMediaNotificationProvider(UApp.applicationContext())) + setMediaNotificationProvider(MediaNotificationProvider(UApp.applicationContext())) val subsonicAPIClient: SubsonicAPIClient by inject() diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerController.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerController.kt index 72003cfd..73a5ef25 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerController.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerController.kt @@ -659,7 +659,6 @@ fun Track.toMediaItem(): MediaItem { val bitrate = Settings.maxBitRate val uri = "$id|$bitrate|$filePath" - val rmd = MediaItem.RequestMetadata.Builder() .setMediaUri(uri.toUri()) .build() From 8796006ced0f751d87963e6257e3525189dd2e3a Mon Sep 17 00:00:00 2001 From: Maxence G Date: Sun, 19 Jun 2022 23:24:23 +0200 Subject: [PATCH 05/13] Fix checkstyle --- .../moire/ultrasonic/playback/MediaNotificationProvider.kt | 4 ---- 1 file changed, 4 deletions(-) diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/MediaNotificationProvider.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/MediaNotificationProvider.kt index 2aabdc45..d9cd7c36 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/MediaNotificationProvider.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/MediaNotificationProvider.kt @@ -11,14 +11,11 @@ import android.content.Context import androidx.core.app.NotificationCompat import androidx.media3.common.Player import androidx.media3.common.util.UnstableApi -import androidx.media3.common.util.Util.getAvailableCommands import androidx.media3.session.CommandButton import androidx.media3.session.DefaultMediaNotificationProvider -import androidx.media3.session.MediaController import androidx.media3.session.MediaNotification import androidx.media3.session.MediaSession - @UnstableApi class MediaNotificationProvider(context: Context) : DefaultMediaNotificationProvider(context) { @@ -45,4 +42,3 @@ class MediaNotificationProvider(context: Context) : DefaultMediaNotificationProv return commands } } - From b57a9735100f30a1bd6256b0d84abd2e8c0f7255 Mon Sep 17 00:00:00 2001 From: Maxence G Date: Sun, 19 Jun 2022 23:29:07 +0200 Subject: [PATCH 06/13] Fix static analysis --- .../org/moire/ultrasonic/playback/AutoMediaBrowserCallback.kt | 3 --- 1 file changed, 3 deletions(-) diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/AutoMediaBrowserCallback.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/AutoMediaBrowserCallback.kt index f1c8e41d..cbaa3879 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/AutoMediaBrowserCallback.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/AutoMediaBrowserCallback.kt @@ -80,9 +80,6 @@ private const val MEDIA_SEARCH_SONG_ITEM = "MEDIA_SEARCH_SONG_ITEM" private const val DISPLAY_LIMIT = 100 private const val SEARCH_LIMIT = 10 -private const val SEARCH_QUERY_PREFIX_COMPAT = "androidx://media3-session/playFromSearch" -private const val SEARCH_QUERY_PREFIX = "androidx://media3-session/setMediaUri" - /** * MediaBrowserService implementation for e.g. Android Auto */ From 23fd336ffd262ad1d56293bc50b9efc6fea9fd81 Mon Sep 17 00:00:00 2001 From: Maxence G Date: Sun, 19 Jun 2022 23:46:01 +0200 Subject: [PATCH 07/13] Fix lint --- .../kotlin/org/moire/ultrasonic/playback/PlaybackService.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/PlaybackService.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/PlaybackService.kt index 1cdde030..d61aec74 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/PlaybackService.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/PlaybackService.kt @@ -157,7 +157,7 @@ class PlaybackService : MediaLibraryService(), KoinComponent { private fun getAudioAttributes(): AudioAttributes { return AudioAttributes.Builder() .setUsage(USAGE_MEDIA) - .setContentType(CONTENT_TYPE_MUSIC) + .setContentType(C.AUDIO_CONTENT_TYPE_MUSIC) .build() } } From bc4b0aa83243ce925791d3a00ca50cdbf25e513f Mon Sep 17 00:00:00 2001 From: Maxence G Date: Sun, 19 Jun 2022 23:51:18 +0200 Subject: [PATCH 08/13] final fix checkstyle v2 --- .../main/kotlin/org/moire/ultrasonic/playback/PlaybackService.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/PlaybackService.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/PlaybackService.kt index d61aec74..3d456587 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/PlaybackService.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/PlaybackService.kt @@ -11,7 +11,6 @@ import android.content.Intent import android.os.Build import androidx.media3.common.AudioAttributes import androidx.media3.common.C -import androidx.media3.common.C.CONTENT_TYPE_MUSIC import androidx.media3.common.C.USAGE_MEDIA import androidx.media3.datasource.DataSource import androidx.media3.exoplayer.DefaultRenderersFactory From 00d7ce326cda4c0fac9a349515190678df8a9cab Mon Sep 17 00:00:00 2001 From: tzugen <67737443+tzugen@users.noreply.github.com> Date: Mon, 20 Jun 2022 09:48:28 +0200 Subject: [PATCH 09/13] Rm comment --- .../kotlin/org/moire/ultrasonic/playback/PlaybackService.kt | 5 ----- 1 file changed, 5 deletions(-) diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/PlaybackService.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/PlaybackService.kt index 3d456587..1cc994d2 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/PlaybackService.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/PlaybackService.kt @@ -42,11 +42,6 @@ class PlaybackService : MediaLibraryService(), KoinComponent { private var isStarted = false - /* - * For some reason the LocalConfiguration of MediaItem are stripped somewhere in ExoPlayer, - * and thereby customarily it is required to rebuild it.. - */ - override fun onCreate() { Timber.i("onCreate called") super.onCreate() From ab4196694366ddfeba3764ed89213a58da59b9d5 Mon Sep 17 00:00:00 2001 From: tzugen <67737443+tzugen@users.noreply.github.com> Date: Mon, 20 Jun 2022 09:49:49 +0200 Subject: [PATCH 10/13] Readd comment --- .../moire/ultrasonic/playback/AutoMediaBrowserCallback.kt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/AutoMediaBrowserCallback.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/AutoMediaBrowserCallback.kt index cbaa3879..a28a7249 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/AutoMediaBrowserCallback.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/AutoMediaBrowserCallback.kt @@ -177,7 +177,12 @@ class AutoMediaBrowserCallback(var player: Player) : return onLoadChildren(parentId) } - // https://stackoverflow.com/questions/70096715/adding-mediaitem-when-using-the-media3-library-caused-an-error + + /* + * For some reason the LocalConfiguration of MediaItem are stripped somewhere in ExoPlayer, + * and thereby customarily it is required to rebuild it.. + * See also: https://stackoverflow.com/questions/70096715/adding-mediaitem-when-using-the-media3-library-caused-an-error + */ override fun onAddMediaItems( mediaSession: MediaSession, controller: MediaSession.ControllerInfo, From 8a90e98989bc5a5b1adebfd161d4a3e09b244b87 Mon Sep 17 00:00:00 2001 From: tzugen <67737443+tzugen@users.noreply.github.com> Date: Mon, 20 Jun 2022 19:35:46 +0200 Subject: [PATCH 11/13] Update CONTRIBUTING.md --- CONTRIBUTING.md | 48 ++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 38 insertions(+), 10 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d52377f1..963f73f1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -18,18 +18,46 @@ By default Pull Request should be opened against **develop** branch, PR against ### Here are a few guidelines you should follow before submitting: 1. **License Acceptance:** All contributions must be licensed as [GNU GPLv3](LICENSE) to be accepted. -Use `git commit --signoff` to acknowledge this. -2. **App is migrating to [Kotlin](https://kotlinlang.org/) programming language:** new Pull Requests -should be written in this programming language. -3. **No Breakage:** New features or changes to existing ones must not degrade the user experience. -4. **Coding standards:** best-practices should be followed, comment generously, and avoid "clever" algorithms. +Use `git commit --signoff` to acknowledge this. +2. **No Breakage:** New features or changes to existing ones must not degrade the user experience. +3. **Coding standards:** best-practices should be followed, comment generously, and avoid "clever" algorithms. Refactoring existing messes is great, but watch out for breakage. -5. **No large PR:** Try to limit the scope of PR only to the related issue, so it will be easier to review +4. **No large PR:** Try to limit the scope of PR only to the related issue, so it will be easier to review and test. ### Pull Request Process +On each Pull Request Github runs a number of checks to make sure there are no problems. + +#### Signed commits +Commits must be signed. [See here how to set it up](https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-commits) + +#### KtLint +This programm checks if the source code is formatted correctly. +You can run it yourself locally with + +`./gradlew -Pqc ktlintFormat` + +Running this command will fix common problems and will notify you of problems it couldn't fix automatically. + +#### Detekt + +Detekt is a static analyser. It helps to find potential bugs in our code. + +You can run it yourself locally with + +`./gradlew -Pqc detekt` + +There is a "baseline" file, in which errors which have been in the code base before are noted. +Sometimes it is necessary to regenerate this file by running: + +`./gradlew -Pqc detektBaseline` + +#### Lint +Lint looks for general problems in the code or unused resources etc. +You can run it with + +`./gradlew -Pqc lintRelease` + +If there is a need to regenerate the baseline, remove `ultrasonic/lint-baseline.xml` and rerun the command. + -1. Ensure [all commits are signed-off](https://docs.github.com/en/free-pro-team@latest/github/authenticating-to-github/about-commit-signature-verification). -2. Check tests for the new code are added. -3. Check code style is passing. -4. Check code static analysis is passing. From 4efb6dcb582cc6d12d5d47835e64eed1852b4967 Mon Sep 17 00:00:00 2001 From: Maxence G Date: Mon, 20 Jun 2022 19:49:17 +0200 Subject: [PATCH 12/13] Up lint --- ultrasonic/lint-baseline.xml | 72 +++++++++++++++++++++++++++++------- 1 file changed, 58 insertions(+), 14 deletions(-) diff --git a/ultrasonic/lint-baseline.xml b/ultrasonic/lint-baseline.xml index 521d62a7..4b9c04b2 100644 --- a/ultrasonic/lint-baseline.xml +++ b/ultrasonic/lint-baseline.xml @@ -55,7 +55,7 @@ errorLine2=" ~~~~~~~~"> @@ -66,7 +66,7 @@ errorLine2=" ~~~~~~~~"> @@ -77,18 +77,7 @@ errorLine2=" ~~~~~~~"> - - - - @@ -180,6 +169,61 @@ column="1"/> + + + + + + + + + + + + + + + + + + + + From a2b9c6b9a3cefcddb552ce9065bc9b89a62cf5d4 Mon Sep 17 00:00:00 2001 From: Maxence G Date: Mon, 20 Jun 2022 19:54:26 +0200 Subject: [PATCH 13/13] Final final version good v3.2 --- .../org/moire/ultrasonic/playback/AutoMediaBrowserCallback.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/AutoMediaBrowserCallback.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/AutoMediaBrowserCallback.kt index a28a7249..1f1b8e85 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/AutoMediaBrowserCallback.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/AutoMediaBrowserCallback.kt @@ -177,7 +177,6 @@ class AutoMediaBrowserCallback(var player: Player) : return onLoadChildren(parentId) } - /* * For some reason the LocalConfiguration of MediaItem are stripped somewhere in ExoPlayer, * and thereby customarily it is required to rebuild it..