Merge branch 'playback' into dev
This commit is contained in:
commit
f0bf7af7b4
5 changed files with 76 additions and 24 deletions
|
@ -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?) {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue