Skip to content

Commit

Permalink
Merge pull request #885 from OxygenCobalt/v3.6.0
Browse files Browse the repository at this point in the history
V3.6.0
  • Loading branch information
OxygenCobalt authored Oct 14, 2024
2 parents 99c11bd + e23643f commit 18f96ed
Show file tree
Hide file tree
Showing 47 changed files with 2,729 additions and 2,081 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/android.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ jobs:
runs-on: ubuntu-latest

steps:
- name: Install ninja-build
run: sudo apt-get install -y ninja-build
- name: Clone repository
uses: actions/checkout@v3
- name: Clone submodules
Expand Down
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,23 @@
# Changelog

## 3.6.0

#### What's New
- Added support for playback from google assistant

#### What's Improved
- Home and detail UIs in Android Auto now reflect app sort settings
- Album view now shows discs in android auto

#### What's Fixed
- Fixed playback briefly pausing when adding songs to playlist
- Fixed media lists in Android Auto being truncated in some cases
- Possibly fixed duplicated song items depending on album/all children
- Possibly fixed truncated tab lists in android auto

#### Dev/Meta
- Moved to raw media session apis rather than media3 session

## 3.5.3

#### What's New
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
<h1 align="center"><b>Auxio</b></h1>
<h4 align="center">A simple, rational music player for android.</h4>
<p align="center">
<a href="https://github.com/oxygencobalt/Auxio/releases/tag/v3.5.3">
<img alt="Latest Version" src="https://img.shields.io/static/v1?label=tag&message=v3.5.3&color=64B5F6&style=flat">
<a href="https://github.com/oxygencobalt/Auxio/releases/tag/v3.6.0">
<img alt="Latest Version" src="https://img.shields.io/static/v1?label=tag&message=v3.6.0&color=64B5F6&style=flat">
</a>
<a href="https://github.com/oxygencobalt/Auxio/releases/">
<img alt="Releases" src="https://img.shields.io/github/downloads/OxygenCobalt/Auxio/total.svg?color=4B95DE&style=flat">
Expand Down
11 changes: 8 additions & 3 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ android {

defaultConfig {
applicationId namespace
versionName "3.5.3"
versionCode 49
versionName "3.6.0"
versionCode 50

minSdk 24
targetSdk 34
Expand Down Expand Up @@ -114,6 +114,9 @@ dependencies {
// Media
implementation "androidx.media:media:1.7.0"

// Android Auto
implementation "androidx.car.app:app:1.4.0"

// Preferences
implementation "androidx.preference:preference-ktx:1.2.1"

Expand All @@ -126,7 +129,6 @@ dependencies {
// --- THIRD PARTY ---

// Exoplayer (Vendored)
implementation project(":media-lib-session")
implementation project(":media-lib-exoplayer")
implementation project(":media-lib-decoder-ffmpeg")
coreLibraryDesugaring "com.android.tools:desugar_jdk_libs:2.0.4"
Expand Down Expand Up @@ -154,6 +156,9 @@ dependencies {
// Tasker integration
implementation 'com.joaomgcd:taskerpluginlibrary:0.4.10'

// Fuzzy search
implementation 'org.apache.commons:commons-text:1.9'

// Testing
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.12'
testImplementation "junit:junit:4.13.2"
Expand Down
1 change: 0 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,6 @@
android:exported="true"
android:roundIcon="@mipmap/ic_launcher">
<intent-filter>
<action android:name="androidx.media3.session.MediaSessionService"/>
<action android:name="android.media.browse.MediaBrowserService"/>
</intent-filter>
</service>
Expand Down
150 changes: 121 additions & 29 deletions app/src/main/java/org/oxycblt/auxio/AuxioService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,32 +19,41 @@
package org.oxycblt.auxio

import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.os.IBinder
import android.support.v4.media.MediaBrowserCompat
import android.support.v4.media.MediaBrowserCompat.MediaItem
import androidx.annotation.StringRes
import androidx.core.app.NotificationChannelCompat
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.core.app.ServiceCompat
import androidx.media3.session.MediaLibraryService
import androidx.media3.session.MediaSession
import androidx.media.MediaBrowserServiceCompat
import androidx.media.utils.MediaConstants
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
import org.oxycblt.auxio.music.service.IndexerServiceFragment
import org.oxycblt.auxio.playback.service.MediaSessionServiceFragment
import org.oxycblt.auxio.music.service.MusicServiceFragment
import org.oxycblt.auxio.playback.service.PlaybackServiceFragment
import org.oxycblt.auxio.util.logD

@AndroidEntryPoint
class AuxioService : MediaLibraryService(), ForegroundListener {
@Inject lateinit var mediaSessionFragment: MediaSessionServiceFragment
class AuxioService :
MediaBrowserServiceCompat(), ForegroundListener, MusicServiceFragment.Invalidator {
@Inject lateinit var playbackFragmentFactory: PlaybackServiceFragment.Factory
private lateinit var playbackFragment: PlaybackServiceFragment

@Inject lateinit var indexingFragment: IndexerServiceFragment
@Inject lateinit var musicFragmentFactory: MusicServiceFragment.Factory
private lateinit var musicFragment: MusicServiceFragment

@SuppressLint("WrongConstant")
override fun onCreate() {
super.onCreate()
mediaSessionFragment.attach(this, this)
indexingFragment.attach(this)
}

override fun onBind(intent: Intent?): IBinder? {
onHandleForeground(intent)
return super.onBind(intent)
playbackFragment = playbackFragmentFactory.create(this, this)
sessionToken = playbackFragment.attach()
musicFragment = musicFragmentFactory.create(this, this, this)
musicFragment.attach()
}

override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
Expand All @@ -54,42 +63,81 @@ class AuxioService : MediaLibraryService(), ForegroundListener {
return super.onStartCommand(intent, flags, startId)
}

override fun onBind(intent: Intent): IBinder? {
onHandleForeground(intent)
return super.onBind(intent)
}

private fun onHandleForeground(intent: Intent?) {
val startId = intent?.getIntExtra(INTENT_KEY_START_ID, -1) ?: -1
indexingFragment.start()
mediaSessionFragment.start(startId)
musicFragment.start()
playbackFragment.start(startId)
}

override fun onTaskRemoved(rootIntent: Intent?) {
super.onTaskRemoved(rootIntent)
mediaSessionFragment.handleTaskRemoved()
playbackFragment.handleTaskRemoved()
}

override fun onDestroy() {
super.onDestroy()
indexingFragment.release()
mediaSessionFragment.release()
musicFragment.release()
playbackFragment.release()
}

override fun onGetSession(controllerInfo: MediaSession.ControllerInfo): MediaLibrarySession =
mediaSessionFragment.mediaSession
override fun onGetRoot(
clientPackageName: String,
clientUid: Int,
rootHints: Bundle?
): BrowserRoot {
return musicFragment.getRoot()
}

override fun onLoadItem(itemId: String, result: Result<MediaItem>) {
musicFragment.getItem(itemId, result)
}

override fun onLoadChildren(parentId: String, result: Result<MutableList<MediaItem>>) {
val maximumRootChildLimit = getRootChildrenLimit()
musicFragment.getChildren(parentId, maximumRootChildLimit, result, null)
}

override fun onLoadChildren(
parentId: String,
result: Result<MutableList<MediaItem>>,
options: Bundle
) {
val maximumRootChildLimit = getRootChildrenLimit()
musicFragment.getChildren(parentId, maximumRootChildLimit, result, options.getPage())
}

override fun onUpdateNotification(session: MediaSession, startInForegroundRequired: Boolean) {
updateForeground(ForegroundListener.Change.MEDIA_SESSION)
override fun onSearch(query: String, extras: Bundle?, result: Result<MutableList<MediaItem>>) {
musicFragment.search(query, result, extras?.getPage())
}

private fun getRootChildrenLimit(): Int {
return browserRootHints?.getInt(
MediaConstants.BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_LIMIT, 4)
?: 4
}

private fun Bundle.getPage(): MusicServiceFragment.Page? {
val page = getInt(MediaBrowserCompat.EXTRA_PAGE, -1).takeIf { it >= 0 } ?: return null
val pageSize =
getInt(MediaBrowserCompat.EXTRA_PAGE_SIZE, -1).takeIf { it > 0 } ?: return null
return MusicServiceFragment.Page(page, pageSize)
}

override fun updateForeground(change: ForegroundListener.Change) {
if (mediaSessionFragment.hasNotification()) {
val mediaNotification = playbackFragment.notification
if (mediaNotification != null) {
if (change == ForegroundListener.Change.MEDIA_SESSION) {
mediaSessionFragment.createNotification {
startForeground(it.notificationId, it.notification)
isForeground = true
}
startForeground(mediaNotification.code, mediaNotification.build())
}
// Nothing changed, but don't show anything music related since we can always
// index during playback.
} else {
indexingFragment.createNotification {
musicFragment.createNotification {
if (it != null) {
startForeground(it.code, it.build())
isForeground = true
Expand All @@ -101,6 +149,11 @@ class AuxioService : MediaLibraryService(), ForegroundListener {
}
}

override fun invalidateMusic(mediaId: String) {
logD(mediaId)
notifyChildrenChanged(mediaId)
}

companion object {
var isForeground = false
private set
Expand All @@ -118,3 +171,42 @@ interface ForegroundListener {
INDEXER
}
}

/**
* Wrapper around [NotificationCompat.Builder] intended for use for [NotificationCompat]s that
* signal a Service's ongoing foreground state.
*
* @author Alexander Capehart (OxygenCobalt)
*/
abstract class ForegroundServiceNotification(context: Context, info: ChannelInfo) :
NotificationCompat.Builder(context, info.id) {
private val notificationManager = NotificationManagerCompat.from(context)

init {
// Set up the notification channel. Foreground notifications are non-substantial, and
// thus make no sense to have lights, vibration, or lead to a notification badge.
val channel =
NotificationChannelCompat.Builder(info.id, NotificationManagerCompat.IMPORTANCE_LOW)
.setName(context.getString(info.nameRes))
.setLightsEnabled(false)
.setVibrationEnabled(false)
.setShowBadge(false)
.build()
notificationManager.createNotificationChannel(channel)
}

/**
* The code used to identify this notification.
*
* @see NotificationManagerCompat.notify
*/
abstract val code: Int

/**
* Reduced representation of a [NotificationChannelCompat].
*
* @param id The ID of the channel.
* @param nameRes A string resource ID corresponding to the human-readable name of this channel.
*/
data class ChannelInfo(val id: String, @StringRes val nameRes: Int)
}
2 changes: 1 addition & 1 deletion app/src/main/java/org/oxycblt/auxio/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ class MainActivity : AppCompatActivity() {
}
}

override fun onNewIntent(intent: Intent?) {
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
startIntentAction(intent)
}
Expand Down
Loading

0 comments on commit 18f96ed

Please sign in to comment.