CommunicationError to Object with static methods

This commit is contained in:
tzugen 2021-11-13 13:43:41 +01:00
parent ed152fa52a
commit 1ed9360bc7
No known key found for this signature in database
GPG Key ID: 61E9C34BC10EC930
6 changed files with 110 additions and 124 deletions

View File

@ -20,7 +20,6 @@ package org.moire.ultrasonic.util;
import android.app.Activity; import android.app.Activity;
import android.os.Handler; import android.os.Handler;
import org.moire.ultrasonic.service.CommunicationErrorUtil;
/** /**
* @author Sindre Mehus * @author Sindre Mehus
@ -54,12 +53,12 @@ public abstract class BackgroundTask<T> implements ProgressListener
protected void error(Throwable error) protected void error(Throwable error)
{ {
CommunicationErrorUtil.Companion.handleError(error, activity); CommunicationError.handleError(error, activity);
} }
protected String getErrorMessage(Throwable error) protected String getErrorMessage(Throwable error)
{ {
return CommunicationErrorUtil.Companion.getErrorMessage(error, activity); return CommunicationError.getErrorMessage(error, activity);
} }
@Override @Override

View File

@ -18,9 +18,9 @@ import org.koin.core.component.inject
import org.moire.ultrasonic.data.ActiveServerProvider import org.moire.ultrasonic.data.ActiveServerProvider
import org.moire.ultrasonic.data.ServerSetting import org.moire.ultrasonic.data.ServerSetting
import org.moire.ultrasonic.domain.MusicFolder import org.moire.ultrasonic.domain.MusicFolder
import org.moire.ultrasonic.service.CommunicationErrorUtil
import org.moire.ultrasonic.service.MusicService import org.moire.ultrasonic.service.MusicService
import org.moire.ultrasonic.service.MusicServiceFactory import org.moire.ultrasonic.service.MusicServiceFactory
import org.moire.ultrasonic.util.CommunicationError
import org.moire.ultrasonic.util.Settings import org.moire.ultrasonic.util.Settings
/** /**
@ -94,7 +94,7 @@ open class GenericListModel(application: Application) :
private fun handleException(exception: Exception, context: Context) { private fun handleException(exception: Exception, context: Context) {
Handler(Looper.getMainLooper()).post { Handler(Looper.getMainLooper()).post {
CommunicationErrorUtil.handleError(exception, context) CommunicationError.handleError(exception, context)
} }
} }

View File

@ -46,6 +46,7 @@ import java.util.ArrayList
import java.util.Date import java.util.Date
import java.util.LinkedList import java.util.LinkedList
import java.util.Locale import java.util.Locale
import java.util.concurrent.CancellationException
import java.util.concurrent.Executors import java.util.concurrent.Executors
import java.util.concurrent.ScheduledExecutorService import java.util.concurrent.ScheduledExecutorService
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
@ -67,7 +68,6 @@ import org.moire.ultrasonic.domain.RepeatMode
import org.moire.ultrasonic.featureflags.Feature import org.moire.ultrasonic.featureflags.Feature
import org.moire.ultrasonic.featureflags.FeatureStorage import org.moire.ultrasonic.featureflags.FeatureStorage
import org.moire.ultrasonic.fragment.FragmentTitle.Companion.setTitle import org.moire.ultrasonic.fragment.FragmentTitle.Companion.setTitle
import org.moire.ultrasonic.service.CommunicationErrorUtil
import org.moire.ultrasonic.service.DownloadFile import org.moire.ultrasonic.service.DownloadFile
import org.moire.ultrasonic.service.LocalMediaPlayer import org.moire.ultrasonic.service.LocalMediaPlayer
import org.moire.ultrasonic.service.MediaPlayerController import org.moire.ultrasonic.service.MediaPlayerController
@ -77,6 +77,7 @@ import org.moire.ultrasonic.subsonic.ImageLoaderProvider
import org.moire.ultrasonic.subsonic.NetworkAndStorageChecker import org.moire.ultrasonic.subsonic.NetworkAndStorageChecker
import org.moire.ultrasonic.subsonic.ShareHandler import org.moire.ultrasonic.subsonic.ShareHandler
import org.moire.ultrasonic.util.CancellationToken import org.moire.ultrasonic.util.CancellationToken
import org.moire.ultrasonic.util.CommunicationError
import org.moire.ultrasonic.util.Constants import org.moire.ultrasonic.util.Constants
import org.moire.ultrasonic.util.Settings import org.moire.ultrasonic.util.Settings
import org.moire.ultrasonic.util.Util import org.moire.ultrasonic.util.Util
@ -84,7 +85,6 @@ import org.moire.ultrasonic.view.AutoRepeatButton
import org.moire.ultrasonic.view.SongListAdapter import org.moire.ultrasonic.view.SongListAdapter
import org.moire.ultrasonic.view.VisualizerView import org.moire.ultrasonic.view.VisualizerView
import timber.log.Timber import timber.log.Timber
import java.util.concurrent.CancellationException
/** /**
* Contains the Music Player screen of Ultrasonic with playback controls and the playlist * Contains the Music Player screen of Ultrasonic with playback controls and the playlist
@ -236,7 +236,7 @@ class PlayerFragment : Fragment(), GestureDetector.OnGestureListener, KoinCompon
previousButton.setOnClickListener { previousButton.setOnClickListener {
networkAndStorageChecker.warnIfNetworkOrStorageUnavailable() networkAndStorageChecker.warnIfNetworkOrStorageUnavailable()
scope.launch(CommunicationErrorUtil.handler(context)) { scope.launch(CommunicationError.getHandler(context)) {
mediaPlayerController.previous() mediaPlayerController.previous()
onCurrentChanged() onCurrentChanged()
onSliderProgressChanged() onSliderProgressChanged()
@ -250,7 +250,7 @@ class PlayerFragment : Fragment(), GestureDetector.OnGestureListener, KoinCompon
nextButton.setOnClickListener { nextButton.setOnClickListener {
networkAndStorageChecker.warnIfNetworkOrStorageUnavailable() networkAndStorageChecker.warnIfNetworkOrStorageUnavailable()
scope.launch(CommunicationErrorUtil.handler(context)) { scope.launch(CommunicationError.getHandler(context)) {
mediaPlayerController.next() mediaPlayerController.next()
onCurrentChanged() onCurrentChanged()
onSliderProgressChanged() onSliderProgressChanged()
@ -262,14 +262,14 @@ class PlayerFragment : Fragment(), GestureDetector.OnGestureListener, KoinCompon
changeProgress(incrementTime) changeProgress(incrementTime)
} }
pauseButton.setOnClickListener { pauseButton.setOnClickListener {
scope.launch(CommunicationErrorUtil.handler(context)) { scope.launch(CommunicationError.getHandler(context)) {
mediaPlayerController.pause() mediaPlayerController.pause()
onCurrentChanged() onCurrentChanged()
onSliderProgressChanged() onSliderProgressChanged()
} }
} }
stopButton.setOnClickListener { stopButton.setOnClickListener {
scope.launch(CommunicationErrorUtil.handler(context)) { scope.launch(CommunicationError.getHandler(context)) {
mediaPlayerController.reset() mediaPlayerController.reset()
onCurrentChanged() onCurrentChanged()
onSliderProgressChanged() onSliderProgressChanged()
@ -277,7 +277,7 @@ class PlayerFragment : Fragment(), GestureDetector.OnGestureListener, KoinCompon
} }
startButton.setOnClickListener { startButton.setOnClickListener {
networkAndStorageChecker.warnIfNetworkOrStorageUnavailable() networkAndStorageChecker.warnIfNetworkOrStorageUnavailable()
scope.launch(CommunicationErrorUtil.handler(context)) { scope.launch(CommunicationError.getHandler(context)) {
start() start()
onCurrentChanged() onCurrentChanged()
onSliderProgressChanged() onSliderProgressChanged()
@ -309,7 +309,7 @@ class PlayerFragment : Fragment(), GestureDetector.OnGestureListener, KoinCompon
progressBar.setOnSeekBarChangeListener(object : OnSeekBarChangeListener { progressBar.setOnSeekBarChangeListener(object : OnSeekBarChangeListener {
override fun onStopTrackingTouch(seekBar: SeekBar) { override fun onStopTrackingTouch(seekBar: SeekBar) {
scope.launch(CommunicationErrorUtil.handler(context)) { scope.launch(CommunicationError.getHandler(context)) {
mediaPlayerController.seekTo(progressBar.progress) mediaPlayerController.seekTo(progressBar.progress)
onSliderProgressChanged() onSliderProgressChanged()
} }
@ -321,7 +321,7 @@ class PlayerFragment : Fragment(), GestureDetector.OnGestureListener, KoinCompon
playlistView.setOnItemClickListener { _, _, position, _ -> playlistView.setOnItemClickListener { _, _, position, _ ->
networkAndStorageChecker.warnIfNetworkOrStorageUnavailable() networkAndStorageChecker.warnIfNetworkOrStorageUnavailable()
scope.launch(CommunicationErrorUtil.handler(context)) { scope.launch(CommunicationError.getHandler(context)) {
mediaPlayerController.play(position) mediaPlayerController.play(position)
onCurrentChanged() onCurrentChanged()
onSliderProgressChanged() onSliderProgressChanged()
@ -388,7 +388,7 @@ class PlayerFragment : Fragment(), GestureDetector.OnGestureListener, KoinCompon
} }
// Query the Jukebox state off-thread // Query the Jukebox state off-thread
scope.launch(CommunicationErrorUtil.handler(context)) { scope.launch(CommunicationError.getHandler(context)) {
try { try {
jukeboxAvailable = mediaPlayerController.isJukeboxAvailable jukeboxAvailable = mediaPlayerController.isJukeboxAvailable
} catch (all: Exception) { } catch (all: Exception) {
@ -795,7 +795,7 @@ class PlayerFragment : Fragment(), GestureDetector.OnGestureListener, KoinCompon
Locale.ROOT, Locale.ROOT,
"%s %s", "%s %s",
resources.getString(R.string.download_playlist_error), resources.getString(R.string.download_playlist_error),
CommunicationErrorUtil.getErrorMessage(it, context) CommunicationError.getErrorMessage(it, context)
) )
Util.toast(context, msg) Util.toast(context, msg)
} }
@ -934,7 +934,7 @@ class PlayerFragment : Fragment(), GestureDetector.OnGestureListener, KoinCompon
return return
} }
onProgressChangedTask = scope.launch(CommunicationErrorUtil.handler(context)) { onProgressChangedTask = scope.launch(CommunicationError.getHandler(context)) {
val isJukeboxEnabled: Boolean = mediaPlayerController.isJukeboxEnabled val isJukeboxEnabled: Boolean = mediaPlayerController.isJukeboxEnabled
val millisPlayed = max(0, mediaPlayerController.playerPosition) val millisPlayed = max(0, mediaPlayerController.playerPosition)
@ -1016,7 +1016,7 @@ class PlayerFragment : Fragment(), GestureDetector.OnGestureListener, KoinCompon
} }
private fun changeProgress(ms: Int) { private fun changeProgress(ms: Int) {
scope.launch(CommunicationErrorUtil.handler(context)) { scope.launch(CommunicationError.getHandler(context)) {
val msPlayed: Int = max(0, mediaPlayerController.playerPosition) val msPlayed: Int = max(0, mediaPlayerController.playerPosition)
val duration = mediaPlayerController.playerDuration val duration = mediaPlayerController.playerDuration
val seekTo = (msPlayed + ms).coerceAtMost(duration) val seekTo = (msPlayed + ms).coerceAtMost(duration)

View File

@ -38,7 +38,6 @@ import org.moire.ultrasonic.data.ActiveServerProvider.Companion.isOffline
import org.moire.ultrasonic.domain.MusicDirectory import org.moire.ultrasonic.domain.MusicDirectory
import org.moire.ultrasonic.fragment.FragmentTitle.Companion.getTitle import org.moire.ultrasonic.fragment.FragmentTitle.Companion.getTitle
import org.moire.ultrasonic.fragment.FragmentTitle.Companion.setTitle import org.moire.ultrasonic.fragment.FragmentTitle.Companion.setTitle
import org.moire.ultrasonic.service.CommunicationErrorUtil
import org.moire.ultrasonic.service.MediaPlayerController import org.moire.ultrasonic.service.MediaPlayerController
import org.moire.ultrasonic.subsonic.DownloadHandler import org.moire.ultrasonic.subsonic.DownloadHandler
import org.moire.ultrasonic.subsonic.ImageLoaderProvider import org.moire.ultrasonic.subsonic.ImageLoaderProvider
@ -47,6 +46,7 @@ import org.moire.ultrasonic.subsonic.ShareHandler
import org.moire.ultrasonic.subsonic.VideoPlayer import org.moire.ultrasonic.subsonic.VideoPlayer
import org.moire.ultrasonic.util.AlbumHeader import org.moire.ultrasonic.util.AlbumHeader
import org.moire.ultrasonic.util.CancellationToken import org.moire.ultrasonic.util.CancellationToken
import org.moire.ultrasonic.util.CommunicationError
import org.moire.ultrasonic.util.Constants import org.moire.ultrasonic.util.Constants
import org.moire.ultrasonic.util.EntryByDiscAndTrackComparator import org.moire.ultrasonic.util.EntryByDiscAndTrackComparator
import org.moire.ultrasonic.util.Settings import org.moire.ultrasonic.util.Settings
@ -211,7 +211,7 @@ class TrackCollectionFragment : Fragment() {
val handler = CoroutineExceptionHandler { _, exception -> val handler = CoroutineExceptionHandler { _, exception ->
Handler(Looper.getMainLooper()).post { Handler(Looper.getMainLooper()).post {
CommunicationErrorUtil.handleError(exception, context) CommunicationError.handleError(exception, context)
} }
refreshAlbumListView!!.isRefreshing = false refreshAlbumListView!!.isRefreshing = false
} }

View File

@ -1,104 +0,0 @@
/*
This file is part of Subsonic.
Subsonic is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Subsonic is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Subsonic. If not, see <http://www.gnu.org/licenses/>.
Copyright 2020 (C) Jozsef Varga
*/
package org.moire.ultrasonic.service
import android.app.AlertDialog
import android.content.Context
import android.os.Handler
import android.os.Looper
import com.fasterxml.jackson.core.JsonParseException
import java.io.FileNotFoundException
import java.io.IOException
import java.security.cert.CertPathValidatorException
import java.security.cert.CertificateException
import javax.net.ssl.SSLException
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.CoroutineExceptionHandler
import org.moire.ultrasonic.R
import org.moire.ultrasonic.api.subsonic.ApiNotSupportedException
import org.moire.ultrasonic.api.subsonic.SubsonicRESTException
import org.moire.ultrasonic.subsonic.getLocalizedErrorMessage
import org.moire.ultrasonic.util.Util
import timber.log.Timber
/**
* Contains helper functions to handle the exceptions
* thrown during the communication with a Subsonic server
*/
@Suppress("ReturnCount", "UtilityClassWithPublicConstructor")
class CommunicationErrorUtil {
companion object {
fun handler(context: Context?, handler: ((CoroutineContext, Throwable) -> Unit)? = null):
CoroutineExceptionHandler {
return CoroutineExceptionHandler { coroutineContext, exception ->
Handler(Looper.getMainLooper()).post {
handleError(exception, context)
handler?.invoke(coroutineContext, exception)
}
}
}
fun handleError(error: Throwable?, context: Context?) {
Timber.w(error)
if (context == null) return
AlertDialog.Builder(context)
.setIcon(android.R.drawable.ic_dialog_alert)
.setTitle(R.string.error_label)
.setMessage(getErrorMessage(error!!, context))
.setCancelable(true)
.setPositiveButton(R.string.common_ok) { _, _ -> }
.create().show()
}
fun getErrorMessage(error: Throwable, context: Context?): String {
if (context == null) return "Couldn't get Error message, Context is null"
if (error is IOException && !Util.isNetworkConnected()) {
return context.resources.getString(R.string.background_task_no_network)
} else if (error is FileNotFoundException) {
return context.resources.getString(R.string.background_task_not_found)
} else if (error is JsonParseException) {
return context.resources.getString(R.string.background_task_parse_error)
} else if (error is SSLException) {
return if (
error.cause is CertificateException &&
error.cause?.cause is CertPathValidatorException
) {
context.resources
.getString(
R.string.background_task_ssl_cert_error, error.cause?.cause?.message
)
} else {
context.resources.getString(R.string.background_task_ssl_error)
}
} else if (error is ApiNotSupportedException) {
return context.resources.getString(
R.string.background_task_unsupported_api, error.serverApiVersion
)
} else if (error is IOException) {
return context.resources.getString(R.string.background_task_network_error)
} else if (error is SubsonicRESTException) {
return error.getLocalizedErrorMessage(context)
}
val message = error.message
return message ?: error.javaClass.simpleName
}
}
}

