Merge branch 'playback' into dev

This commit is contained in:
Alexander Capehart 2024-07-29 21:38:36 -06:00
commit f0bf7af7b4
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
5 changed files with 76 additions and 24 deletions

View file

@ -43,24 +43,25 @@ class AuxioService : MediaLibraryService(), ForegroundListener {
} }
override fun onBind(intent: Intent?): IBinder? { override fun onBind(intent: Intent?): IBinder? {
handleIntent(intent) start(intent)
return super.onBind(intent) return super.onBind(intent)
} }
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
// TODO: Start command occurring from a foreign service basically implies a detached // TODO: Start command occurring from a foreign service basically implies a detached
// service, we might need more handling here. // service, we might need more handling here.
handleIntent(intent) start(intent)
return super.onStartCommand(intent, flags, startId) return super.onStartCommand(intent, flags, startId)
} }
private fun handleIntent(intent: Intent?) { private fun start(intent: Intent?) {
val nativeStart = intent?.getBooleanExtra(INTENT_KEY_NATIVE_START, false) ?: false val nativeStart = intent?.getBooleanExtra(INTENT_KEY_NATIVE_START, false) ?: false
if (!nativeStart) { if (!nativeStart) {
// Some foreign code started us, no guarantees about foreground stability. Figure // Some foreign code started us, no guarantees about foreground stability. Figure
// out what to do. // out what to do.
mediaSessionFragment.handleNonNativeStart() mediaSessionFragment.handleNonNativeStart()
} }
indexingFragment.start()
} }
override fun onTaskRemoved(rootIntent: Intent?) { override fun onTaskRemoved(rootIntent: Intent?) {

View file

@ -280,9 +280,6 @@ constructor(
} }
logD("Registering worker $worker") logD("Registering worker $worker")
indexingWorker = worker indexingWorker = worker
if (indexingState == null) {
worker.requestIndex(true)
}
} }
@Synchronized @Synchronized

View file

