Closes #9530: Don't crash on failed avatar fetches
parent
fd263d8329
commit
f76acc5db1
|
@ -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) }
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,11 +22,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
|
||||||
|
@ -39,7 +37,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
|
||||||
|
@ -375,16 +373,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…
Reference in New Issue