View File

@ -0,0 +1,91 @@
/*
* CommunicationErrorUtil.kt
* Copyright (C) 2009-2021 Ultrasonic developers
*
* Distributed under terms of the GNU GPLv3 license.
*/
package org.moire.ultrasonic.util
import android.app.AlertDialog
import android.content.Context
import android.os.Handler
import android.os.Looper
import com.fasterxml.jackson.core.JsonParseException
import java.io.FileNotFoundException
import java.io.IOException
import java.security.cert.CertPathValidatorException
import java.security.cert.CertificateException
import javax.net.ssl.SSLException
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.CoroutineExceptionHandler
import org.moire.ultrasonic.R
import org.moire.ultrasonic.api.subsonic.ApiNotSupportedException
import org.moire.ultrasonic.api.subsonic.SubsonicRESTException
import org.moire.ultrasonic.subsonic.getLocalizedErrorMessage
import timber.log.Timber
/**
* Contains helper functions to handle the exceptions
* thrown during the communication with a Subsonic server
*/
object CommunicationError {
fun getHandler(context: Context?, handler: ((CoroutineContext, Throwable) -> Unit)? = null):
CoroutineExceptionHandler {
return CoroutineExceptionHandler { coroutineContext, exception ->
Handler(Looper.getMainLooper()).post {
handleError(exception, context)
handler?.invoke(coroutineContext, exception)
}
}
}
@JvmStatic
fun handleError(error: Throwable?, context: Context?) {
Timber.w(error)
if (context == null) return
AlertDialog.Builder(context)
.setIcon(android.R.drawable.ic_dialog_alert)
.setTitle(R.string.error_label)
.setMessage(getErrorMessage(error!!, context))
.setCancelable(true)
.setPositiveButton(R.string.common_ok) { _, _ -> }
.create().show()
}
@JvmStatic
@Suppress("ReturnCount")
fun getErrorMessage(error: Throwable, context: Context?): String {
if (context == null) return "Couldn't get Error message, Context is null"
if (error is IOException && !Util.isNetworkConnected()) {
return context.resources.getString(R.string.background_task_no_network)
} else if (error is FileNotFoundException) {
return context.resources.getString(R.string.background_task_not_found)
} else if (error is JsonParseException) {
return context.resources.getString(R.string.background_task_parse_error)
} else if (error is SSLException) {
return if (
error.cause is CertificateException &&
error.cause?.cause is CertPathValidatorException
) {
context.resources
.getString(
R.string.background_task_ssl_cert_error, error.cause?.cause?.message
)
} else {
context.resources.getString(R.string.background_task_ssl_error)
}
} else if (error is ApiNotSupportedException) {
return context.resources.getString(
R.string.background_task_unsupported_api, error.serverApiVersion
)
} else if (error is IOException) {
return context.resources.getString(R.string.background_task_network_error)
} else if (error is SubsonicRESTException) {
return error.getLocalizedErrorMessage(context)
}
val message = error.message
return message ?: error.javaClass.simpleName
}
}