@ -21,13 +21,13 @@ package org.oxycblt.auxio.music.external
import android.content.Context import android.content.Context
import android.net.Uri import android.net.Uri
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
import javax.inject.Inject
import org.oxycblt.auxio.music.Playlist import org.oxycblt.auxio.music.Playlist
import org.oxycblt.auxio.music.fs.Components import org.oxycblt.auxio.music.fs.Components
import org.oxycblt.auxio.music.fs.DocumentPathFactory import org.oxycblt.auxio.music.fs.DocumentPathFactory
import org.oxycblt.auxio.music.fs.Path import org.oxycblt.auxio.music.fs.Path
import org.oxycblt.auxio.music.fs.contentResolverSafe import org.oxycblt.auxio.music.fs.contentResolverSafe
import org.oxycblt.auxio.util.logE import org.oxycblt.auxio.util.logE
import javax.inject.Inject
/** /**
* Generic playlist file importing abstraction. * Generic playlist file importing abstraction.
@ -92,7 +92,20 @@ constructor(
return try { return try {
context.contentResolverSafe.openInputStream(uri)?.use { context.contentResolverSafe.openInputStream(uri)?.use {
return m3u.read(it, filePath.directory) val imported = m3u.read(it, filePath.directory) ?: return null
val name = imported.name
if (name != null) {
return imported
}
// Strip extension
val fileName = filePath.name ?: return imported
val split = fileName.split(".")
var newName = split[0]
// Replace delimiters with space
newName = newName.replace(Regex("[_-]"), " ")
// Replace long stretches of whitespace with one space
newName = newName.replace(Regex("\\s+"), " ")
return ImportedPlaylist(newName, imported.paths)
} }
} catch (e: Exception) { } catch (e: Exception) {
logE("Failed to import playlist: $e") logE("Failed to import playlist: $e")

View file

@ -72,13 +72,19 @@ constructor(
fun release() { fun release() {
contentObserver.release() contentObserver.release()
musicSettings.registerListener(this) musicRepository.unregisterWorker(this)
musicRepository.addIndexingListener(this)
musicRepository.addUpdateListener(this)
musicRepository.removeIndexingListener(this) musicRepository.removeIndexingListener(this)
musicRepository.removeUpdateListener(this)
musicSettings.unregisterListener(this)
foregroundListener = null foregroundListener = null
} }
fun start() {
if (musicRepository.indexingState == null) {
requestIndex(true)
}
}
fun createNotification(post: (IndexerNotification?) -> Unit) { fun createNotification(post: (IndexerNotification?) -> Unit) {
val state = musicRepository.indexingState val state = musicRepository.indexingState
if (state is IndexingState.Indexing) { if (state is IndexingState.Indexing) {

View file

@ -101,18 +101,52 @@ class WidgetProvider : AppWidgetProvider() {
SizeF(180f, 272f) to newThinPaneLayout(context, uiSettings, state), SizeF(180f, 272f) to newThinPaneLayout(context, uiSettings, state),
SizeF(304f, 272f) to newWidePaneLayout(context, uiSettings, state)) SizeF(304f, 272f) to newWidePaneLayout(context, uiSettings, state))
// This is the order in which we will disable cover art layouts if they exceed the
// maximum bitmap memory usage. (See the comment in the loop below for more info.)
val victims =
mutableSetOf(
R.layout.widget_wafer_thin,
R.layout.widget_wafer_wide,
R.layout.widget_pane_thin,
R.layout.widget_pane_wide,
R.layout.widget_docked_thin,
R.layout.widget_docked_wide,
)
// Manually update AppWidgetManager with the new views. // Manually update AppWidgetManager with the new views.
val awm = AppWidgetManager.getInstance(context) val awm = AppWidgetManager.getInstance(context)
val component = ComponentName(context, this::class.java) val component = ComponentName(context, this::class.java)
while (victims.size > 0) {
try { try {
awm.updateAppWidgetCompat(context, component, views) awm.updateAppWidgetCompat(context, component, views)
logD("Successfully updated RemoteViews layout") logD("Successfully updated RemoteViews layout")
return
} catch (e: IllegalArgumentException) {
val msg = e.message ?: return
if (!msg.startsWith(
"RemoteViews for widget update exceeds maximum bitmap memory usage")) {
throw e
}
// Some android devices on Android 12-14 suffer from a bug where the maximum bitmap
// size calculation does not factor in bitmaps shared across multiple RemoteView
// forms.
// To mitigate an outright crash, progressively disable layouts that contain cover
// art
// in order of least to most commonly used until it actually works.
val victim = victims.first()
val view = views.entries.find { it.value.layoutId == victim } ?: continue
view.value.discardCover(context)
victims.remove(victim)
} catch (e: Exception) { } catch (e: Exception) {
// Layout update failed, gracefully degrade to the default widget. // Layout update failed, gracefully degrade to the default widget.
logW("Unable to update widget: $e") logW("Unable to update widget: $e")
reset(context, uiSettings) reset(context, uiSettings)
} }
} }
// We flat-out cannot fit the bitmap into the widget. Weird.
logW("Unable to update widget: Bitmap too large")
reset(context, uiSettings)
}
/** /**
* Revert to the default layout that displays "No music playing". * Revert to the default layout that displays "No music playing".
@ -288,16 +322,17 @@ class WidgetProvider : AppWidgetProvider() {
context.getString( context.getString(
R.string.desc_album_cover, state.song.album.name.resolve(context))) R.string.desc_album_cover, state.song.album.name.resolve(context)))
} else { } else {
// We are unable to use the typical placeholder cover with the song item due to discardCover(context)
// limitations with the corner radius. Instead use a custom-made album icon as the
// placeholder.
setImageViewResource(R.id.widget_cover, R.drawable.ic_remote_default_cover_24)
setContentDescription(R.id.widget_cover, context.getString(R.string.desc_no_cover))
} }
return this return this
} }
private fun RemoteViews.discardCover(context: Context) {
setImageViewResource(R.id.widget_cover, R.drawable.ic_remote_default_cover_24)
setContentDescription(R.id.widget_cover, context.getString(R.string.desc_no_cover))
}
private fun RemoteViews.setupFillingCover(uiSettings: UISettings): RemoteViews { private fun RemoteViews.setupFillingCover(uiSettings: UISettings): RemoteViews {
// Below API 31, enable a rounded background only if round mode is enabled. // Below API 31, enable a rounded background only if round mode is enabled.
// On API 31+, the background should always be round in order to fit in with other // On API 31+, the background should always be round in order to fit in with other