Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add localized error message. #250

Merged
merged 9 commits into from
Sep 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* Copyright (c) 2023. SRG SSR. All rights reserved.
* License information is available from the LICENSE file.
*/
package ch.srgssr.pillarbox.core.business

import io.ktor.client.plugins.ClientRequestException
import java.io.IOException

/**
* Http result exception
*
* @constructor
*
* @param message Message for the IOException, constructor used by PlaybackException to rebuild this exception.
*/
class HttpResultException internal constructor(message: String) : IOException(message) {
constructor(throwable: ClientRequestException) : this(
"${throwable.response.status.description} (${throwable.response.status.value})"
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,24 @@ import android.net.Uri
import androidx.media3.common.C
import androidx.media3.common.MediaItem
import androidx.media3.common.MediaMetadata
import ch.srgssr.pillarbox.core.business.integrationlayer.data.BlockReasonException
import ch.srgssr.pillarbox.core.business.exception.BlockReasonException
import ch.srgssr.pillarbox.core.business.exception.DataParsingException
import ch.srgssr.pillarbox.core.business.exception.ResourceNotFoundException
import ch.srgssr.pillarbox.core.business.integrationlayer.data.Chapter
import ch.srgssr.pillarbox.core.business.integrationlayer.data.Drm
import ch.srgssr.pillarbox.core.business.integrationlayer.data.MediaComposition
import ch.srgssr.pillarbox.core.business.integrationlayer.data.MediaUrn
import ch.srgssr.pillarbox.core.business.integrationlayer.data.Resource
import ch.srgssr.pillarbox.core.business.integrationlayer.data.ResourceNotFoundException
import ch.srgssr.pillarbox.core.business.integrationlayer.service.MediaCompositionDataSource
import ch.srgssr.pillarbox.core.business.tracker.SRGEventLoggerTracker
import ch.srgssr.pillarbox.core.business.tracker.commandersact.CommandersActTracker
import ch.srgssr.pillarbox.core.business.tracker.comscore.ComScoreTracker
import ch.srgssr.pillarbox.player.data.MediaItemSource
import ch.srgssr.pillarbox.player.getMediaItemTrackerData
import ch.srgssr.pillarbox.player.setTrackerData
import io.ktor.client.plugins.ClientRequestException
import kotlinx.serialization.SerializationException
import java.io.IOException

/**
* Load [MediaItem] playable from a [ch.srgssr.pillarbox.core.business.integrationlayer.data.MediaComposition]
Expand Down Expand Up @@ -65,7 +69,21 @@ class MediaCompositionMediaItemSource(
require(MediaUrn.isValid(mediaItem.mediaId)) { "Invalid urn=${mediaItem.mediaId}" }
val mediaUri = mediaItem.localConfiguration?.uri
require(!MediaUrn.isValid(mediaUri.toString())) { "Uri can't be a urn" }
val result = mediaCompositionDataSource.getMediaCompositionByUrn(mediaItem.mediaId).getOrThrow()
val result = mediaCompositionDataSource.getMediaCompositionByUrn(mediaItem.mediaId).getOrElse {
when (it) {
is ClientRequestException -> {
throw HttpResultException(it)
}

is SerializationException -> {
throw DataParsingException(it)
}

else -> {
throw IOException(it.message)
}
}
}
val chapter = result.mainChapter
chapter.blockReason?.let {
throw BlockReasonException(it)
Expand All @@ -74,7 +92,7 @@ class MediaCompositionMediaItemSource(
throw BlockReasonException(it)
}

val resource = resourceSelector.selectResourceFromChapter(chapter) ?: throw ResourceNotFoundException
val resource = resourceSelector.selectResourceFromChapter(chapter) ?: throw ResourceNotFoundException()
var uri = Uri.parse(resource.url)
if (resource.tokenType == Resource.TokenType.AKAMAI) {
uri = appendTokenQueryToUri(uri)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,38 +4,45 @@
*/
package ch.srgssr.pillarbox.core.business

import android.os.RemoteException
import android.content.Context
import android.util.Pair
import androidx.media3.common.ErrorMessageProvider
import androidx.media3.common.PlaybackException
import ch.srgssr.pillarbox.core.business.integrationlayer.data.BlockReasonException
import ch.srgssr.pillarbox.core.business.integrationlayer.data.ResourceNotFoundException
import io.ktor.client.plugins.ClientRequestException
import ch.srgssr.pillarbox.core.business.exception.BlockReasonException
import ch.srgssr.pillarbox.core.business.exception.DataParsingException
import ch.srgssr.pillarbox.core.business.exception.ResourceNotFoundException
import ch.srgssr.pillarbox.core.business.extension.getString
import java.io.IOException

/**
* Process error message from [PlaybackException]
*/
class SRGErrorMessageProvider : ErrorMessageProvider<PlaybackException> {
class SRGErrorMessageProvider(private val context: Context) : ErrorMessageProvider<PlaybackException> {

override fun getErrorMessage(throwable: PlaybackException): Pair<Int, String> {
return when (val cause = throwable.cause) {
is BlockReasonException -> {
Pair.create(0, cause.blockReason.name)
Pair.create(0, context.getString(cause.blockReason))
}
// When using MediaController, RemoteException is send instead of HttpException.
is RemoteException ->
Pair.create(throwable.errorCode, cause.message)

is ClientRequestException -> {
Pair.create(cause.response.status.value, cause.response.status.description)
is ResourceNotFoundException -> {
Pair.create(0, context.getString(R.string.noPlayableResourceFound))
}

is ResourceNotFoundException -> {
Pair.create(0, "Can't find Resource to play")
is DataParsingException -> {
Pair.create(0, context.getString(R.string.invalidDataError))
}

is HttpResultException -> {
Pair.create(0, cause.message)
}

is IOException -> {
Pair.create(0, context.getString(R.string.NoInternet))
}

else -> {
Pair.create(throwable.errorCode, "${throwable.localizedMessage} (${throwable.errorCodeName})")
Pair.create(throwable.errorCode, context.getString(R.string.unknownError))
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright (c) 2023. SRG SSR. All rights reserved.
* License information is available from the LICENSE file.
*/
package ch.srgssr.pillarbox.core.business.exception

import ch.srgssr.pillarbox.core.business.integrationlayer.data.BlockReason
import java.io.IOException

/**
* Block reason exception
*
* @property blockReason the reason a [Chapter] or a [Segment] is blocked.
*/
class BlockReasonException(val blockReason: BlockReason) : IOException(blockReason.name) {
/*
* ExoPlaybackException bundles cause exception with class name and message.
* In order to recreate the cause of the throwable, it needs a throwable class with constructor(string).
*/
internal constructor(message: String) : this(parseMessage(message))

companion object {
@Suppress("SwallowedException")
private fun parseMessage(message: String): BlockReason {
return try {
BlockReason.valueOf(message)
} catch (e: IllegalArgumentException) {
BlockReason.UNKNOWN
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
* Copyright (c) 2023. SRG SSR. All rights reserved.
* License information is available from the LICENSE file.
*/
package ch.srgssr.pillarbox.core.business.exception

import java.io.IOException

/**
* Data parsing exception
*
* @constructor
*
* @param message Message for the IOException, constructor used by PlaybackException to rebuild this exception.
*/
class DataParsingException internal constructor(message: String? = "Data parsing error") : IOException(message) {
constructor(throwable: Throwable) : this(throwable.message)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
* Copyright (c) 2023. SRG SSR. All rights reserved.
* License information is available from the LICENSE file.
*/
package ch.srgssr.pillarbox.core.business.exception

import java.io.IOException

/**
* Resource not found exception is throw when :
* - [Chapter] doesn't have a playable resource
* - [Chapter.listResource] is empty or null
*/
class ResourceNotFoundException internal constructor(message: String) : IOException(message) {
constructor() : this("Unable to find suitable resources")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright (c) 2023. SRG SSR. All rights reserved.
* License information is available from the LICENSE file.
*/
package ch.srgssr.pillarbox.core.business.extension

import android.content.Context
import ch.srgssr.pillarbox.core.business.R
import ch.srgssr.pillarbox.core.business.integrationlayer.data.BlockReason

/**
* Get string
*
* @param blockReason The [BlockReason] to get the string of.
* @return The string message of [blockReason]
*/
fun Context.getString(blockReason: BlockReason): String {
return getString(blockReason.getStringResId())
}

/**
* Get string resource id
*
* @return The android string resource id of a [BlockReason]
*/
fun BlockReason.getStringResId(): Int {
return when (this) {
BlockReason.AGERATING12 -> R.string.blockReason_ageRating12
BlockReason.GEOBLOCK -> R.string.blockReason_geoBlock
BlockReason.LEGAL -> R.string.blockReason_legal
BlockReason.COMMERCIAL -> R.string.blockReason_commercial
BlockReason.AGERATING18 -> R.string.blockReason_ageRating18
BlockReason.STARTDATE -> R.string.blockReason_startDate
BlockReason.ENDDATE -> R.string.blockReason_endDate
BlockReason.UNKNOWN -> R.string.blockReason_unknown
}
}

This file was deleted.

This file was deleted.

14 changes: 14 additions & 0 deletions pillarbox-core-business/src/main/res/values-de/strings.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2023. SRG SSR. All rights reserved.
~ License information is available from the LICENSE file.
-->
<resources>
<string name="blockReason_geoBlock">Dieser Inhalt ist ausserhalb der Schweiz nicht verfügbar.</string>
<string name="blockReason_legal">Dieser Inhalt ist aus rechtlichen Gründen nicht verfügbar.</string>
<string name="blockReason_commercial">Dieser Werbe-Inhalt ist nicht verfügbar.</string>
<string name="blockReason_ageRating18">Dieser Inhalt ist aus Gründen des Jugendschutzes nur zwischen 22:00 und 5:00 Uhr verfügbar.</string>
<string name="blockReason_ageRating12">Dieser Inhalt ist aus Gründen des Jugendschutzes nur zwischen 20:00 und 6:00 Uhr verfügbar.</string>
<string name="blockReason_startDate">Dieser Inhalt ist noch nicht verfügbar.</string>
<string name="blockReason_endDate">Dieser Inhalt ist nicht mehr verfügbar.</string>
<string name="blockReason_unknown">Dieser Inhalt ist nicht verfügbar.</string>
</resources>
18 changes: 18 additions & 0 deletions pillarbox-core-business/src/main/res/values-en/strings.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2023. SRG SSR. All rights reserved.
~ License information is available from the LICENSE file.
-->
<resources>
<string name="unknownError">Unknown error.</string>
<string name="invalidDataError">The data is invalid.</string>
<string name="noPlayableResourceFound">No playable resources could be found.</string>
<string name="blockReason_geoBlock">This content is not available outside Switzerland.</string>
<string name="blockReason_legal">This content is not available due to legal restrictions.</string>
<string name="blockReason_commercial">This commercial content is not available.</string>
<string name="blockReason_ageRating18">To protect children this content is only available between 10PM and 5AM.</string>
<string name="blockReason_ageRating12">To protect children this content is only available between 8PM and 6AM.</string>
<string name="blockReason_startDate">This content is not available yet.</string>
<string name="blockReason_endDate">This content is not available anymore.</string>
<string name="blockReason_unknown">This content is not available.</string>
</resources>
17 changes: 17 additions & 0 deletions pillarbox-core-business/src/main/res/values-fr/strings.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2023. SRG SSR. All rights reserved.
~ License information is available from the LICENSE file.
-->
<resources>
<string name="invalidDataError">Les données sont invalides.</string>
<string name="noPlayableResourceFound">Aucune ressource jouable n\'a pu être trouvée.</string>
<string name="blockReason_geoBlock">Ce contenu n\'est pas disponible hors de Suisse.</string>
<string name="blockReason_legal">Ce contenu a été retiré par décision de justice.</string>
<string name="blockReason_commercial">Ce contenu n’est actuellement pas disponible.</string>
<string name="blockReason_ageRating18">Ce contenu n\'est disponible qu\'entre 22h et 5h afin de protéger le jeune public.</string>
<string name="blockReason_ageRating12">Ce contenu n\'est disponible qu\'entre 20h et 6h afin de protéger le jeune public.</string>
<string name="blockReason_startDate">Ce contenu n’est pas encore disponible.</string>
<string name="blockReason_endDate">Ce contenu n’est plus disponible.</string>
<string name="blockReason_unknown">Ce contenu n’est pas disponible.</string>
<string name="unknownError">Erreur inconnue.</string>
</resources>
12 changes: 12 additions & 0 deletions pillarbox-core-business/src/main/res/values-it/strings.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2023. SRG SSR. All rights reserved.
~ License information is available from the LICENSE file.
-->
<resources>
<string name="blockReason_geoBlock">Questo media non è disponibile fuori dalla Svizzera.</string>
<string name="blockReason_legal">Questo media non è disponibile a causa di restrizioni legali.</string>
<string name="blockReason_commercial">Questo contenuto commerciale non è disponibile.</string>
<string name="blockReason_startDate">Questo media non è ancora disponibile.</string>
<string name="blockReason_endDate">Questo media non è più disponibile.</string>
<string name="blockReason_unknown">Questo media non è disponibile.</string>
</resources>
12 changes: 12 additions & 0 deletions pillarbox-core-business/src/main/res/values-rm/strings.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2023. SRG SSR. All rights reserved.
~ License information is available from the LICENSE file.
-->
<resources>
<string name="blockReason_geoBlock">Quest medium n\'è betg disponibel ordaifer la Svizra.</string>
<string name="blockReason_legal">Quest medium n\'è betg disponibel perquei ch\'el è scadì.</string>
<string name="blockReason_commercial">Quest medium commerzial n\'è betg disponibel.</string>
<string name="blockReason_startDate">Quest medium n\'è betg anc disponibel.</string>
<string name="blockReason_endDate">Quest medium n\'è betg pli disponibel.</string>
<string name="blockReason_unknown">Quest medium n\'è betg disponibel.</string>
</resources>
18 changes: 18 additions & 0 deletions pillarbox-core-business/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2023. SRG SSR. All rights reserved.
~ License information is available from the LICENSE file.
-->
<resources>
<string name="unknownError">Unknown error.</string>
<string name="invalidDataError">The data is invalid.</string>
<string name="noPlayableResourceFound">No playable resources could be found.</string>
<string name="NoInternet">It seems your device is not connected to internet.</string>
<string name="blockReason_geoBlock">This content is not available outside Switzerland.</string>
<string name="blockReason_legal">This content is not available due to legal restrictions.</string>
<string name="blockReason_commercial">This commercial content is not available.</string>
<string name="blockReason_ageRating18">To protect children this content is only available between 10PM and 5AM.</string>
<string name="blockReason_ageRating12">To protect children this content is only available between 8PM and 6AM.</string>
<string name="blockReason_startDate">This content is not available yet.</string>
<string name="blockReason_endDate">This content is not available anymore.</string>
<string name="blockReason_unknown">This content is not available.</string>
</resources>
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ package ch.srgssr.pillarbox.core.business

import androidx.media3.common.MediaItem
import androidx.media3.common.MediaMetadata
import ch.srgssr.pillarbox.core.business.exception.BlockReasonException
import ch.srgssr.pillarbox.core.business.exception.ResourceNotFoundException
import ch.srgssr.pillarbox.core.business.integrationlayer.data.BlockReason
import ch.srgssr.pillarbox.core.business.integrationlayer.data.BlockReasonException
import ch.srgssr.pillarbox.core.business.integrationlayer.data.Chapter
import ch.srgssr.pillarbox.core.business.integrationlayer.data.MediaComposition
import ch.srgssr.pillarbox.core.business.integrationlayer.data.Resource
import ch.srgssr.pillarbox.core.business.integrationlayer.data.ResourceNotFoundException
import ch.srgssr.pillarbox.core.business.integrationlayer.data.Segment
import ch.srgssr.pillarbox.core.business.integrationlayer.service.MediaCompositionDataSource
import kotlinx.coroutines.runBlocking
Expand All @@ -35,13 +35,6 @@ class MediaCompositionMediaItemSourceTest {
Unit
}

@Test(expected = IllegalArgumentException::class)
fun testUrnAsUri() = runBlocking {
val urn = "urn:rts:video:1234"
mediaItemSource.loadMediaItem(MediaItem.Builder().setMediaId(urn).setUri(urn).build())
Unit
}

@Test(expected = ResourceNotFoundException::class)
fun testNoResource() = runBlocking {
mediaItemSource.loadMediaItem(createMediaItem(DummyMediaCompositionProvider.URN_NO_RESOURCES))
Expand Down
Loading