diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/imageloader/ImageLoader.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/imageloader/ImageLoader.kt index 4b28e82c..bb713738 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/imageloader/ImageLoader.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/imageloader/ImageLoader.kt @@ -21,7 +21,7 @@ import org.moire.ultrasonic.api.subsonic.throwOnFailure import org.moire.ultrasonic.api.subsonic.toStreamResponse import org.moire.ultrasonic.domain.MusicDirectory import org.moire.ultrasonic.util.FileUtil -import org.moire.ultrasonic.util.Util +import org.moire.ultrasonic.util.Util.safeClose import timber.log.Timber /** @@ -185,10 +185,10 @@ class ImageLoader( outputStream = FileOutputStream(file) outputStream.write(bytes) } finally { - Util.close(outputStream) + outputStream.safeClose() } } finally { - Util.close(inputStream) + inputStream.safeClose() } } } diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/log/FileLoggerTree.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/log/FileLoggerTree.kt index 0b8d894b..c7a69856 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/log/FileLoggerTree.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/log/FileLoggerTree.kt @@ -6,7 +6,6 @@ import java.text.SimpleDateFormat import java.util.Date import java.util.Locale import org.moire.ultrasonic.util.FileUtil -import org.moire.ultrasonic.util.Util import timber.log.Timber /** @@ -38,7 +37,7 @@ class FileLoggerTree : Timber.DebugTree() { // Using base class DebugTree here, we don't want to try to log this into file super.log(6, TAG, String.format("Failed to write log to %s", file), x) } finally { - if (writer != null) Util.close(writer) + writer.safeClose() } } diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/DownloadFile.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/DownloadFile.kt index 86b9aeab..7c1b8f88 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/DownloadFile.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/DownloadFile.kt @@ -28,6 +28,7 @@ import org.moire.ultrasonic.util.CancellableTask import org.moire.ultrasonic.util.FileUtil import org.moire.ultrasonic.util.Settings import org.moire.ultrasonic.util.Util +import org.moire.ultrasonic.util.Util.safeClose import timber.log.Timber /** @@ -135,9 +136,9 @@ class DownloadFile( fun delete() { cancelDownload() - Util.delete(partialFile) - Util.delete(completeFile) - Util.delete(saveFile) + FileUtil.delete(partialFile) + FileUtil.delete(completeFile) + FileUtil.delete(saveFile) Util.scanMedia(saveFile) } @@ -156,11 +157,11 @@ class DownloadFile( fun cleanup(): Boolean { var ok = true if (completeFile.exists() || saveFile.exists()) { - ok = Util.delete(partialFile) + ok = FileUtil.delete(partialFile) } if (saveFile.exists()) { - ok = ok and Util.delete(completeFile) + ok = ok and FileUtil.delete(completeFile) } return ok @@ -182,14 +183,14 @@ class DownloadFile( private fun doPendingRename() { try { if (saveWhenDone) { - Util.renameFile(completeFile, saveFile) + FileUtil.renameFile(completeFile, saveFile) saveWhenDone = false } else if (completeWhenDone) { if (save) { - Util.renameFile(partialFile, saveFile) + FileUtil.renameFile(partialFile, saveFile) Util.scanMedia(saveFile) } else { - Util.renameFile(partialFile, completeFile) + FileUtil.renameFile(partialFile, completeFile) } completeWhenDone = false } @@ -221,7 +222,7 @@ class DownloadFile( if (isPlaying) { saveWhenDone = true } else { - Util.renameFile(completeFile, saveFile) + FileUtil.renameFile(completeFile, saveFile) } } else { Timber.i("%s already exists. Skipping.", completeFile) @@ -291,16 +292,16 @@ class DownloadFile( completeWhenDone = true } else { if (save) { - Util.renameFile(partialFile, saveFile) + FileUtil.renameFile(partialFile, saveFile) Util.scanMedia(saveFile) } else { - Util.renameFile(partialFile, completeFile) + FileUtil.renameFile(partialFile, completeFile) } } } catch (all: Exception) { - Util.close(outputStream) - Util.delete(completeFile) - Util.delete(saveFile) + outputStream.safeClose() + FileUtil.delete(completeFile) + FileUtil.delete(saveFile) if (!isCancelled) { isFailed = true if (retryCount > 1) { @@ -313,8 +314,8 @@ class DownloadFile( Timber.w(all, "Failed to download '%s'.", song) } } finally { - Util.close(inputStream) - Util.close(outputStream) + inputStream.safeClose() + outputStream.safeClose() CacheCleaner().cleanSpace() downloader.checkDownloads() } diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/OfflineMusicService.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/OfflineMusicService.kt index d705119a..b16e198d 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/OfflineMusicService.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/OfflineMusicService.kt @@ -41,7 +41,7 @@ import org.moire.ultrasonic.domain.Share import org.moire.ultrasonic.domain.UserInfo import org.moire.ultrasonic.util.Constants import org.moire.ultrasonic.util.FileUtil -import org.moire.ultrasonic.util.Util +import org.moire.ultrasonic.util.Util.safeClose import timber.log.Timber // TODO: There are quite a number of deeply nested and complicated functions in this class.. @@ -213,8 +213,8 @@ class OfflineMusicService : MusicService, KoinComponent { } playlist } finally { - Util.close(buffer) - Util.close(reader) + buffer.safeClose() + reader.safeClose() } } diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/FileUtil.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/FileUtil.kt index c384309c..b8c9694e 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/FileUtil.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/FileUtil.kt @@ -27,8 +27,10 @@ import java.util.TreeSet import java.util.regex.Pattern import org.moire.ultrasonic.app.UApp import org.moire.ultrasonic.domain.MusicDirectory +import org.moire.ultrasonic.util.Util.safeClose import timber.log.Timber +@Suppress("TooManyFunctions") object FileUtil { private val FILE_SYSTEM_UNSAFE = arrayOf("/", "\\", "..", ":", "\"", "?", "*", "<", ">", "|") @@ -416,7 +418,7 @@ object FileUtil { Timber.w("Failed to serialize object to %s", file) false } finally { - Util.close(out) + out.safeClose() } } @@ -438,7 +440,7 @@ object FileUtil { Timber.w(all, "Failed to deserialize object from %s", file) null } finally { - Util.close(inStream) + inStream.safeClose() } } @@ -466,8 +468,55 @@ object FileUtil { Timber.w("Failed to save playlist: %s", name) throw e } finally { - bw.close() - fw.close() + bw.safeClose() + fw.safeClose() } } + + @Throws(IOException::class) + fun atomicCopy(from: File, to: File) { + val tmp = File(String.format(Locale.ROOT, "%s.tmp", to.path)) + val input = FileInputStream(from) + val out = FileOutputStream(tmp) + try { + input.channel.transferTo(0, from.length(), out.channel) + out.close() + if (!tmp.renameTo(to)) { + throw IOException( + String.format(Locale.ROOT, "Failed to rename %s to %s", tmp, to) + ) + } + Timber.i("Copied %s to %s", from, to) + } catch (x: IOException) { + out.safeClose() + delete(to) + throw x + } finally { + input.safeClose() + out.safeClose() + delete(tmp) + } + } + + @JvmStatic + @Throws(IOException::class) + fun renameFile(from: File, to: File) { + if (from.renameTo(to)) { + Timber.i("Renamed %s to %s", from, to) + } else { + atomicCopy(from, to) + } + } + + @JvmStatic + fun delete(file: File?): Boolean { + if (file != null && file.exists()) { + if (!file.delete()) { + Timber.w("Failed to delete file %s", file) + return false + } + Timber.i("Deleted file %s", file) + } + return true + } } diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/SubsonicUncaughtExceptionHandler.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/SubsonicUncaughtExceptionHandler.kt index 658d2943..a4265017 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/SubsonicUncaughtExceptionHandler.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/SubsonicUncaughtExceptionHandler.kt @@ -4,6 +4,7 @@ import android.content.Context import android.os.Build import java.io.File import java.io.PrintWriter +import org.moire.ultrasonic.util.Util.safeClose import timber.log.Timber /** @@ -34,7 +35,7 @@ class SubsonicUncaughtExceptionHandler( } catch (x: Throwable) { Timber.e(x, "Failed to write stack trace to %s", file) } finally { - Util.close(printWriter) + printWriter.safeClose() defaultHandler?.uncaughtException(thread, throwable) } } diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/Util.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/Util.kt index d8366c5e..9a16e4c5 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/Util.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/Util.kt @@ -43,9 +43,6 @@ import androidx.annotation.AnyRes import androidx.media.utils.MediaConstants import java.io.Closeable import java.io.File -import java.io.FileInputStream -import java.io.FileOutputStream -import java.io.IOException import java.io.UnsupportedEncodingException import java.security.MessageDigest import java.text.DecimalFormat @@ -114,62 +111,6 @@ object Util { } } - @Throws(IOException::class) - fun atomicCopy(from: File, to: File) { - val tmp = File(String.format(Locale.ROOT, "%s.tmp", to.path)) - val input = FileInputStream(from) - val out = FileOutputStream(tmp) - try { - input.channel.transferTo(0, from.length(), out.channel) - out.close() - if (!tmp.renameTo(to)) { - throw IOException( - String.format(Locale.ROOT, "Failed to rename %s to %s", tmp, to) - ) - } - Timber.i("Copied %s to %s", from, to) - } catch (x: IOException) { - close(out) - delete(to) - throw x - } finally { - close(input) - close(out) - delete(tmp) - } - } - - @JvmStatic - @Throws(IOException::class) - fun renameFile(from: File, to: File) { - if (from.renameTo(to)) { - Timber.i("Renamed %s to %s", from, to) - } else { - atomicCopy(from, to) - } - } - - @JvmStatic - fun close(closeable: Closeable?) { - try { - closeable?.close() - } catch (_: Throwable) { - // Ignored - } - } - - @JvmStatic - fun delete(file: File?): Boolean { - if (file != null && file.exists()) { - if (!file.delete()) { - Timber.w("Failed to delete file %s", file) - return false - } - Timber.i("Deleted file %s", file) - } - return true - } - @JvmStatic @JvmOverloads fun toast(context: Context?, messageId: Int, shortDuration: Boolean = true) { @@ -957,8 +898,22 @@ object Util { return context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager } + /** + * Small data class to store information about the current network + **/ data class NetworkInfo( var connected: Boolean = false, var unmetered: Boolean = false ) + + /** + * Closes a Closeable while ignoring any errors. + **/ + fun Closeable?.safeClose() { + try { + this?.close() + } catch (_: Exception) { + // Ignored + } + } }