From f8f888ccad1ad127cb5f111b4ebc95dd70317f08 Mon Sep 17 00:00:00 2001
From: birdbird <6892457-tzugen@users.noreply.gitlab.com>
Date: Wed, 12 Oct 2022 11:48:49 +0000
Subject: [PATCH] Fix crashes

---
 .../ultrasonic/activity/NavigationActivity.kt |  20 ++--
 .../ultrasonic/fragment/PlayerFragment.kt     |   9 +-
 .../ultrasonic/service/DownloadService.kt     |  23 +++-
 .../moire/ultrasonic/service/DownloadTask.kt  | 103 ++++++++----------
 4 files changed, 79 insertions(+), 76 deletions(-)

diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/activity/NavigationActivity.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/activity/NavigationActivity.kt
index ee625720..6206816e 100644
--- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/activity/NavigationActivity.kt
+++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/activity/NavigationActivity.kt
@@ -22,7 +22,6 @@ import android.view.Menu
 import android.view.MenuItem
 import android.view.View
 import android.widget.ImageView
-import androidx.activity.OnBackPressedCallback
 import androidx.appcompat.app.AppCompatActivity
 import androidx.appcompat.widget.Toolbar
 import androidx.core.content.ContextCompat
@@ -221,8 +220,6 @@ class NavigationActivity : AppCompatActivity() {
             cachedServerCount = count ?: 0
             updateNavigationHeaderForServer()
         }
