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

View File

@ -18,9 +18,9 @@ import org.koin.core.component.inject
import org.moire.ultrasonic.data.ActiveServerProvider
import org.moire.ultrasonic.data.ServerSetting
import org.moire.ultrasonic.domain.MusicFolder
import org.moire.ultrasonic.service.CommunicationErrorUtil
import org.moire.ultrasonic.service.MusicService
import org.moire.ultrasonic.service.MusicServiceFactory
import org.moire.ultrasonic.util.CommunicationError
import org.moire.ultrasonic.util.Settings
/**
@ -94,7 +94,7 @@ open class GenericListModel(application: Application) :
private fun handleException(exception: Exception, context: Context) {
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.LinkedList
import java.util.Locale
import java.util.concurrent.CancellationException
import java.util.concurrent.Executors
import java.util.concurrent.ScheduledExecutorService
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.FeatureStorage
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.LocalMediaPlayer
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.ShareHandler
import org.moire.ultrasonic.util.CancellationToken
import org.moire.ultrasonic.util.CommunicationError
import org.moire.ultrasonic.util.Constants
import org.moire.ultrasonic.util.Settings
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.VisualizerView
import timber.log.Timber
import java.util.concurrent.CancellationException
/**
* 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 {
networkAndStorageChecker.warnIfNetworkOrStorageUnavailable()
scope.launch(CommunicationErrorUtil.handler(context)) {
scope.launch(CommunicationError.getHandler(context)) {
mediaPlayerController.previous()
onCurrentChanged()
onSliderProgressChanged()
@ -250,7 +250,7 @@ class PlayerFragment : Fragment(), GestureDetector.OnGestureListener, KoinCompon
nextButton.setOnClickListener {
networkAndStorageChecker.warnIfNetworkOrStorageUnavailable()
scope.launch(CommunicationErrorUtil.handler(context)) {
scope.launch(CommunicationError.getHandler(context)) {
mediaPlayerController.next()
onCurrentChanged()
onSliderProgressChanged()
@ -262,14 +262,14 @@ class PlayerFragment : Fragment(), GestureDetector.OnGestureListener, KoinCompon
changeProgress(incrementTime)
}
pauseButton.setOnClickListener {
scope.launch(CommunicationErrorUtil.handler(context)) {
scope.launch(CommunicationError.getHandler(context)) {
mediaPlayerController.pause()
onCurrentChanged()
onSliderProgressChanged()
}
}
stopButton.setOnClickListener {
scope.launch(CommunicationErrorUtil.handler(context)) {
scope.launch(CommunicationError.getHandler(context)) {
mediaPlayerController.reset()
onCurrentChanged()
onSliderProgressChanged()
@ -277,7 +277,7 @@ class PlayerFragment : Fragment(), GestureDetector.OnGestureListener, KoinCompon
}
startButton.setOnClickListener {
networkAndStorageChecker.warnIfNetworkOrStorageUnavailable()
scope.launch(CommunicationErrorUtil.handler(context)) {
scope.launch(CommunicationError.getHandler(context)) {
start()
onCurrentChanged()
onSliderProgressChanged()
@ -309,7 +309,7 @@ class PlayerFragment : Fragment(), GestureDetector.OnGestureListener, KoinCompon
progressBar.setOnSeekBarChangeListener(object : OnSeekBarChangeListener {
override fun onStopTrackingTouch(seekBar: SeekBar) {
scope.launch(CommunicationErrorUtil.handler(context)) {
scope.launch(CommunicationError.getHandler(context)) {
mediaPlayerController.seekTo(progressBar.progress)
onSliderProgressChanged()
}
@ -321,7 +321,7 @@ class PlayerFragment : Fragment(), GestureDetector.OnGestureListener, KoinCompon
playlistView.setOnItemClickListener { _, _, position, _ ->
networkAndStorageChecker.warnIfNetworkOrStorageUnavailable()
scope.launch(CommunicationErrorUtil.handler(context)) {
scope.launch(CommunicationError.getHandler(context)) {
mediaPlayerController.play(position)
onCurrentChanged()
onSliderProgressChanged()
@ -388,7 +388,7 @@ class PlayerFragment : Fragment(), GestureDetector.OnGestureListener, KoinCompon
}
// Query the Jukebox state off-thread
scope.launch(CommunicationErrorUtil.handler(context)) {
scope.launch(CommunicationError.getHandler(context)) {
try {
jukeboxAvailable = mediaPlayerController.isJukeboxAvailable
} catch (all: Exception) {
@ -795,7 +795,7 @@ class PlayerFragment : Fragment(), GestureDetector.OnGestureListener, KoinCompon
Locale.ROOT,
"%s %s",
resources.getString(R.string.download_playlist_error),
CommunicationErrorUtil.getErrorMessage(it, context)
CommunicationError.getErrorMessage(it, context)
)
Util.toast(context, msg)
}
@ -934,7 +934,7 @@ class PlayerFragment : Fragment(), GestureDetector.OnGestureListener, KoinCompon
return
}
onProgressChangedTask = scope.launch(CommunicationErrorUtil.handler(context)) {
onProgressChangedTask = scope.launch(CommunicationError.getHandler(context)) {
val isJukeboxEnabled: Boolean = mediaPlayerController.isJukeboxEnabled
val millisPlayed = max(0, mediaPlayerController.playerPosition)
@ -1016,7 +1016,7 @@ class PlayerFragment : Fragment(), GestureDetector.OnGestureListener, KoinCompon
}
private fun changeProgress(ms: Int) {
scope.launch(CommunicationErrorUtil.handler(context)) {
scope.launch(CommunicationError.getHandler(context)) {
val msPlayed: Int = max(0, mediaPlayerController.playerPosition)
val duration = mediaPlayerController.playerDuration
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.fragment.FragmentTitle.Companion.getTitle
import org.moire.ultrasonic.fragment.FragmentTitle.Companion.setTitle
import org.moire.ultrasonic.service.CommunicationErrorUtil
import org.moire.ultrasonic.service.MediaPlayerController
import org.moire.ultrasonic.subsonic.DownloadHandler
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.util.AlbumHeader
import org.moire.ultrasonic.util.CancellationToken
import org.moire.ultrasonic.util.CommunicationError
import org.moire.ultrasonic.util.Constants
import org.moire.ultrasonic.util.EntryByDiscAndTrackComparator
import org.moire.ultrasonic.util.Settings
@ -211,7 +211,7 @@ class TrackCollectionFragment : Fragment() {
val handler = CoroutineExceptionHandler { _, exception ->
Handler(Looper.getMainLooper()).post {
CommunicationErrorUtil.handleError(exception, context)
CommunicationError.handleError(exception, context)
}
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
}
}