diff --git a/app/src/main/java/com/nextcloud/client/widget/DashboardWidgetConfigurationActivity.kt b/app/src/main/java/com/nextcloud/client/widget/DashboardWidgetConfigurationActivity.kt index 57b5e2aa6e09..89f1621c8646 100644 --- a/app/src/main/java/com/nextcloud/client/widget/DashboardWidgetConfigurationActivity.kt +++ b/app/src/main/java/com/nextcloud/client/widget/DashboardWidgetConfigurationActivity.kt @@ -14,6 +14,7 @@ import android.os.Bundle import android.view.View import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.content.res.AppCompatResources +import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.LinearLayoutManager import com.nextcloud.android.lib.resources.dashboard.DashBoardButtonType import com.nextcloud.android.lib.resources.dashboard.DashboardListWidgetsRemoteOperation @@ -77,7 +78,13 @@ class DashboardWidgetConfigurationActivity : val layoutManager = LinearLayoutManager(this) // TODO follow our new architecture - mAdapter = DashboardWidgetListAdapter(accountManager, clientFactory, this, this) + mAdapter = DashboardWidgetListAdapter( + lifecycleScope, + accountManager, + clientFactory, + this, + this + ) binding.list.apply { setHasFooter(false) setAdapter(mAdapter) diff --git a/app/src/main/java/com/nextcloud/client/widget/DashboardWidgetUpdater.kt b/app/src/main/java/com/nextcloud/client/widget/DashboardWidgetUpdater.kt index 3430664dfd8d..abbff4c4c95d 100644 --- a/app/src/main/java/com/nextcloud/client/widget/DashboardWidgetUpdater.kt +++ b/app/src/main/java/com/nextcloud/client/widget/DashboardWidgetUpdater.kt @@ -19,7 +19,6 @@ import androidx.core.net.toUri import com.bumptech.glide.request.target.AppWidgetTarget import com.nextcloud.android.lib.resources.dashboard.DashboardButton import com.nextcloud.client.account.CurrentAccountProvider -import com.nextcloud.client.network.ClientFactory import com.nextcloud.utils.GlideHelper import com.owncloud.android.R import com.owncloud.android.lib.common.OwnCloudClientManagerFactory @@ -32,9 +31,9 @@ import javax.inject.Inject class DashboardWidgetUpdater @Inject constructor( private val context: Context, - private val clientFactory: ClientFactory, private val accountProvider: CurrentAccountProvider ) { + private val scope = CoroutineScope(Dispatchers.IO) fun updateAppWidget( appWidgetManager: AppWidgetManager, @@ -155,7 +154,7 @@ class DashboardWidgetUpdater @Inject constructor( private fun loadIcon(appWidgetId: Int, iconUrl: String, remoteViews: RemoteViews) { val target = AppWidgetTarget(context, R.id.icon, remoteViews, appWidgetId) - CoroutineScope(Dispatchers.IO).launch { + scope.launch { val client = OwnCloudClientManagerFactory.getDefaultSingleton() .getNextcloudClientFor(accountProvider.user.toOwnCloudAccount(), context) val drawable = GlideHelper.getDrawable(context, client, iconUrl) diff --git a/app/src/main/java/com/nextcloud/model/SearchResultEntryType.kt b/app/src/main/java/com/nextcloud/model/SearchResultEntryType.kt index e0343aed9efe..7eb6068fb1d0 100644 --- a/app/src/main/java/com/nextcloud/model/SearchResultEntryType.kt +++ b/app/src/main/java/com/nextcloud/model/SearchResultEntryType.kt @@ -29,9 +29,11 @@ enum class SearchResultEntryType { TextCode, Link, Font, + Avatar, Unknown; fun iconId(): Int = when (this) { + Avatar -> R.drawable.ic_user CalendarEvent -> R.drawable.file_calendar Folder -> R.drawable.folder Note -> R.drawable.ic_edit diff --git a/app/src/main/java/com/nextcloud/utils/GlideHelper.kt b/app/src/main/java/com/nextcloud/utils/GlideHelper.kt index ce89d910eacd..36847652f071 100644 --- a/app/src/main/java/com/nextcloud/utils/GlideHelper.kt +++ b/app/src/main/java/com/nextcloud/utils/GlideHelper.kt @@ -13,9 +13,11 @@ import android.graphics.Bitmap import android.graphics.drawable.Drawable import android.graphics.drawable.PictureDrawable import android.widget.ImageView +import androidx.activity.ComponentActivity import androidx.annotation.DrawableRes import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory import androidx.core.net.toUri +import androidx.lifecycle.lifecycleScope import com.bumptech.glide.Glide import com.bumptech.glide.RequestBuilder import com.bumptech.glide.load.DataSource @@ -28,8 +30,13 @@ import com.bumptech.glide.request.target.BitmapImageViewTarget import com.bumptech.glide.request.target.Target import com.nextcloud.common.NextcloudClient import com.nextcloud.utils.LinkHelper.validateAndGetURL +import com.owncloud.android.lib.common.OwnCloudAccount +import com.owncloud.android.lib.common.OwnCloudClientManagerFactory import com.owncloud.android.lib.common.utils.Log_OC import com.owncloud.android.utils.svg.SvgSoftwareLayerSetter +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext /** * Utility object for loading images (including SVGs) using Glide. @@ -37,27 +44,111 @@ import com.owncloud.android.utils.svg.SvgSoftwareLayerSetter * Provides methods for loading images into `ImageView`, `Target`, `Target` ... * from both URLs and URIs. */ -@Suppress("TooManyFunctions") +@Suppress("TooManyFunctions", "TooGenericExceptionCaught") object GlideHelper { private const val TAG = "GlideHelper" - private class GlideLogger(private val methodName: String, private val identifier: String) : RequestListener { - override fun onLoadFailed(p0: GlideException?, p1: Any?, p2: Target, p3: Boolean): Boolean { - Log_OC.e(TAG, "$methodName: Load failed for $identifier") - Log_OC.e(TAG, "$methodName: Error: ${p0?.message}") - p0?.logRootCauses(TAG) - return false + @Suppress("TooGenericExceptionCaught") + fun getBitmap(context: Context, url: String?): Bitmap? { + val validatedUrl = validateAndGetURL(url) ?: return null + + return try { + Glide.with(context) + .asBitmap() + .load(validatedUrl) + .diskCacheStrategy(DiskCacheStrategy.NONE) + .skipMemoryCache(true) + .withLogging("downloadImageSynchronous", validatedUrl) + .submit(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL) + .get() + } catch (e: Exception) { + Log_OC.e(TAG, "exception getBitmap: $e") + null } + } - override fun onResourceReady(p0: T & Any, p1: Any, p2: Target?, p3: DataSource, p4: Boolean): Boolean { - Log_OC.i(TAG, "Glide load completed: $p0") - return false + fun loadCircularBitmapIntoImageView(context: Context, url: String?, imageView: ImageView, placeholder: Drawable?) { + val validatedUrl = validateAndGetURL(url) ?: return + + try { + Glide.with(context) + .asBitmap() + .load(validatedUrl) + .placeholder(placeholder) + .error(placeholder) + .withLogging("loadCircularBitmapIntoImageView", validatedUrl) + .into(object : BitmapImageViewTarget(imageView) { + override fun setResource(resource: Bitmap?) { + val circularBitmapDrawable = RoundedBitmapDrawableFactory.create(context.resources, resource) + circularBitmapDrawable.isCircular = true + imageView.setImageDrawable(circularBitmapDrawable) + } + }) + } catch (e: Exception) { + Log_OC.e(TAG, "exception loadCircularBitmapIntoImageView: $e") + imageView.setImageDrawable(placeholder) } } - private fun isSVG(url: String): Boolean = (url.toUri().encodedPath?.endsWith(".svg") == true) + @SuppressLint("CheckResult") + fun loadIntoImageView( + context: Context, + client: NextcloudClient?, + url: String?, + imageView: ImageView, + @DrawableRes placeholder: Int, + circleCrop: Boolean = false + ) { + try { + createRequestBuilder(context, client, url) + ?.placeholder(placeholder) + ?.error(placeholder) + ?.apply { if (circleCrop) circleCrop() } + ?.withLogging("loadIntoImageView", url ?: "null") + ?.into(imageView) ?: imageView.setImageResource(placeholder) + } catch (e: Exception) { + Log_OC.e(TAG, "exception loadIntoImageView: $e") + imageView.setImageResource(placeholder) + } + } + + fun getDrawable(context: Context, client: NextcloudClient?, urlString: String?): Drawable? = try { + createRequestBuilder(context, client, urlString)?.submit()?.get() + } catch (e: Exception) { + Log_OC.e(TAG, "exception getDrawable: $e") + null + } + + fun loadIntoTarget( + activity: ComponentActivity, + account: OwnCloudAccount?, + url: String, + target: Target, + @DrawableRes placeholder: Int + ) { + if (account == null) { + Log_OC.e(TAG, "loadIntoTargetWithActivity: account cannot be null") + return + } + + activity.lifecycleScope.launch(Dispatchers.IO) { + val clientFactory = OwnCloudClientManagerFactory.getDefaultSingleton() + val client = clientFactory.getNextcloudClientFor(account, activity) + withContext(Dispatchers.Main) { + try { + createRequestBuilder(activity, client, url) + ?.placeholder(placeholder) + ?.error(placeholder) + ?.withLogging("loadIntoTarget", url) + ?.into(target) + } catch (e: Exception) { + Log_OC.e(TAG, "exception loadIntoTarget: $e") + } + } + } + } - private fun createGlideUrl(url: String, client: NextcloudClient) = GlideUrl( + fun createGlideUrl(url: String, client: NextcloudClient) = GlideUrl( url, LazyHeaders.Builder() .addHeader("Authorization", client.credentials) @@ -65,6 +156,35 @@ object GlideHelper { .build() ) + // region private methods + private class GlideLogger(private val methodName: String, private val identifier: String) : RequestListener { + + override fun onLoadFailed( + e: GlideException?, + model: Any?, + target: Target, + isFirstResource: Boolean + ): Boolean { + Log_OC.e(TAG, "$methodName: Load failed for $identifier") + Log_OC.e(TAG, "$methodName: Error: ${e?.message}") + e?.logRootCauses(TAG) + return false + } + + override fun onResourceReady( + resource: T & Any, + model: Any?, + target: Target?, + dataSource: DataSource, + isFirstResource: Boolean + ): Boolean { + Log_OC.i(TAG, "$methodName: Successfully loaded $identifier from $dataSource") + return false + } + } + + private fun isSVG(url: String): Boolean = (url.toUri().encodedPath?.endsWith(".svg") == true) + private fun RequestBuilder.withLogging(methodName: String, identifier: String): RequestBuilder = listener(GlideLogger(methodName, identifier)) @@ -81,8 +201,10 @@ object GlideHelper { .`as`(PictureDrawable::class.java) .load(glideUrl) .apply { - placeholder?.let { placeholder(it) } - placeholder?.let { error(it) } + placeholder?.let { + placeholder(it) + error(it) + } } .listener(SvgSoftwareLayerSetter()) } @@ -94,47 +216,11 @@ object GlideHelper { ): RequestBuilder { val glideUrl = createGlideUrl(url, client) return Glide.with(context) + .asDrawable() .load(glideUrl) .centerCrop() } - @Suppress("TooGenericExceptionCaught") - fun getBitmap(context: Context, url: String?): Bitmap? { - val validatedUrl = validateAndGetURL(url) ?: return null - - return try { - Glide.with(context) - .asBitmap() - .load(validatedUrl) - .diskCacheStrategy(DiskCacheStrategy.NONE) - .skipMemoryCache(true) - .withLogging("downloadImageSynchronous", validatedUrl) - .submit(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL) - .get() - } catch (e: Exception) { - Log_OC.e(TAG, "Could not download image $e") - null - } - } - - fun loadCircularBitmapIntoImageView(context: Context, url: String?, imageView: ImageView, placeholder: Drawable) { - val validatedUrl = validateAndGetURL(url) ?: return - - Glide.with(context) - .asBitmap() - .load(validatedUrl) - .placeholder(placeholder) - .error(placeholder) - .withLogging("loadCircularBitmapIntoImageView", validatedUrl) - .into(object : BitmapImageViewTarget(imageView) { - override fun setResource(resource: Bitmap?) { - val circularBitmapDrawable = RoundedBitmapDrawableFactory.create(context.resources, resource) - circularBitmapDrawable.isCircular = true - imageView.setImageDrawable(circularBitmapDrawable) - } - }) - } - @Suppress("UNCHECKED_CAST", "TooGenericExceptionCaught", "ReturnCount") private fun createRequestBuilder(context: Context, client: NextcloudClient?, url: String?): RequestBuilder? { if (client == null) { @@ -147,47 +233,15 @@ object GlideHelper { return try { val isSVG = isSVG(validatedUrl) - return if (isSVG) { + if (isSVG) { createSvgRequestBuilder(context, validatedUrl, client) } else { createUrlRequestBuilder(context, client, validatedUrl) - } - .withLogging("createRequestBuilder", validatedUrl) as RequestBuilder? + }.withLogging("createRequestBuilder", validatedUrl) as RequestBuilder? } catch (e: Exception) { - Log_OC.e(TAG, "Error createRequestBuilder: $e") + Log_OC.e(TAG, "exception createRequestBuilder: $e") null } } - - @SuppressLint("CheckResult") - fun loadIntoImageView( - context: Context, - client: NextcloudClient?, - url: String?, - imageView: ImageView, - @DrawableRes placeholder: Int, - circleCrop: Boolean = false - ) { - createRequestBuilder(context, client, url) - ?.placeholder(placeholder) - ?.error(placeholder) - ?.apply { if (circleCrop) circleCrop() } - ?.into(imageView) - } - - fun getDrawable(context: Context, client: NextcloudClient?, urlString: String?): Drawable? = - createRequestBuilder(context, client, urlString)?.submit()?.get() - - fun loadIntoTarget( - context: Context, - client: NextcloudClient?, - url: String, - target: Target, - @DrawableRes placeholder: Int - ) { - createRequestBuilder(context, client, url) - ?.placeholder(placeholder) - ?.error(placeholder) - ?.into(target) - } + // endregion } diff --git a/app/src/main/java/com/nextcloud/utils/extensions/SearchResultEntryExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/SearchResultEntryExtensions.kt index f7df7f63b6de..8a42a2f869ec 100644 --- a/app/src/main/java/com/nextcloud/utils/extensions/SearchResultEntryExtensions.kt +++ b/app/src/main/java/com/nextcloud/utils/extensions/SearchResultEntryExtensions.kt @@ -13,6 +13,11 @@ import com.owncloud.android.lib.common.SearchResultEntry fun SearchResultEntry.getType(): SearchResultEntryType { val value = icon.lowercase() + fun isAvatarUrl(url: String): Boolean { + val regex = Regex("""^https?://[^/]+/avatar/[^/]+/\d+$""") + return regex.matches(url) + } + return when { value.contains("icon-folder") -> SearchResultEntryType.Folder value.contains("icon-note") -> SearchResultEntryType.Note @@ -33,6 +38,7 @@ fun SearchResultEntry.getType(): SearchResultEntryType { value.contains("text-code") -> SearchResultEntryType.TextCode value.contains("link") -> SearchResultEntryType.Link value.contains("font") -> SearchResultEntryType.Font + isAvatarUrl(thumbnailUrl) -> SearchResultEntryType.Avatar else -> SearchResultEntryType.Unknown } } diff --git a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java index cff5cb42600a..379457aa3526 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java @@ -129,7 +129,6 @@ import androidx.fragment.app.Fragment; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import hct.Hct; -import kotlin.Unit; /** * Base class to handle setup of the drawer implementation including user switching and avatar fetching and fallback @@ -425,14 +424,11 @@ public void updateHeader() { if (!TextUtils.isEmpty(serverLogoURL) && URLUtil.isValidUrl(serverLogoURL)) { Target target = createSVGLogoTarget(primaryColor, capability); - getClientRepository().getNextcloudClient(nextcloudClient -> { - GlideHelper.INSTANCE.loadIntoTarget(DrawerActivity.this, - nextcloudClient, - serverLogoURL, - target, - R.drawable.background); - return Unit.INSTANCE; - }); + GlideHelper.INSTANCE.loadIntoTarget(this, + accountManager.getCurrentOwnCloudAccount(), + serverLogoURL, + target, + R.drawable.background); } } } @@ -965,14 +961,11 @@ private void updateQuotaLink() { }); Target quotaTarget = createQuotaDrawableTarget(size, mQuotaTextLink); - getClientRepository().getNextcloudClient(nextcloudClient -> { - GlideHelper.INSTANCE.loadIntoTarget(this, - nextcloudClient, - firstQuota.getIconUrl(), - quotaTarget, - R.drawable.ic_link); - return Unit.INSTANCE; - }); + GlideHelper.INSTANCE.loadIntoTarget(this, + accountManager.getCurrentOwnCloudAccount(), + firstQuota.getIconUrl(), + quotaTarget, + R.drawable.ic_link); } else { mQuotaTextLink.setVisibility(View.GONE); } @@ -1097,15 +1090,11 @@ private void updateExternalLinksInDrawer() { .getItemId(); Target iconTarget = createMenuItemTarget(id, greyColor); - getClientRepository().getNextcloudClient(nextcloudClient -> { - GlideHelper.INSTANCE.loadIntoTarget( - this, - nextcloudClient, - link.getIconUrl(), - iconTarget, - R.drawable.ic_link); - return Unit.INSTANCE; - }); + GlideHelper.INSTANCE.loadIntoTarget(this, + accountManager.getCurrentOwnCloudAccount(), + link.getIconUrl(), + iconTarget, + R.drawable.ic_link); } } diff --git a/app/src/main/java/com/owncloud/android/ui/activity/UserInfoActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/UserInfoActivity.java index dd137e4f9581..ae7e644965b3 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/UserInfoActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/UserInfoActivity.java @@ -67,7 +67,6 @@ import androidx.fragment.app.FragmentManager; import androidx.lifecycle.Lifecycle; import androidx.recyclerview.widget.RecyclerView; -import kotlin.Unit; /** * This Activity presents the user information. @@ -209,14 +208,11 @@ private void setHeaderImage() { } Target backgroundImageTarget = createBackgroundImageTarget(backgroundImageView); - getClientRepository().getNextcloudClient(nextcloudClient -> { - GlideHelper.INSTANCE.loadIntoTarget(this, - nextcloudClient, - backgroundURL, - backgroundImageTarget, - R.drawable.background); - return Unit.INSTANCE; - }); + GlideHelper.INSTANCE.loadIntoTarget(this, + accountManager.getCurrentOwnCloudAccount(), + backgroundURL, + backgroundImageTarget, + R.drawable.background); } private Target createBackgroundImageTarget(ImageView backgroundImageView) { diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/DashboardWidgetListAdapter.kt b/app/src/main/java/com/owncloud/android/ui/adapter/DashboardWidgetListAdapter.kt index 154ab99e1018..d8d6b96d285f 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/DashboardWidgetListAdapter.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/DashboardWidgetListAdapter.kt @@ -17,8 +17,10 @@ import com.nextcloud.client.account.UserAccountManager import com.nextcloud.client.network.ClientFactory import com.nextcloud.client.widget.DashboardWidgetConfigurationInterface import com.owncloud.android.databinding.WidgetListItemBinding +import kotlinx.coroutines.CoroutineScope class DashboardWidgetListAdapter( + private val lifecycleScope: CoroutineScope, val accountManager: UserAccountManager, val clientFactory: ClientFactory, val context: Context, @@ -28,6 +30,7 @@ class DashboardWidgetListAdapter( override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder = WidgetListItemViewHolder( + lifecycleScope, WidgetListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false), accountManager, clientFactory, diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/UnifiedSearchItemViewHolder.kt b/app/src/main/java/com/owncloud/android/ui/adapter/UnifiedSearchItemViewHolder.kt index 9d9412fd5244..174fdeff666e 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/UnifiedSearchItemViewHolder.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/UnifiedSearchItemViewHolder.kt @@ -140,14 +140,23 @@ class UnifiedSearchItemViewHolder( if (entry.thumbnailUrl.isNotBlank()) { filesAction.loadFileThumbnail(entry) { client -> - GlideHelper.loadIntoImageView( - context, - client, - entry.thumbnailUrl, - binding.thumbnail, - entryType.iconId(), - circleCrop = entry.rounded - ) + if (entryType == SearchResultEntryType.Avatar) { + GlideHelper.loadCircularBitmapIntoImageView( + context, + entry.thumbnailUrl, + binding.thumbnail, + ContextCompat.getDrawable(context, R.drawable.ic_user) + ) + } else { + GlideHelper.loadIntoImageView( + context, + client, + entry.thumbnailUrl, + binding.thumbnail, + entryType.iconId(), + circleCrop = entry.rounded + ) + } } } else { binding.thumbnail.setImageDrawable(ContextCompat.getDrawable(context, entryType.iconId())) diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/WidgetListItemViewHolder.kt b/app/src/main/java/com/owncloud/android/ui/adapter/WidgetListItemViewHolder.kt index 80d8858d2cc9..c494ca40b023 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/WidgetListItemViewHolder.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/WidgetListItemViewHolder.kt @@ -23,6 +23,7 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withContext class WidgetListItemViewHolder( + private val lifecycleScope: CoroutineScope, val binding: WidgetListItemBinding, val accountManager: UserAccountManager, val clientFactory: ClientFactory, @@ -34,7 +35,7 @@ class WidgetListItemViewHolder( ) { binding.layout.setOnClickListener { dashboardWidgetConfigurationInterface.onItemClicked(dashboardWidget) } - CoroutineScope(Dispatchers.IO).launch { + lifecycleScope.launch(Dispatchers.IO) { val client = OwnCloudClientManagerFactory.getDefaultSingleton() .getNextcloudClientFor(accountManager.currentOwnCloudAccount, context) diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/contactsbackup/BackupListAdapter.kt b/app/src/main/java/com/owncloud/android/ui/fragment/contactsbackup/BackupListAdapter.kt index 69d71a5872f8..553cc55908ca 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/contactsbackup/BackupListAdapter.kt +++ b/app/src/main/java/com/owncloud/android/ui/fragment/contactsbackup/BackupListAdapter.kt @@ -20,7 +20,6 @@ import android.view.ViewGroup import android.widget.AdapterView import android.widget.CheckedTextView import android.widget.ImageView -import androidx.lifecycle.lifecycleScope import com.afollestad.sectionedrecyclerview.SectionedRecyclerViewAdapter import com.afollestad.sectionedrecyclerview.SectionedViewHolder import com.bumptech.glide.request.target.CustomTarget @@ -33,7 +32,6 @@ import com.owncloud.android.databinding.BackupListItemHeaderBinding import com.owncloud.android.databinding.CalendarlistListItemBinding import com.owncloud.android.databinding.ContactlistListItemBinding import com.owncloud.android.datamodel.OCFile -import com.owncloud.android.lib.common.OwnCloudClientManagerFactory import com.owncloud.android.lib.common.utils.Log_OC import com.owncloud.android.ui.TextDrawable import com.owncloud.android.ui.fragment.contactsbackup.BackupListFragment.getDisplayName @@ -41,10 +39,6 @@ import com.owncloud.android.utils.BitmapUtils import com.owncloud.android.utils.theme.ViewThemeUtils import ezvcard.VCard import ezvcard.property.Photo -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext import third_parties.sufficientlysecure.AndroidCalendar @Suppress("LongParameterList", "TooManyFunctions") @@ -256,20 +250,13 @@ class BackupListAdapter( } } - backupListFragment.lifecycleScope.launch(Dispatchers.IO) { - val client = OwnCloudClientManagerFactory.getDefaultSingleton() - .getNextcloudClientFor(accountManager.currentOwnCloudAccount, context) - - withContext(Dispatchers.Main) { - GlideHelper.loadIntoTarget( - context, - client, - url, - target, - R.drawable.ic_user_outline - ) - } - } + GlideHelper.loadIntoTarget( + backupListFragment.requireActivity(), + accountManager.currentOwnCloudAccount, + url, + target, + R.drawable.ic_user_outline + ) } } diff --git a/app/src/main/java/com/owncloud/android/utils/glide/CustomGlideUriLoader.kt b/app/src/main/java/com/owncloud/android/utils/glide/CustomGlideUriLoader.kt index b626bde5bc11..9a4f1cf450a1 100644 --- a/app/src/main/java/com/owncloud/android/utils/glide/CustomGlideUriLoader.kt +++ b/app/src/main/java/com/owncloud/android/utils/glide/CustomGlideUriLoader.kt @@ -20,7 +20,7 @@ import java.io.InputStream */ class CustomGlideUriLoader : ModelLoader { override fun buildLoadData(uri: Uri, width: Int, height: Int, options: Options): LoadData = - LoadData(ObjectKey(uri), HttpStreamFetcher(uri.toString())) + LoadData(ObjectKey(uri), HttpStreamFetcher(uri.toString())) override fun handles(uri: Uri): Boolean = false } diff --git a/app/src/main/java/com/owncloud/android/utils/glide/GlideStringStreamFetcher.kt b/app/src/main/java/com/owncloud/android/utils/glide/GlideStringStreamFetcher.kt index 685973c24f33..e34afb909563 100644 --- a/app/src/main/java/com/owncloud/android/utils/glide/GlideStringStreamFetcher.kt +++ b/app/src/main/java/com/owncloud/android/utils/glide/GlideStringStreamFetcher.kt @@ -25,46 +25,47 @@ import java.io.InputStream class GlideStringStreamFetcher(private val url: String?) : DataFetcher { private var stream: InputStream? = null + private var get: GetMethod? = null @Suppress("TooGenericExceptionCaught") override fun loadData(priority: Priority, callback: DataFetcher.DataCallback) { - var get: GetMethod? = null try { - val ownCloudAccount = - UserAccountManagerImpl.fromContext(MainApp.getAppContext()).currentOwnCloudAccount + val ownCloudAccount = UserAccountManagerImpl.fromContext(MainApp.getAppContext()).currentOwnCloudAccount val client = OwnCloudClientManagerFactory.getDefaultSingleton() .getClientFor(ownCloudAccount, MainApp.getAppContext()) + get = GetMethod(url) - get.setRequestHeader("Cookie", "nc_sameSiteCookielax=true;nc_sameSiteCookiestrict=true") - get.setRequestHeader(RemoteOperation.OCS_API_HEADER, RemoteOperation.OCS_API_HEADER_VALUE) - val status = client.executeMethod(get) + get?.setRequestHeader("Cookie", "nc_sameSiteCookielax=true;nc_sameSiteCookiestrict=true") + get?.setRequestHeader(RemoteOperation.OCS_API_HEADER, RemoteOperation.OCS_API_HEADER_VALUE) + + val status = client?.executeMethod(get) if (status == HttpStatus.SC_OK) { - val inputStream = get.getResponseBodyAsStream() - this.stream = inputStream - callback.onDataReady(inputStream) + stream = get?.responseBodyAsStream + stream?.let { callback.onDataReady(it) } ?: callback.onLoadFailed(IOException("Stream is null")) } else { - client.exhaustResponse(get.getResponseBodyAsStream()) + client?.exhaustResponse(get?.responseBodyAsStream) callback.onLoadFailed(IOException("Unexpected HTTP status $status")) } } catch (e: Exception) { - Log_OC.e(TAG, e.message, e) + Log_OC.e(TAG, "exception GlideStringStreamFetcher.loadData: $e") callback.onLoadFailed(e) - } finally { - get?.releaseConnection() } } override fun cleanup() { - Log_OC.i(TAG, "Cleanup") try { stream?.close() - } catch (e: IOException) { - Log_OC.w(TAG, "Cleanup failed$e") + } catch (_: IOException) { + } finally { + get?.releaseConnection() } } override fun cancel() { - Log_OC.i(TAG, "Cancel") + try { + get?.abort() + } catch (_: Exception) { + } } override fun getDataClass(): Class = InputStream::class.java diff --git a/app/src/main/java/com/owncloud/android/utils/glide/GlideStringStreamLoader.kt b/app/src/main/java/com/owncloud/android/utils/glide/GlideStringStreamLoader.kt index 7ea6350cc64a..bb80e611ffe4 100644 --- a/app/src/main/java/com/owncloud/android/utils/glide/GlideStringStreamLoader.kt +++ b/app/src/main/java/com/owncloud/android/utils/glide/GlideStringStreamLoader.kt @@ -18,7 +18,7 @@ import java.io.InputStream */ class GlideStringStreamLoader : ModelLoader { override fun buildLoadData(url: String, width: Int, height: Int, options: Options): LoadData = - LoadData(ObjectKey(url), GlideStringStreamFetcher(url)) + LoadData(ObjectKey(url), GlideStringStreamFetcher(url)) override fun handles(s: String): Boolean = true } diff --git a/app/src/main/java/com/owncloud/android/utils/glide/HttpStreamFetcher.kt b/app/src/main/java/com/owncloud/android/utils/glide/HttpStreamFetcher.kt index 6985cba53083..f519b4a3c701 100644 --- a/app/src/main/java/com/owncloud/android/utils/glide/HttpStreamFetcher.kt +++ b/app/src/main/java/com/owncloud/android/utils/glide/HttpStreamFetcher.kt @@ -15,11 +15,8 @@ import com.nextcloud.client.account.UserAccountManagerImpl import com.owncloud.android.MainApp import com.owncloud.android.lib.common.OwnCloudClientManagerFactory import com.owncloud.android.lib.common.operations.RemoteOperation -import com.owncloud.android.lib.common.utils.Log_OC import org.apache.commons.httpclient.HttpStatus import org.apache.commons.httpclient.methods.GetMethod -import java.io.ByteArrayInputStream -import java.io.ByteArrayOutputStream import java.io.IOException import java.io.InputStream @@ -27,72 +24,55 @@ import java.io.InputStream class HttpStreamFetcher internal constructor(private val url: String) : DataFetcher { private var stream: InputStream? = null + private var get: GetMethod? = null - @Throws(Exception::class) override fun loadData(priority: Priority, callback: DataFetcher.DataCallback) { - val ownCloudAccount = - UserAccountManagerImpl.fromContext(MainApp.getAppContext()).currentOwnCloudAccount - val client = OwnCloudClientManagerFactory.getDefaultSingleton() - .getClientFor(ownCloudAccount, MainApp.getAppContext()) - if (client == null || url.isBlank()) { - callback.onLoadFailed(IllegalStateException("Invalid client or URL")) - return - } - - var get: GetMethod? = null try { + val ownCloudAccount = UserAccountManagerImpl.fromContext(MainApp.getAppContext()).currentOwnCloudAccount + val client = OwnCloudClientManagerFactory.getDefaultSingleton() + .getClientFor(ownCloudAccount, MainApp.getAppContext()) + + if (client == null || url.isBlank()) { + callback.onLoadFailed(IllegalStateException("Invalid client or URL")) + return + } + get = GetMethod(url) - get.setRequestHeader("Cookie", "nc_sameSiteCookielax=true;nc_sameSiteCookiestrict=true") - get.setRequestHeader(RemoteOperation.OCS_API_HEADER, RemoteOperation.OCS_API_HEADER_VALUE) + get?.setRequestHeader("Cookie", "nc_sameSiteCookielax=true;nc_sameSiteCookiestrict=true") + get?.setRequestHeader(RemoteOperation.OCS_API_HEADER, RemoteOperation.OCS_API_HEADER_VALUE) val status = client.executeMethod(get) if (status == HttpStatus.SC_OK) { - val inputStream = getResponseAsInputStream(get) - this.stream = inputStream - callback.onDataReady(inputStream) + stream = get?.responseBodyAsStream + stream?.let { callback.onDataReady(it) } ?: callback.onLoadFailed(IOException("Stream is null")) } else { - client.exhaustResponse(get.responseBodyAsStream) + client.exhaustResponse(get?.responseBodyAsStream) callback.onLoadFailed(IOException("Unexpected HTTP status $status")) } } catch (e: Exception) { - Log_OC.e(TAG, e.message, e) callback.onLoadFailed(e) - } finally { - get?.releaseConnection() - } - } - - private fun getResponseAsInputStream(getMethod: GetMethod): ByteArrayInputStream { - val byteOutputStream = ByteArrayOutputStream() - getMethod.responseBodyAsStream.use { input -> - byteOutputStream.use { output -> - input.copyTo(output) - } } - - return ByteArrayInputStream(byteOutputStream.toByteArray()) } override fun cleanup() { - Log_OC.i(TAG, "Cleanup") try { stream?.close() - } catch (e: IOException) { - Log_OC.w(TAG, "Cleanup failed$e") + } catch (_: IOException) { + } finally { + get?.releaseConnection() } } fun getId(): String = url override fun cancel() { - Log_OC.i(TAG, "Cancel") + try { + get?.abort() + } catch (_: Exception) { + } } override fun getDataClass(): Class = InputStream::class.java override fun getDataSource(): DataSource = DataSource.REMOTE - - companion object { - private val TAG = HttpStreamFetcher::class.java.name - } } diff --git a/app/src/main/java/com/owncloud/android/utils/svg/SvgDecoder.kt b/app/src/main/java/com/owncloud/android/utils/svg/SvgDecoder.kt index 9bd0713fab05..d6c3c32c7f66 100644 --- a/app/src/main/java/com/owncloud/android/utils/svg/SvgDecoder.kt +++ b/app/src/main/java/com/owncloud/android/utils/svg/SvgDecoder.kt @@ -27,7 +27,7 @@ class SvgDecoder : ResourceDecoder { override fun decode(source: InputStream, width: Int, height: Int, options: Options): Resource { try { val svg = SVG.getFromInputStream(source) - return SimpleResource(svg) + return SimpleResource(svg) } catch (ex: SVGParseException) { throw IOException("Cannot load SVG from stream", ex) } diff --git a/app/src/main/java/com/owncloud/android/utils/svg/SvgDrawableTranscoder.kt b/app/src/main/java/com/owncloud/android/utils/svg/SvgDrawableTranscoder.kt index ec961eab890c..9ad504066ceb 100644 --- a/app/src/main/java/com/owncloud/android/utils/svg/SvgDrawableTranscoder.kt +++ b/app/src/main/java/com/owncloud/android/utils/svg/SvgDrawableTranscoder.kt @@ -26,6 +26,6 @@ class SvgDrawableTranscoder : ResourceTranscoder { val svg = toTranscode.get() val picture = svg.renderToPicture() val drawable = PictureDrawable(picture) - return SimpleResource(drawable) + return SimpleResource(drawable) } }