Closes #9530: Don't crash on failed avatar fetches

fennec/beta
Grisha Kruglov 4 years ago committed by Sebastian Kaspari
parent eb5cdcd7ec
commit 12f4071e33

@ -5,20 +5,23 @@
package org.mozilla.fenix.ext package org.mozilla.fenix.ext
import android.content.Context import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory import android.graphics.BitmapFactory
import android.net.Uri import android.net.Uri
import android.util.Patterns import android.util.Patterns
import android.webkit.URLUtil import android.webkit.URLUtil
import androidx.core.graphics.drawable.RoundedBitmapDrawable
import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory
import androidx.core.net.toUri import androidx.core.net.toUri
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import mozilla.components.concept.fetch.Client
import mozilla.components.concept.fetch.Request
import mozilla.components.lib.publicsuffixlist.PublicSuffixList import mozilla.components.lib.publicsuffixlist.PublicSuffixList
import mozilla.components.lib.publicsuffixlist.ext.urlToTrimmedHost import mozilla.components.lib.publicsuffixlist.ext.urlToTrimmedHost
import mozilla.components.support.ktx.android.net.hostWithoutCommonPrefixes import mozilla.components.support.ktx.android.net.hostWithoutCommonPrefixes
import java.io.IOException
import java.net.IDN import java.net.IDN
import java.net.MalformedURLException
import java.net.URL
import java.util.Locale import java.util.Locale
const val FILE_PREFIX = "file://" const val FILE_PREFIX = "file://"
@ -113,17 +116,23 @@ fun String.simplifiedUrl(): String {
} }
/** /**
* Gets a rounded drawable from a URL if possible, else null. Must be called off main thread. * Gets a rounded drawable from a URL if possible, else null.
*/ */
fun String.decodeUrlToRoundedDrawable(context: Context): RoundedBitmapDrawable? { suspend fun String.toRoundedDrawable(context: Context, client: Client) = bitmapForUrl(this, client)?.let { bitmap ->
val avatarUrl = try { RoundedBitmapDrawableFactory.create(context.resources, bitmap).also {
URL(this) it.isCircular = true
} catch (e: MalformedURLException) { it.setAntiAlias(true)
return null
} }
val bitmap = BitmapFactory.decodeStream(avatarUrl.openConnection().getInputStream()) }
val roundedBitmapDrawable = RoundedBitmapDrawableFactory.create(context.resources, bitmap)
roundedBitmapDrawable.isCircular = true suspend fun bitmapForUrl(url: String, client: Client): Bitmap? = withContext(Dispatchers.IO) {
roundedBitmapDrawable.setAntiAlias(true) // TODO cache this image, see https://github.com/mozilla-mobile/fenix/issues/9531
return roundedBitmapDrawable // Code below will cache it in Gecko's cache, which ensures that as long as we've fetched it once,
// we will be able to display this avatar as long as the cache isn't purged (e.g. via 'clear user data').
val body = try {
client.fetch(Request(url, useCaches = true)).body
} catch (e: IOException) {
return@withContext null
}
body.useStream { BitmapFactory.decodeStream(it) }
} }

@ -20,11 +20,9 @@ import androidx.preference.PreferenceCategory
import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceFragmentCompat
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.Dispatchers.Main import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import mozilla.components.concept.sync.AccountObserver import mozilla.components.concept.sync.AccountObserver
import mozilla.components.concept.sync.AuthType import mozilla.components.concept.sync.AuthType
import mozilla.components.concept.sync.OAuthAccount import mozilla.components.concept.sync.OAuthAccount
@ -37,7 +35,7 @@ import org.mozilla.fenix.R
import org.mozilla.fenix.components.metrics.Event import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.ext.application import org.mozilla.fenix.ext.application
import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.decodeUrlToRoundedDrawable import org.mozilla.fenix.ext.toRoundedDrawable
import org.mozilla.fenix.ext.getPreferenceKey import org.mozilla.fenix.ext.getPreferenceKey
import org.mozilla.fenix.ext.metrics import org.mozilla.fenix.ext.metrics
import org.mozilla.fenix.ext.requireComponents import org.mozilla.fenix.ext.requireComponents
@ -347,16 +345,11 @@ class SettingsFragment : PreferenceFragmentCompat() {
if (account != null && !accountManager.accountNeedsReauth()) { if (account != null && !accountManager.accountNeedsReauth()) {
preferenceSignIn?.isVisible = false preferenceSignIn?.isVisible = false
profile?.avatar?.url?.let { profile?.avatar?.url?.let { avatarUrl ->
lifecycleScope.launch(IO) { lifecycleScope.launch(Main) {
val roundedDrawable = it.decodeUrlToRoundedDrawable(context) val roundedDrawable = avatarUrl.toRoundedDrawable(context, requireComponents.core.client)
withContext(Main) { preferenceFirefoxAccount?.icon =
preferenceFirefoxAccount?.icon = roundedDrawable ?: AppCompatResources.getDrawable(context, R.drawable.ic_account)
roundedDrawable ?: AppCompatResources.getDrawable(
context,
R.drawable.ic_account
)
}
} }
} }
preferenceSignIn?.onPreferenceClickListener = null preferenceSignIn?.onPreferenceClickListener = null

Loading…
Cancel
Save