diff --git a/app/src/main/java/org/oxycblt/auxio/AuxioService.kt b/app/src/main/java/org/oxycblt/auxio/AuxioService.kt index 64121e6d1..a8cb344cf 100644 --- a/app/src/main/java/org/oxycblt/auxio/AuxioService.kt +++ b/app/src/main/java/org/oxycblt/auxio/AuxioService.kt @@ -43,24 +43,25 @@ class AuxioService : MediaLibraryService(), ForegroundListener { } override fun onBind(intent: Intent?): IBinder? { - handleIntent(intent) + start(intent) return super.onBind(intent) } override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { // TODO: Start command occurring from a foreign service basically implies a detached // service, we might need more handling here. - handleIntent(intent) + start(intent) 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 if (!nativeStart) { // Some foreign code started us, no guarantees about foreground stability. Figure // out what to do. mediaSessionFragment.handleNonNativeStart() } + indexingFragment.start() } override fun onTaskRemoved(rootIntent: Intent?) { diff --git a/app/src/main/java/org/oxycblt/auxio/music/MusicRepository.kt b/app/src/main/java/org/oxycblt/auxio/music/MusicRepository.kt index b8a3d2b26..7d60f825d 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/MusicRepository.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/MusicRepository.kt @@ -280,9 +280,6 @@ constructor( } logD("Registering worker $worker") indexingWorker = worker - if (indexingState == null) { - worker.requestIndex(true) - } } @Synchronized diff --git a/app/src/main/java/org/oxycblt/auxio/music/external/ExternalPlaylistManager.kt b/app/src/main/java/org/oxycblt/auxio/music/external/ExternalPlaylistManager.kt index a1171efee..81d8720cc 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/external/ExternalPlaylistManager.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/external/ExternalPlaylistManager.kt @@ -15,19 +15,19 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - + package org.oxycblt.auxio.music.external import android.content.Context import android.net.Uri import dagger.hilt.android.qualifiers.ApplicationContext -import javax.inject.Inject import org.oxycblt.auxio.music.Playlist import org.oxycblt.auxio.music.fs.Components import org.oxycblt.auxio.music.fs.DocumentPathFactory import org.oxycblt.auxio.music.fs.Path import org.oxycblt.auxio.music.fs.contentResolverSafe import org.oxycblt.auxio.util.logE +import javax.inject.Inject /** * Generic playlist file importing abstraction. @@ -92,7 +92,20 @@ constructor( return try { 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) { logE("Failed to import playlist: $e") diff --git a/app/src/main/java/org/oxycblt/auxio/music/service/IndexerServiceFragment.kt b/app/src/main/java/org/oxycblt/auxio/music/service/IndexerServiceFragment.kt index 6362def6b..c6277ba7c 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/service/IndexerServiceFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/service/IndexerServiceFragment.kt @@ -72,13 +72,19 @@ constructor( fun release() { contentObserver.release() - musicSettings.registerListener(this) - musicRepository.addIndexingListener(this) - musicRepository.addUpdateListener(this) + musicRepository.unregisterWorker(this) musicRepository.removeIndexingListener(this) + musicRepository.removeUpdateListener(this) + musicSettings.unregisterListener(this) foregroundListener = null } + fun start() { + if (musicRepository.indexingState == null) { + requestIndex(true) + } + } + fun createNotification(post: (IndexerNotification?) -> Unit) { val state = musicRepository.indexingState if (state is IndexingState.Indexing) { diff --git a/app/src/main/java/org/oxycblt/auxio/widgets/WidgetProvider.kt b/app/src/main/java/org/oxycblt/auxio/widgets/WidgetProvider.kt index 2ee0fcafd..80b8758e8 100644 --- a/app/src/main/java/org/oxycblt/auxio/widgets/WidgetProvider.kt +++ b/app/src/main/java/org/oxycblt/auxio/widgets/WidgetProvider.kt @@ -101,17 +101,51 @@ class WidgetProvider : AppWidgetProvider() { SizeF(180f, 272f) to newThinPaneLayout(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. val awm = AppWidgetManager.getInstance(context) val component = ComponentName(context, this::class.java) - try { - awm.updateAppWidgetCompat(context, component, views) - logD("Successfully updated RemoteViews layout") - } catch (e: Exception) { - // Layout update failed, gracefully degrade to the default widget. - logW("Unable to update widget: $e") - reset(context, uiSettings) + while (victims.size > 0) { + try { + awm.updateAppWidgetCompat(context, component, views) + 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) { + // Layout update failed, gracefully degrade to the default widget. + logW("Unable to update widget: $e") + 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) } /** @@ -288,16 +322,17 @@ class WidgetProvider : AppWidgetProvider() { context.getString( R.string.desc_album_cover, state.song.album.name.resolve(context))) } else { - // We are unable to use the typical placeholder cover with the song item due to - // 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)) + discardCover(context) } 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 { // 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