-
-        onBackPressedDispatcher.addCallback(this, callback)
     }
 
     override fun onResume() {
@@ -340,16 +337,13 @@ class NavigationActivity : AppCompatActivity() {
         setupActionBarWithNavController(navController, appBarConfig)
     }
 
-    val callback: OnBackPressedCallback = object : OnBackPressedCallback(
-        true
-    ) {
-        override fun handleOnBackPressed() {
-            if (drawerLayout?.isDrawerVisible(GravityCompat.START) == true) {
-                drawerLayout?.closeDrawer(GravityCompat.START)
-            } else {
-                val currentFragment = host!!.childFragmentManager.fragments.last()
-                if (currentFragment is OnBackPressedHandler) currentFragment.onBackPressed()
-            }
+    override fun onBackPressed() {
+        if (drawerLayout?.isDrawerVisible(GravityCompat.START) == true) {
+            this.drawerLayout?.closeDrawer(GravityCompat.START)
+        } else {
+            val currentFragment = host!!.childFragmentManager.fragments.last()
+            if (currentFragment is OnBackPressedHandler) currentFragment.onBackPressed()
+            else super.onBackPressed()
         }
     }
 
diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/PlayerFragment.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/PlayerFragment.kt
index 6881fe95..6a79f080 100644
--- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/PlayerFragment.kt
+++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/PlayerFragment.kt
@@ -775,11 +775,12 @@ class PlayerFragment :
         Util.toast(context, resources.getString(R.string.download_playlist_saving, playlistName))
         mediaPlayerController.suggestedPlaylistName = playlistName
 
-        ioScope.launch {
+        // The playlist can be acquired only from the main thread
+        val entries = mediaPlayerController.playlist.map {
+            it.toTrack()
+        }
 
-            val entries = mediaPlayerController.playlist.map {
-                it.toTrack()
-            }
+        ioScope.launch {
             val musicService = getMusicService()
             musicService.createPlaylist(null, playlistName, entries)
         }.invokeOnCompletion {
diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/DownloadService.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/DownloadService.kt
index d473bc54..91f19fed 100644
--- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/DownloadService.kt
+++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/DownloadService.kt
@@ -266,7 +266,7 @@ class DownloadService : Service(), KoinComponent {
         return notificationBuilder.build()
     }
 
-    @Suppress("MagicNumber")
+    @Suppress("MagicNumber", "NestedBlockDepth")
     companion object {
 
         private var startFuture: SettableFuture<DownloadService>? = null
@@ -291,7 +291,12 @@ class DownloadService : Service(), KoinComponent {
             if (save) {
                 tracks.filter { Storage.isPathExists(it.getCompleteFile()) }.forEach { track ->
                     Storage.getFromPath(track.getCompleteFile())?.let {
-                        Storage.rename(it, track.getPinnedFile())
+                        try {
+                            Storage.rename(it, track.getPinnedFile())
+                        } catch (ignored: FileAlreadyExistsException) {
+                            // Play console has revealed a crash when for some reason both files exist
+                            Storage.delete(it.path)
+                        }
                         postState(track, DownloadState.PINNED)
                     }
                 }
@@ -299,7 +304,12 @@ class DownloadService : Service(), KoinComponent {
             } else {
                 tracks.filter { Storage.isPathExists(it.getPinnedFile()) }.forEach { track ->
                     Storage.getFromPath(track.getPinnedFile())?.let {
-                        Storage.rename(it, track.getCompleteFile())
+                        try {
+                            Storage.rename(it, track.getCompleteFile())
+                        } catch (ignored: FileAlreadyExistsException) {
+                            // Play console has revealed a crash when for some reason both files exist
+                            Storage.delete(it.path)
+                        }
                         postState(track, DownloadState.DONE)
                     }
                 }
@@ -367,7 +377,12 @@ class DownloadService : Service(), KoinComponent {
             val pinnedFile = track.getPinnedFile()
             if (!Storage.isPathExists(pinnedFile)) return
             val file = Storage.getFromPath(track.getPinnedFile()) ?: return
-            Storage.rename(file, track.getCompleteFile())
+            try {
+                Storage.rename(file, track.getCompleteFile())
+            } catch (ignored: FileAlreadyExistsException) {
+                // Play console has revealed a crash when for some reason both files exist
+                Storage.delete(file.path)
+            }
             postState(track, DownloadState.DONE)
         }
 
diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/DownloadTask.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/DownloadTask.kt
index a192f598..883e8816 100644
--- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/DownloadTask.kt
+++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/DownloadTask.kt
@@ -81,73 +81,66 @@ class DownloadTask(
 
             stateChangedCallback(item, DownloadState.DOWNLOADING, null)
 
-            // Some devices seem to throw error on partial file which doesn't exist
-            val needsDownloading: Boolean
-            val duration = item.track.duration
             val fileLength = Storage.getFromPath(item.partialFile)?.length ?: 0
 
-            needsDownloading = (duration == null || duration == 0 || fileLength == 0L)
+            // Attempt partial HTTP GET, appending to the file if it exists.
+            val (inStream, isPartial) = musicService.getDownloadInputStream(
+                item.track, fileLength,
+                Settings.maxBitRate,
+                item.pinned
+            )
 
-            if (needsDownloading) {
-                // Attempt partial HTTP GET, appending to the file if it exists.
-                val (inStream, isPartial) = musicService.getDownloadInputStream(
-                    item.track, fileLength,
-                    Settings.maxBitRate,
-                    item.pinned
-                )
+            inputStream = inStream
 
-                inputStream = inStream
+            if (isPartial) {
+                Timber.i("Executed partial HTTP GET, skipping %d bytes", fileLength)
+            }
 
-                if (isPartial) {
-                    Timber.i("Executed partial HTTP GET, skipping %d bytes", fileLength)
-                }
+            outputStream = Storage.getOrCreateFileFromPath(item.partialFile)
+                .getFileOutputStream(isPartial)
 
-                outputStream = Storage.getOrCreateFileFromPath(item.partialFile)
-                    .getFileOutputStream(isPartial)
+            var lastPostTime: Long = 0
+            val len = inputStream.copyTo(outputStream) { totalBytesCopied ->
+                // Manual throttling to avoid overloading Rx
+                if (SystemClock.elapsedRealtime() - lastPostTime > REFRESH_INTERVAL) {
+                    lastPostTime = SystemClock.elapsedRealtime()
 
-                var lastPostTime: Long = 0
-                val len = inputStream.copyTo(outputStream) { totalBytesCopied ->
-                    // Manual throttling to avoid overloading Rx
-                    if (SystemClock.elapsedRealtime() - lastPostTime > REFRESH_INTERVAL) {
-                        lastPostTime = SystemClock.elapsedRealtime()
-
-                        // If the file size is unknown we can only provide null as the progress
-                        val size = item.track.size ?: 0
-                        val progress = if (size <= 0) {
-                            null
-                        } else {
-                            (totalBytesCopied * 100 / (size)).toInt()
-                        }
-
-                        stateChangedCallback(
-                            item,
-                            DownloadState.DOWNLOADING,
-                            progress
-                        )
+                    // If the file size is unknown we can only provide null as the progress
+                    val size = item.track.size ?: 0
+                    val progress = if (size <= 0) {
+                        null
+                    } else {
+                        (totalBytesCopied * 100 / (size)).toInt()
                     }
-                }
 
-                Timber.i("Downloaded %d bytes to %s", len, item.partialFile)
-
-                inputStream.close()
-                outputStream.flush()
-                outputStream.close()
-
-                if (isCancelled) {
-                    stateChangedCallback(item, DownloadState.CANCELLED, null)
-                    throw RuntimeException(
-                        String.format(
-                            Locale.ROOT, "Download of '%s' was cancelled",
-                            item
-                        )
+                    stateChangedCallback(
+                        item,
+                        DownloadState.DOWNLOADING,
+                        progress
                     )
                 }
+            }
 
-                try {
-                    item.track.cacheMetadataAndArtwork()
-                } catch (ignore: Exception) {
-                    Timber.w(ignore)
-                }
+            Timber.i("Downloaded %d bytes to %s", len, item.partialFile)
+
+            inputStream.close()
+            outputStream.flush()
+            outputStream.close()
+
+            if (isCancelled) {
+                stateChangedCallback(item, DownloadState.CANCELLED, null)
+                throw RuntimeException(
+                    String.format(
+                        Locale.ROOT, "Download of '%s' was cancelled",
+                        item
+                    )
+                )
+            }
+
+            try {
+                item.track.cacheMetadataAndArtwork()
+            } catch (ignore: Exception) {
+                Timber.w(ignore)
             }
 
             if (item.pinned) {