From 3367c4cda8b6abb0ba7f940cb1efe7373a4c70c1 Mon Sep 17 00:00:00 2001 From: David Luhmer Date: Mon, 12 Sep 2022 16:56:37 +0200 Subject: [PATCH 1/8] replace universal-image-loader with glide --- .../SubscriptionExpandableListAdapter.java | 14 +-- .../NewsDetailImageDialogFragment.java | 3 +- .../NewsReaderListActivity.java | 23 +++-- .../owncloudnewsreader/PodcastFragment.java | 22 +++-- .../owncloudnewsreader/SettingsFragment.java | 58 ++++++----- .../RssItemHeadlineThumbnailViewHolder.java | 34 ++++--- .../adapter/RssItemThumbnailViewHolder.java | 39 ++++---- .../adapter/RssItemViewHolder.java | 12 ++- .../adapter/RssItemWebViewHolder.java | 2 +- .../async_tasks/DownloadImageHandler.java | 60 ++++-------- .../async_tasks/RssItemToHtmlTask.java | 90 +++++++++++++---- .../owncloudnewsreader/di/ApiProvider.java | 35 +------ .../owncloudnewsreader/di/AppComponent.java | 3 + .../helper/FavIconHandler.java | 98 +++++++++++-------- .../helper/FavIconUtils.java | 67 +++++++++++++ .../helper/ImageHandler.java | 15 ++- .../helper/NewsFileUtils.java | 11 +-- .../helper/NextcloudGlideModule.java | 47 +++++++++ .../helper/SquareRoundedBitmapDisplayer.java | 72 -------------- .../NextcloudNotificationManager.java | 81 +++++++-------- .../reader/InsertIntoDatabase.java | 6 +- .../reader/OkHttpImageDownloader.java | 40 -------- .../services/DownloadImagesService.java | 15 ++- 23 files changed, 431 insertions(+), 416 deletions(-) create mode 100644 News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/helper/FavIconUtils.java create mode 100644 News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/helper/NextcloudGlideModule.java delete mode 100644 News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/helper/SquareRoundedBitmapDisplayer.java delete mode 100644 News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/reader/OkHttpImageDownloader.java diff --git a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/ListView/SubscriptionExpandableListAdapter.java b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/ListView/SubscriptionExpandableListAdapter.java index 960247cd9..4abb6704b 100644 --- a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/ListView/SubscriptionExpandableListAdapter.java +++ b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/ListView/SubscriptionExpandableListAdapter.java @@ -21,6 +21,10 @@ package de.luhmer.owncloudnewsreader.ListView; +import static de.luhmer.owncloudnewsreader.ListView.SubscriptionExpandableListAdapter.SPECIAL_FOLDERS.ALL_STARRED_ITEMS; +import static de.luhmer.owncloudnewsreader.ListView.SubscriptionExpandableListAdapter.SPECIAL_FOLDERS.ALL_UNREAD_ITEMS; +import static de.luhmer.owncloudnewsreader.ListView.SubscriptionExpandableListAdapter.SPECIAL_FOLDERS.ITEMS_WITHOUT_FOLDER; + import android.annotation.SuppressLint; import android.content.Context; import android.content.SharedPreferences; @@ -29,18 +33,18 @@ import android.util.SparseArray; import android.view.LayoutInflater; import android.view.View; -import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.BaseExpandableListAdapter; import android.widget.ExpandableListView; import android.widget.LinearLayout; import android.widget.ListView; +import androidx.annotation.NonNull; +import androidx.core.view.ViewCompat; + import java.util.ArrayList; import java.util.List; -import androidx.annotation.NonNull; -import androidx.core.view.ViewCompat; import de.luhmer.owncloudnewsreader.R; import de.luhmer.owncloudnewsreader.SettingsActivity; import de.luhmer.owncloudnewsreader.database.DatabaseConnectionOrm; @@ -57,10 +61,6 @@ import de.luhmer.owncloudnewsreader.model.FolderSubscribtionItem; import de.luhmer.owncloudnewsreader.model.Tuple; -import static de.luhmer.owncloudnewsreader.ListView.SubscriptionExpandableListAdapter.SPECIAL_FOLDERS.ALL_STARRED_ITEMS; -import static de.luhmer.owncloudnewsreader.ListView.SubscriptionExpandableListAdapter.SPECIAL_FOLDERS.ALL_UNREAD_ITEMS; -import static de.luhmer.owncloudnewsreader.ListView.SubscriptionExpandableListAdapter.SPECIAL_FOLDERS.ITEMS_WITHOUT_FOLDER; - public class SubscriptionExpandableListAdapter extends BaseExpandableListAdapter { private final String TAG = getClass().getCanonicalName(); diff --git a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/NewsDetailImageDialogFragment.java b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/NewsDetailImageDialogFragment.java index 0ff781bd8..06ad0d1ec 100644 --- a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/NewsDetailImageDialogFragment.java +++ b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/NewsDetailImageDialogFragment.java @@ -327,7 +327,6 @@ private void downloadImage(URL url) { } private void storeCachedImage(String path) { - final String CHANNEL_ID = "Store cached Image"; if(isExternalStorageWritable()) { String filename = getFileNameFromPath(path, false); @@ -338,7 +337,7 @@ private void storeCachedImage(String path) { Toast.makeText(requireContext().getApplicationContext(), e.getMessage(), Toast.LENGTH_LONG).show(); } - NextcloudNotificationManager.showNotificationSaveSingleCachedImageService(requireContext().getApplicationContext(), CHANNEL_ID, dstPath); + NextcloudNotificationManager.showNotificationDownloadSingleImageComplete(requireContext().getApplicationContext(), dstPath); requireDialog().hide(); } else { Toast.makeText(requireContext().getApplicationContext(), getString(R.string.toast_img_notwriteable), Toast.LENGTH_LONG).show(); diff --git a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/NewsReaderListActivity.java b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/NewsReaderListActivity.java index 1dc0e8815..5a430c74b 100644 --- a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/NewsReaderListActivity.java +++ b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/NewsReaderListActivity.java @@ -63,6 +63,7 @@ import androidx.preference.PreferenceManager; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; +import com.bumptech.glide.load.engine.DiskCacheStrategy; import com.google.android.material.snackbar.Snackbar; import com.nextcloud.android.sso.AccountImporter; import com.nextcloud.android.sso.api.NextcloudAPI; @@ -76,9 +77,6 @@ import com.nextcloud.android.sso.exceptions.TokenMismatchException; import com.nextcloud.android.sso.helper.SingleAccountHelper; import com.nextcloud.android.sso.ui.UiExceptionManager; -import com.nostra13.universalimageloader.core.DisplayImageOptions; -import com.nostra13.universalimageloader.core.ImageLoader; -import com.nostra13.universalimageloader.core.display.CircleBitmapDisplayer; import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; @@ -106,6 +104,7 @@ import de.luhmer.owncloudnewsreader.databinding.ActivityNewsreaderBinding; import de.luhmer.owncloudnewsreader.events.podcast.FeedPanelSlideEvent; import de.luhmer.owncloudnewsreader.helper.DatabaseUtils; +import de.luhmer.owncloudnewsreader.helper.GlideApp; import de.luhmer.owncloudnewsreader.helper.ThemeChooser; import de.luhmer.owncloudnewsreader.model.OcsUser; import de.luhmer.owncloudnewsreader.reader.nextcloud.RssItemObservable; @@ -579,19 +578,19 @@ public void onTopItemLongClicked(long idFeed, boolean isFolder) { @Override public void onUserInfoUpdated(OcsUser userInfo) { final Drawable placeHolder = getDrawable(R.drawable.ic_baseline_account_circle_24); - DisplayImageOptions displayImageOptions = new DisplayImageOptions.Builder() - .displayer(new CircleBitmapDisplayer()) - .showImageOnLoading(placeHolder) - .showImageForEmptyUri(placeHolder) - .showImageOnFail(placeHolder) - .cacheOnDisk(true) - .cacheInMemory(true) - .build(); if (userInfo.getId() != null) { String mOc_root_path = mPrefs.getString(SettingsActivity.EDT_OWNCLOUDROOTPATH_STRING, null); String avatarUrl = mOc_root_path + "/index.php/avatar/" + Uri.encode(userInfo.getId()) + "/64"; - ImageLoader.getInstance().displayImage(avatarUrl, binding.toolbarLayout.avatar, displayImageOptions); + + GlideApp.with(this) + .load(avatarUrl) + .diskCacheStrategy(DiskCacheStrategy.DATA) + .placeholder(placeHolder) + .error(placeHolder) + .circleCrop() + .into(binding.toolbarLayout.avatar); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { binding.toolbarLayout.avatar.setTooltipText(userInfo.getDisplayName()); } diff --git a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/PodcastFragment.java b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/PodcastFragment.java index 19047f086..1f10653a4 100644 --- a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/PodcastFragment.java +++ b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/PodcastFragment.java @@ -30,8 +30,10 @@ import androidx.appcompat.app.AlertDialog; import androidx.fragment.app.Fragment; -import com.nostra13.universalimageloader.core.DisplayImageOptions; -import com.nostra13.universalimageloader.core.ImageLoader; +import com.bumptech.glide.load.MultiTransformation; +import com.bumptech.glide.load.engine.DiskCacheStrategy; +import com.bumptech.glide.load.resource.bitmap.CenterCrop; +import com.bumptech.glide.load.resource.bitmap.RoundedCorners; import com.sothree.slidinguppanel.SlidingUpPanelLayout; import org.greenrobot.eventbus.EventBus; @@ -52,6 +54,7 @@ import de.luhmer.owncloudnewsreader.events.podcast.StartDownloadPodcast; import de.luhmer.owncloudnewsreader.events.podcast.TogglePlayerStateEvent; import de.luhmer.owncloudnewsreader.events.podcast.WindPodcast; +import de.luhmer.owncloudnewsreader.helper.GlideApp; import de.luhmer.owncloudnewsreader.model.PodcastFeedItem; import de.luhmer.owncloudnewsreader.model.PodcastItem; import de.luhmer.owncloudnewsreader.services.PodcastDownloadService; @@ -390,12 +393,15 @@ private void displayMetadata(MediaMetadataCompat metadata) { String favIconUrl = metadata.getString(MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI); if(favIconUrl != null) { Log.d(TAG, "currentPlayingPodcastReceived: " + favIconUrl); - DisplayImageOptions displayImageOptions = new DisplayImageOptions.Builder(). - showImageOnLoading(R.drawable.default_feed_icon_light). - showImageForEmptyUri(R.drawable.default_feed_icon_light). - showImageOnFail(R.drawable.default_feed_icon_light). - build(); - ImageLoader.getInstance().displayImage(favIconUrl, binding.imgFeedFavicon, displayImageOptions); + + int placeholder = R.drawable.default_feed_icon_light; + GlideApp.with(this.mActivity) + .load(favIconUrl) + .diskCacheStrategy(DiskCacheStrategy.DATA) + .placeholder(placeholder) + .error(placeholder) + .transform(new MultiTransformation<>(new CenterCrop(), new RoundedCorners(10))) + .into(binding.imgFeedFavicon); } PlaybackService.VideoType mediaType = PlaybackService.VideoType.valueOf(metadata.getString(CURRENT_PODCAST_MEDIA_TYPE)); diff --git a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/SettingsFragment.java b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/SettingsFragment.java index e76eb16c0..8ed0a2f3d 100644 --- a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/SettingsFragment.java +++ b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/SettingsFragment.java @@ -1,8 +1,34 @@ package de.luhmer.owncloudnewsreader; +import static de.luhmer.owncloudnewsreader.Constants.USER_INFO_STRING; +import static de.luhmer.owncloudnewsreader.SettingsActivity.CB_MARK_AS_READ_WHILE_SCROLLING_STRING; +import static de.luhmer.owncloudnewsreader.SettingsActivity.CB_NAVIGATE_WITH_VOLUME_BUTTONS_STRING; +import static de.luhmer.owncloudnewsreader.SettingsActivity.CB_OLED_MODE; +import static de.luhmer.owncloudnewsreader.SettingsActivity.CB_REPORT_ISSUE; +import static de.luhmer.owncloudnewsreader.SettingsActivity.CB_SHOWONLYUNREAD_STRING; +import static de.luhmer.owncloudnewsreader.SettingsActivity.CB_SHOW_FAST_ACTIONS; +import static de.luhmer.owncloudnewsreader.SettingsActivity.CB_SKIP_DETAILVIEW_AND_OPEN_BROWSER_DIRECTLY_STRING; +import static de.luhmer.owncloudnewsreader.SettingsActivity.CB_SYNCONSTARTUP_STRING; +import static de.luhmer.owncloudnewsreader.SettingsActivity.CB_VERSION; +import static de.luhmer.owncloudnewsreader.SettingsActivity.EDT_CLEAR_CACHE; +import static de.luhmer.owncloudnewsreader.SettingsActivity.EDT_OWNCLOUDROOTPATH_STRING; +import static de.luhmer.owncloudnewsreader.SettingsActivity.EDT_PASSWORD_STRING; +import static de.luhmer.owncloudnewsreader.SettingsActivity.EDT_USERNAME_STRING; +import static de.luhmer.owncloudnewsreader.SettingsActivity.LV_CACHE_IMAGES_OFFLINE_STRING; +import static de.luhmer.owncloudnewsreader.SettingsActivity.PREF_SYNC_SETTINGS; +import static de.luhmer.owncloudnewsreader.SettingsActivity.SP_APP_THEME; +import static de.luhmer.owncloudnewsreader.SettingsActivity.SP_DISPLAY_BROWSER; +import static de.luhmer.owncloudnewsreader.SettingsActivity.SP_FEED_LIST_LAYOUT; +import static de.luhmer.owncloudnewsreader.SettingsActivity.SP_FONT_SIZE; +import static de.luhmer.owncloudnewsreader.SettingsActivity.SP_MAX_CACHE_SIZE; +import static de.luhmer.owncloudnewsreader.SettingsActivity.SP_SEARCH_IN; +import static de.luhmer.owncloudnewsreader.SettingsActivity.SP_SORT_ORDER; +import static de.luhmer.owncloudnewsreader.SettingsActivity.SP_SWIPE_LEFT_ACTION; +import static de.luhmer.owncloudnewsreader.SettingsActivity.SP_SWIPE_RIGHT_ACTION; +import static de.luhmer.owncloudnewsreader.SettingsActivity.SYNC_INTERVAL_IN_MINUTES_STRING_DEPRECATED; + import android.accounts.Account; import android.accounts.AccountManager; -import android.app.Activity; import android.app.DialogFragment; import android.app.ProgressDialog; import android.content.ContentResolver; @@ -41,34 +67,6 @@ import de.luhmer.owncloudnewsreader.helper.NewsFileUtils; import de.luhmer.owncloudnewsreader.helper.PostDelayHandler; -import static android.app.Activity.RESULT_OK; -import static de.luhmer.owncloudnewsreader.Constants.USER_INFO_STRING; -import static de.luhmer.owncloudnewsreader.SettingsActivity.CB_MARK_AS_READ_WHILE_SCROLLING_STRING; -import static de.luhmer.owncloudnewsreader.SettingsActivity.CB_NAVIGATE_WITH_VOLUME_BUTTONS_STRING; -import static de.luhmer.owncloudnewsreader.SettingsActivity.CB_OLED_MODE; -import static de.luhmer.owncloudnewsreader.SettingsActivity.CB_REPORT_ISSUE; -import static de.luhmer.owncloudnewsreader.SettingsActivity.CB_SHOWONLYUNREAD_STRING; -import static de.luhmer.owncloudnewsreader.SettingsActivity.CB_SHOW_FAST_ACTIONS; -import static de.luhmer.owncloudnewsreader.SettingsActivity.CB_SKIP_DETAILVIEW_AND_OPEN_BROWSER_DIRECTLY_STRING; -import static de.luhmer.owncloudnewsreader.SettingsActivity.CB_SYNCONSTARTUP_STRING; -import static de.luhmer.owncloudnewsreader.SettingsActivity.CB_VERSION; -import static de.luhmer.owncloudnewsreader.SettingsActivity.EDT_CLEAR_CACHE; -import static de.luhmer.owncloudnewsreader.SettingsActivity.EDT_OWNCLOUDROOTPATH_STRING; -import static de.luhmer.owncloudnewsreader.SettingsActivity.EDT_PASSWORD_STRING; -import static de.luhmer.owncloudnewsreader.SettingsActivity.EDT_USERNAME_STRING; -import static de.luhmer.owncloudnewsreader.SettingsActivity.LV_CACHE_IMAGES_OFFLINE_STRING; -import static de.luhmer.owncloudnewsreader.SettingsActivity.PREF_SYNC_SETTINGS; -import static de.luhmer.owncloudnewsreader.SettingsActivity.SP_APP_THEME; -import static de.luhmer.owncloudnewsreader.SettingsActivity.SP_DISPLAY_BROWSER; -import static de.luhmer.owncloudnewsreader.SettingsActivity.SP_FEED_LIST_LAYOUT; -import static de.luhmer.owncloudnewsreader.SettingsActivity.SP_FONT_SIZE; -import static de.luhmer.owncloudnewsreader.SettingsActivity.SP_MAX_CACHE_SIZE; -import static de.luhmer.owncloudnewsreader.SettingsActivity.SP_SEARCH_IN; -import static de.luhmer.owncloudnewsreader.SettingsActivity.SP_SORT_ORDER; -import static de.luhmer.owncloudnewsreader.SettingsActivity.SP_SWIPE_LEFT_ACTION; -import static de.luhmer.owncloudnewsreader.SettingsActivity.SP_SWIPE_RIGHT_ACTION; -import static de.luhmer.owncloudnewsreader.SettingsActivity.SYNC_INTERVAL_IN_MINUTES_STRING_DEPRECATED; - public class SettingsFragment extends PreferenceFragmentCompat { protected @Inject SharedPreferences mPrefs; @@ -442,7 +440,7 @@ protected Void doInBackground(Void... params) { DatabaseConnectionOrm dbConn = new DatabaseConnectionOrm(context); dbConn.resetDatabase(); - ImageHandler.clearCache(); + ImageHandler.clearCache(context); NewsFileUtils.clearWebArchiveCache(context); NewsFileUtils.clearPodcastCache(context); return null; diff --git a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/adapter/RssItemHeadlineThumbnailViewHolder.java b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/adapter/RssItemHeadlineThumbnailViewHolder.java index 8d0bc1baa..d5f529573 100644 --- a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/adapter/RssItemHeadlineThumbnailViewHolder.java +++ b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/adapter/RssItemHeadlineThumbnailViewHolder.java @@ -12,33 +12,27 @@ import androidx.annotation.NonNull; import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat; -import com.nostra13.universalimageloader.core.DisplayImageOptions; -import com.nostra13.universalimageloader.core.ImageLoader; +import com.bumptech.glide.load.MultiTransformation; +import com.bumptech.glide.load.engine.DiskCacheStrategy; +import com.bumptech.glide.load.resource.bitmap.CenterCrop; +import com.bumptech.glide.load.resource.bitmap.RoundedCorners; import de.luhmer.owncloudnewsreader.R; import de.luhmer.owncloudnewsreader.database.DatabaseConnectionOrm; import de.luhmer.owncloudnewsreader.database.model.RssItem; import de.luhmer.owncloudnewsreader.databinding.SubscriptionDetailListItemHeadlineThumbnailBinding; -import de.luhmer.owncloudnewsreader.helper.SquareRoundedBitmapDisplayer; public class RssItemHeadlineThumbnailViewHolder extends RssItemViewHolder { - private final DisplayImageOptions displayImageOptionsThumbnail; + + //private final DisplayImageOptions displayImageOptionsThumbnail; + Drawable feedIcon = VectorDrawableCompat.create(itemView.getResources(), R.drawable.feed_icon, null); + RssItemHeadlineThumbnailViewHolder(@NonNull SubscriptionDetailListItemHeadlineThumbnailBinding binding, SharedPreferences sharedPreferences) { super(binding, sharedPreferences); - Drawable feedIcon = VectorDrawableCompat.create(itemView.getResources(), R.drawable.feed_icon, null); - int widthThumbnail = Math.round(88f * binding.imgViewThumbnail.getContext().getResources().getDisplayMetrics().density); - displayImageOptionsThumbnail = new DisplayImageOptions.Builder() - .resetViewBeforeLoading(true) - .preProcessor(new SquareRoundedBitmapDisplayer(30, 0, widthThumbnail)) - .showImageOnLoading(feedIcon) - .showImageForEmptyUri(feedIcon) - .showImageOnFail(feedIcon) - .cacheOnDisk(true) - .cacheInMemory(true) - .build(); + //int widthThumbnail = Math.round(88f * binding.imgViewThumbnail.getContext().getResources().getDisplayMetrics().density); } @Override @@ -102,7 +96,15 @@ public void bind(@NonNull RssItem rssItem) { String mediaThumbnail = rssItem.getMediaThumbnail(); if (mediaThumbnail != null && !mediaThumbnail.isEmpty()) { binding.imgViewThumbnail.setVisibility(View.VISIBLE); - ImageLoader.getInstance().displayImage(mediaThumbnail, binding.imgViewThumbnail, displayImageOptionsThumbnail); + //ImageLoader.getInstance().displayImage(mediaThumbnail, binding.imgViewThumbnail, displayImageOptionsThumbnail); + + mGlide + .load(mediaThumbnail) + .diskCacheStrategy(DiskCacheStrategy.DATA) + .placeholder(feedIcon) + .error(feedIcon) + .transform(new MultiTransformation<>(new CenterCrop(), new RoundedCorners(60))) + .into(binding.imgViewThumbnail); } else { // Show Podcast Icon if no thumbnail is available but it is a podcast (otherwise the podcast button will go missing) if (DatabaseConnectionOrm.ALLOWED_PODCASTS_TYPES.contains(rssItem.getEnclosureMime())) { diff --git a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/adapter/RssItemThumbnailViewHolder.java b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/adapter/RssItemThumbnailViewHolder.java index 0185e977b..e6f83d198 100644 --- a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/adapter/RssItemThumbnailViewHolder.java +++ b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/adapter/RssItemThumbnailViewHolder.java @@ -1,5 +1,7 @@ package de.luhmer.owncloudnewsreader.adapter; +import static android.view.View.GONE; + import android.content.SharedPreferences; import android.graphics.drawable.Drawable; import android.view.View; @@ -12,34 +14,25 @@ import androidx.annotation.NonNull; import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat; -import com.nostra13.universalimageloader.core.DisplayImageOptions; -import com.nostra13.universalimageloader.core.ImageLoader; +import com.bumptech.glide.load.MultiTransformation; +import com.bumptech.glide.load.engine.DiskCacheStrategy; +import com.bumptech.glide.load.resource.bitmap.CenterCrop; +import com.bumptech.glide.load.resource.bitmap.RoundedCorners; import de.luhmer.owncloudnewsreader.R; import de.luhmer.owncloudnewsreader.database.DatabaseConnectionOrm; import de.luhmer.owncloudnewsreader.database.model.RssItem; import de.luhmer.owncloudnewsreader.databinding.SubscriptionDetailListItemThumbnailBinding; -import de.luhmer.owncloudnewsreader.helper.SquareRoundedBitmapDisplayer; - -import static android.view.View.GONE; public class RssItemThumbnailViewHolder extends RssItemViewHolder { - private final DisplayImageOptions displayImageOptionsThumbnail; + //private final DisplayImageOptions displayImageOptionsThumbnail; + + Drawable feedIcon = VectorDrawableCompat.create(itemView.getResources(), R.drawable.feed_icon, null); RssItemThumbnailViewHolder(@NonNull SubscriptionDetailListItemThumbnailBinding binding, SharedPreferences sharedPreferences) { super(binding, sharedPreferences); - Drawable feedIcon = VectorDrawableCompat.create(itemView.getResources(), R.drawable.feed_icon, null); - int width = Math.round(88f * binding.imgViewThumbnail.getContext().getResources().getDisplayMetrics().density); - displayImageOptionsThumbnail = new DisplayImageOptions.Builder() - .resetViewBeforeLoading(true) - .preProcessor(new SquareRoundedBitmapDisplayer(30, 0, width)) - .showImageOnLoading(feedIcon) - .showImageForEmptyUri(feedIcon) - .showImageOnFail(feedIcon) - .cacheOnDisk(true) - .cacheInMemory(true) - .build(); + //int width = Math.round(88f * binding.imgViewThumbnail.getContext().getResources().getDisplayMetrics().density); } @Override @@ -100,7 +93,17 @@ public void bind(@NonNull RssItem rssItem) { String mediaThumbnail = rssItem.getMediaThumbnail(); if (mediaThumbnail != null && !mediaThumbnail.isEmpty()) { binding.imgViewThumbnail.setVisibility(View.VISIBLE); - ImageLoader.getInstance().displayImage(mediaThumbnail, binding.imgViewThumbnail, displayImageOptionsThumbnail); + //ImageLoader.getInstance().displayImage(mediaThumbnail, binding.imgViewThumbnail, displayImageOptionsThumbnail); + // ImageLoader.getInstance().displayImage(mediaThumbnail, binding.imgViewThumbnail, displayImageOptionsThumbnail); + + mGlide + .load(mediaThumbnail) + .diskCacheStrategy(DiskCacheStrategy.DATA) + .placeholder(feedIcon) + .error(feedIcon) + .transform(new MultiTransformation<>(new CenterCrop(), new RoundedCorners(60))) + .into(binding.imgViewThumbnail); + } else { // Show Podcast Icon if no thumbnail is available but it is a podcast (otherwise the podcast button will go missing) if (DatabaseConnectionOrm.ALLOWED_PODCASTS_TYPES.contains(rssItem.getEnclosureMime())) { diff --git a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/adapter/RssItemViewHolder.java b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/adapter/RssItemViewHolder.java index f6bac3584..e24d746c2 100644 --- a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/adapter/RssItemViewHolder.java +++ b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/adapter/RssItemViewHolder.java @@ -1,5 +1,6 @@ package de.luhmer.owncloudnewsreader.adapter; +import android.content.Context; import android.content.SharedPreferences; import android.content.res.TypedArray; import android.graphics.Color; @@ -23,6 +24,8 @@ import androidx.recyclerview.widget.RecyclerView; import androidx.viewbinding.ViewBinding; +import com.bumptech.glide.RequestManager; + import org.greenrobot.eventbus.Subscribe; import java.util.regex.Pattern; @@ -33,6 +36,7 @@ import de.luhmer.owncloudnewsreader.helper.ColorHelper; import de.luhmer.owncloudnewsreader.helper.DateTimeFormatter; import de.luhmer.owncloudnewsreader.helper.FavIconHandler; +import de.luhmer.owncloudnewsreader.helper.GlideApp; import de.luhmer.owncloudnewsreader.services.PodcastDownloadService; public abstract class RssItemViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener { @@ -51,6 +55,7 @@ public abstract class RssItemViewHolder extends RecyclerV private boolean playing; private int starColor; private int inactiveStarColor; + protected RequestManager mGlide; private final SparseIntArray initalFontSizes = new SparseIntArray(); @@ -59,10 +64,13 @@ public abstract class RssItemViewHolder extends RecyclerV this.binding = (T) binding; this.mPrefs = sharedPreferences; - bodyForegroundColor = new ForegroundColorSpan(ContextCompat.getColor(itemView.getContext(), android.R.color.secondary_text_dark)); + Context context = itemView.getContext(); + bodyForegroundColor = new ForegroundColorSpan(ContextCompat.getColor(context, android.R.color.secondary_text_dark)); + + mGlide = GlideApp.with(context); if (favIconHandler == null) { - favIconHandler = new FavIconHandler(itemView.getContext()); + favIconHandler = new FavIconHandler(context); } itemView.setOnClickListener(this); diff --git a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/adapter/RssItemWebViewHolder.java b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/adapter/RssItemWebViewHolder.java index 6e792f0b4..ace937b7e 100644 --- a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/adapter/RssItemWebViewHolder.java +++ b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/adapter/RssItemWebViewHolder.java @@ -75,7 +75,7 @@ protected ProgressBar getPodcastDownloadProgress() { public void bind(@NonNull RssItem rssItem) { super.bind(rssItem); - String htmlPage = RssItemToHtmlTask.getHtmlPage(rssItem, false, mPrefs, itemView.getContext()); + String htmlPage = RssItemToHtmlTask.getHtmlPage(this.mGlide, rssItem, false, mPrefs, itemView.getContext()); binding.webViewBody.loadDataWithBaseURL("file:///android_asset/", htmlPage, "text/html", "UTF-8", ""); } diff --git a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/async_tasks/DownloadImageHandler.java b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/async_tasks/DownloadImageHandler.java index 653ed5ec9..86df4f069 100644 --- a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/async_tasks/DownloadImageHandler.java +++ b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/async_tasks/DownloadImageHandler.java @@ -23,67 +23,43 @@ import android.graphics.Bitmap; import android.util.Log; -import android.view.View; -import com.nostra13.universalimageloader.core.DisplayImageOptions; -import com.nostra13.universalimageloader.core.ImageLoader; -import com.nostra13.universalimageloader.core.assist.FailReason; -import com.nostra13.universalimageloader.core.listener.ImageLoadingListener; +import com.bumptech.glide.RequestManager; +import com.bumptech.glide.load.engine.DiskCacheStrategy; import java.net.URL; +import java.util.concurrent.ExecutionException; import de.luhmer.owncloudnewsreader.helper.ImageDownloadFinished; -public class DownloadImageHandler implements ImageLoadingListener -{ - private static final String TAG = "GetImageAsyncTask"; +public class DownloadImageHandler { + private static final String TAG = DownloadImageHandler.class.getCanonicalName(); private URL mImageUrl; private ImageDownloadFinished imageDownloadFinished; - private static final DisplayImageOptions displayImageOptions = new DisplayImageOptions.Builder() - .cacheOnDisk(true) - .build(); - public DownloadImageHandler(String imageUrl) { try { this.mImageUrl = new URL(imageUrl); } catch(Exception ex) { Log.d(TAG, "Invalid URL: " + imageUrl, ex); } - - - } - - public void downloadSync() { - ImageLoader.getInstance().loadImageSync(mImageUrl.toString(), displayImageOptions); - } - - public void downloadAsync(ImageDownloadFinished imgDownloadFinished) { - this.imageDownloadFinished = imgDownloadFinished; - ImageLoader.getInstance().loadImage(mImageUrl.toString(), displayImageOptions, this); - } - - @Override - public void onLoadingStarted(String imageUri, View view) { - - } - - @Override - public void onLoadingFailed(String imageUri, View view, FailReason failReason) { - NotifyDownloadFinished(null); - Log.d(TAG, "Failed to load file: " + imageUri); } - @Override - public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) { - NotifyDownloadFinished(loadedImage); - } - - @Override - public void onLoadingCancelled(String imageUri, View view) { + public void preloadSync(RequestManager glide) { + try { + Bitmap bm = glide + .asBitmap() + .load(mImageUrl.toString()) + .diskCacheStrategy(DiskCacheStrategy.DATA) + .submit() + .get(); + NotifyDownloadFinished(bm); + } catch (ExecutionException | InterruptedException e) { + e.printStackTrace(); + } NotifyDownloadFinished(null); - Log.d(TAG, "Cancelled: " + imageUri); + //ImageLoader.getInstance().loadImageSync(mImageUrl.toString(), displayImageOptions); } private void NotifyDownloadFinished(Bitmap bitmap) { diff --git a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/async_tasks/RssItemToHtmlTask.java b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/async_tasks/RssItemToHtmlTask.java index 77780a85c..03d15120e 100644 --- a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/async_tasks/RssItemToHtmlTask.java +++ b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/async_tasks/RssItemToHtmlTask.java @@ -10,8 +10,14 @@ import android.text.format.DateUtils; import android.util.Log; -import com.nostra13.universalimageloader.cache.disc.DiskCache; -import com.nostra13.universalimageloader.core.ImageLoader; +import androidx.annotation.Nullable; + +import com.bumptech.glide.RequestManager; +import com.bumptech.glide.load.DataSource; +import com.bumptech.glide.load.engine.DiskCacheStrategy; +import com.bumptech.glide.load.engine.GlideException; +import com.bumptech.glide.request.RequestListener; +import com.bumptech.glide.request.target.Target; import java.io.File; import java.text.DecimalFormat; @@ -24,6 +30,7 @@ import de.luhmer.owncloudnewsreader.SettingsActivity; import de.luhmer.owncloudnewsreader.database.model.Feed; import de.luhmer.owncloudnewsreader.database.model.RssItem; +import de.luhmer.owncloudnewsreader.helper.GlideApp; import de.luhmer.owncloudnewsreader.helper.ImageHandler; import de.luhmer.owncloudnewsreader.helper.ThemeChooser; @@ -45,7 +52,7 @@ public class RssItemToHtmlTask extends AsyncTask { private final Listener mListener; private final SharedPreferences mPrefs; private final boolean isRightToLeft; - + private final RequestManager mGlide; public interface Listener { /** @@ -60,13 +67,14 @@ public RssItemToHtmlTask(Context context, RssItem rssItem, Listener listener, Sh this.mRssItem = rssItem; this.mListener = listener; this.mPrefs = prefs; + this.mGlide = GlideApp.with(context); this.isRightToLeft = context.getResources().getBoolean(R.bool.is_right_to_left); } @Override protected String doInBackground(Void... params) { - return getHtmlPage(mRssItem, true, mPrefs, isRightToLeft); + return getHtmlPage(this.mGlide, mRssItem, true, mPrefs, isRightToLeft); } @Override @@ -75,8 +83,8 @@ protected void onPostExecute(String htmlPage) { super.onPostExecute(htmlPage); } - public static String getHtmlPage(RssItem rssItem, boolean showHeader, SharedPreferences mPrefs, Context context) { - return getHtmlPage(rssItem, showHeader, mPrefs, context.getResources().getBoolean(R.bool.is_right_to_left)); + public static String getHtmlPage(RequestManager glide, RssItem rssItem, boolean showHeader, SharedPreferences mPrefs, Context context) { + return getHtmlPage(glide, rssItem, showHeader, mPrefs, context.getResources().getBoolean(R.bool.is_right_to_left)); } /** @@ -84,7 +92,7 @@ public static String getHtmlPage(RssItem rssItem, boolean showHeader, SharedPref * @param showHeader true if a header with item title, feed title, etc. should be included * @return given RSS item as full HTML page */ - public static String getHtmlPage(RssItem rssItem, boolean showHeader, SharedPreferences mPrefs, boolean isRightToLeft) { + public static String getHtmlPage(RequestManager glide, RssItem rssItem, boolean showHeader, SharedPreferences mPrefs, boolean isRightToLeft) { boolean incognitoMode = mPrefs.getBoolean(INCOGNITO_MODE_ENABLED, false); String favIconUrl = null; @@ -97,7 +105,7 @@ public static String getHtmlPage(RssItem rssItem, boolean showHeader, SharedPref } if (favIconUrl != null) { - favIconUrl = getCachedFavIcon(favIconUrl); + favIconUrl = getCachedFavIcon(glide, favIconUrl); } else { favIconUrl = "file:///android_res/drawable/default_feed_icon_light.png"; } @@ -133,7 +141,7 @@ public static String getHtmlPage(RssItem rssItem, boolean showHeader, SharedPref if(!incognitoMode) { // If incognito mode is disabled, try getting images from cache - description = getDescriptionWithCachedImages(rssItem.getLink(), description).trim(); + description = getDescriptionWithCachedImages(glide, rssItem.getLink(), description).trim(); } else { // When incognito is on, we need to provide some error handling //description = description.replaceAll(" links = ImageHandler.getImageLinksFromText(articleUrl, text); - DiskCache diskCache = ImageLoader.getInstance().getDiskCache(); for(String link : links) { link = link.trim(); try { - File file = diskCache.get(link); + File file = null; + try { + file = glide + .asFile() + .diskCacheStrategy(DiskCacheStrategy.DATA) + .onlyRetrieveFromCache(true) + // .listener(rl) + .load(link) + .submit() + .get(); + Log.d(TAG, "image is cached"); + } catch (Exception e) { + Log.w(TAG, "image is not cached"); + } if(file != null) { text = text.replace(link, "file://" + file.getAbsolutePath()); } @@ -255,6 +285,30 @@ private static String getDescriptionWithCachedImages(String articleUrl, String t return text; } + private static RequestListener rl = new RequestListener<>() { + @Override + public boolean onLoadFailed(@Nullable GlideException e, Object model, Target target, boolean isFirstResource) { + // Log the GlideException here (locally or with a remote logging framework): + Log.e(TAG, "Load failed", e); + + // You can also log the individual causes: + for (Throwable t : e.getRootCauses()) { + Log.e(TAG, "Caused by", t); + } + // Or, to log all root causes locally, you can use the built in helper method: + e.logRootCauses(TAG); + + return false; // Allow calling onLoadFailed on the Target. + } + + @Override + public boolean onResourceReady(File resource, Object model, Target target, DataSource dataSource, boolean isFirstResource) { + // Log successes here or use DataSource to keep track of cache hits and misses. + return false; // Allow calling onResourceReady on the Target. + } + + }; + private static String replacePatternInText(Pattern pattern, String text, String replacement) { Matcher m = pattern.matcher(text); return m.replaceAll(replacement); diff --git a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/di/ApiProvider.java b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/di/ApiProvider.java index 38380607f..dd72a9a85 100644 --- a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/di/ApiProvider.java +++ b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/di/ApiProvider.java @@ -11,14 +11,9 @@ import com.nextcloud.android.sso.exceptions.SSOException; import com.nextcloud.android.sso.helper.SingleAccountHelper; import com.nextcloud.android.sso.model.SingleSignOnAccount; -import com.nostra13.universalimageloader.cache.disc.naming.Md5FileNameGenerator; -import com.nostra13.universalimageloader.core.DisplayImageOptions; -import com.nostra13.universalimageloader.core.ImageLoader; -import com.nostra13.universalimageloader.core.ImageLoaderConfiguration; import de.luhmer.owncloudnewsreader.SettingsActivity; import de.luhmer.owncloudnewsreader.helper.GsonConfig; -import de.luhmer.owncloudnewsreader.reader.OkHttpImageDownloader; import de.luhmer.owncloudnewsreader.reader.nextcloud.NewsAPI; import de.luhmer.owncloudnewsreader.reader.nextcloud.OcsAPI; import de.luhmer.owncloudnewsreader.ssl.MemorizingTrustManager; @@ -69,8 +64,8 @@ public void initApi(@NonNull NextcloudAPI.ApiConnectedListener apiConnectedListe boolean useSSO = mPrefs.getBoolean(SettingsActivity.SW_USE_SINGLE_SIGN_ON, false); if(useSSO) { - OkHttpClient client = new OkHttpClient.Builder().build(); - initImageLoader(mPrefs, client, context); + // OkHttpClient client = new OkHttpClient.Builder().build(); + //initImageLoader(mPrefs, client, context); initSsoApi(apiConnectedListener); } else { if(mPrefs.contains(SettingsActivity.EDT_OWNCLOUDROOTPATH_STRING)) { @@ -82,7 +77,7 @@ public void initApi(@NonNull NextcloudAPI.ApiConnectedListener apiConnectedListe .build(); Log.d("ApiModule", "HttpUrl: " + baseUrl.toString()); OkHttpClient client = OkHttpSSLClient.GetSslClient(baseUrl, username, password, mPrefs, mMemorizingTrustManager); - initImageLoader(mPrefs, client, context); + // initImageLoader(mPrefs, client, context); initRetrofitApi(baseUrl, client); apiConnectedListener.onConnected(); } else { @@ -114,30 +109,6 @@ protected void initSsoApi(final NextcloudAPI.ApiConnectedListener callback) { } } - - - private void initImageLoader(SharedPreferences mPrefs, OkHttpClient okHttpClient, Context context) { - String cacheSize = mPrefs.getString(SettingsActivity.SP_MAX_CACHE_SIZE,"500"); - int diskCacheSize = Integer.parseInt(cacheSize)*1024*1024; - if(ImageLoader.getInstance().isInited()) { - ImageLoader.getInstance().destroy(); - } - DisplayImageOptions imageOptions = new DisplayImageOptions.Builder() - .cacheOnDisk(true) - .cacheInMemory(true) - .build(); - - ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(context) - .diskCacheSize(diskCacheSize) - .memoryCacheSize(10 * 1024 * 1024) - .diskCacheFileNameGenerator(new Md5FileNameGenerator()) - .defaultDisplayImageOptions(imageOptions) - .imageDownloader(new OkHttpImageDownloader(context, okHttpClient)) - .build(); - - ImageLoader.getInstance().init(config); - } - public NewsAPI getNewsAPI() { return mNewsApi; } diff --git a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/di/AppComponent.java b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/di/AppComponent.java index a13dbf39a..82d7f32a1 100644 --- a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/di/AppComponent.java +++ b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/di/AppComponent.java @@ -18,6 +18,7 @@ import de.luhmer.owncloudnewsreader.SettingsFragment; import de.luhmer.owncloudnewsreader.authentication.OwnCloudSyncAdapter; import de.luhmer.owncloudnewsreader.database.DatabaseConnectionOrm; +import de.luhmer.owncloudnewsreader.helper.NextcloudGlideModule; import de.luhmer.owncloudnewsreader.services.SyncItemStateService; import de.luhmer.owncloudnewsreader.widget.WidgetProvider; @@ -49,5 +50,7 @@ public interface AppComponent { void injectWidget(WidgetProvider widgetProvider); + void injectGlideModule(NextcloudGlideModule glideModule); + void injectDatabaseConnection(DatabaseConnectionOrm databaseConnectionOrm); } diff --git a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/helper/FavIconHandler.java b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/helper/FavIconHandler.java index 4613a77c2..293ad185c 100644 --- a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/helper/FavIconHandler.java +++ b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/helper/FavIconHandler.java @@ -23,18 +23,22 @@ import android.content.Context; import android.graphics.Bitmap; +import android.graphics.drawable.Drawable; import android.util.Log; -import android.view.Display; -import android.view.View; import android.widget.ImageView; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.core.content.ContextCompat; import androidx.palette.graphics.Palette; -import com.nostra13.universalimageloader.core.DisplayImageOptions; -import com.nostra13.universalimageloader.core.ImageLoader; -import com.nostra13.universalimageloader.core.assist.FailReason; -import com.nostra13.universalimageloader.core.listener.ImageLoadingListener; +import com.bumptech.glide.RequestManager; +import com.bumptech.glide.load.engine.DiskCacheStrategy; +import com.bumptech.glide.load.resource.bitmap.RoundedCorners; +import com.bumptech.glide.request.RequestOptions; +import com.bumptech.glide.request.target.SimpleTarget; +import com.bumptech.glide.request.target.Target; +import com.bumptech.glide.request.transition.Transition; import de.luhmer.owncloudnewsreader.R; import de.luhmer.owncloudnewsreader.database.DatabaseConnectionOrm; @@ -42,28 +46,40 @@ public class FavIconHandler { private static final String TAG = FavIconHandler.class.getCanonicalName(); - private final DisplayImageOptions displayImageOptions; + + private RequestManager mGlide; + private Context mContext; + private int mPlaceHolder; public FavIconHandler(Context context) { - int placeHolder = FavIconHandler.getResourceIdForRightDefaultFeedIcon(); - - int widthFavIcon = Math.round(20f * context.getResources().getDisplayMetrics().density); - displayImageOptions = new DisplayImageOptions.Builder() - .preProcessor(new SquareRoundedBitmapDisplayer(6, 0, widthFavIcon)) - .showImageOnLoading(placeHolder) - .showImageForEmptyUri(placeHolder) - .showImageOnFail(placeHolder) - .cacheOnDisk(true) - .cacheInMemory(true) - .build(); + mPlaceHolder = FavIconHandler.getResourceIdForRightDefaultFeedIcon(); + //int widthFavIcon = Math.round(20f * context.getResources().getDisplayMetrics().density); + mContext = context; + mGlide = GlideApp.with(context); } - public void loadFavIconForFeed(String favIconUrl, ImageView imgView) { - ImageLoader.getInstance().displayImage(favIconUrl, imgView, displayImageOptions); - } + public void loadFavIconForFeed(@Nullable String favIconUrl, ImageView imgView) { + RequestOptions requestOptions = new RequestOptions(); + requestOptions = requestOptions.transforms(new RoundedCorners(6)); + + if(favIconUrl == null) { + mGlide + .load(mPlaceHolder) + .apply(requestOptions) + .into(imgView); + } else { + mGlide + .load(favIconUrl) + .diskCacheStrategy(DiskCacheStrategy.DATA) + .placeholder(mPlaceHolder) + .error(mPlaceHolder) + .apply(requestOptions) + .onlyRetrieveFromCache(true) // disable loading of favicons from network (usually those favicons are broken) + .into(imgView); + //ImageLoader.getInstance().displayImage(favIconUrl, imgView, displayImageOptions); + } + - public void loadFavIconForFeed(String favIconUrl, ImageView imgView, DisplayImageOptions displayImageOptions) { - ImageLoader.getInstance().displayImage(favIconUrl, imgView, displayImageOptions); } /** @@ -88,37 +104,35 @@ public static int getResourceIdForRightDefaultFeedIcon() { } - public void preCacheFavIcon(final Feed feed, Context context) throws IllegalStateException { + public void preCacheFavIcon(final Feed feed) throws IllegalStateException { if (feed.getFaviconUrl() == null) { Log.v(TAG, "No favicon for " + feed.getFeedTitle()); return; } - Log.v(TAG, "Loading image: " + feed.getFaviconUrl()); - ImageLoader.getInstance().loadImage(feed.getFaviconUrl(), displayImageOptions, new ImageLoadingListener() { + String favIconUrl = feed.getFaviconUrl(); + Log.v(TAG, "Loading image: " + favIconUrl); + mGlide + .asBitmap() + .load(favIconUrl) + .diskCacheStrategy(DiskCacheStrategy.DATA) + .apply(RequestOptions.overrideOf(Target.SIZE_ORIGINAL)) + .into(new SimpleTarget() { @Override - public void onLoadingStarted(String imageUri, View view) { - + public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition transition) { + UpdateAvgColorOfFeed(feed.getId(), resource, mContext); + Log.d(TAG, "Successfully downloaded image for url: " + favIconUrl); } @Override - public void onLoadingFailed(String imageUri, View view, FailReason failReason) { - - } - - @Override - public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) { - DownloadFinished(feed.getId(), loadedImage, context); - } - - @Override - public void onLoadingCancelled(String imageUri, View view) { - + public void onLoadFailed(@Nullable Drawable errorDrawable) { + super.onLoadFailed(errorDrawable); + Log.d(TAG, "Failed to download image for url: " + favIconUrl); } }); } - private void DownloadFinished(long feedId, Bitmap bitmap, Context context) { + private void UpdateAvgColorOfFeed(long feedId, Bitmap bitmap, Context context) { if (bitmap != null) { DatabaseConnectionOrm dbConn = new DatabaseConnectionOrm(context); Feed feed = dbConn.getFeedById(feedId); @@ -129,7 +143,7 @@ private void DownloadFinished(long feedId, Bitmap bitmap, Context context) { feed.setAvgColour(avg); dbConn.updateFeed(feed); - Log.v(TAG, "Updating AVG color of feed: " + feed.getFeedTitle() + " - Color: " + avg); + // Log.v(TAG, "Updating AVG color of feed: " + feed.getFeedTitle() + " - Color: " + avg); } else { Log.v(TAG, "Failed to update AVG color of feed: " + feedId); } diff --git a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/helper/FavIconUtils.java b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/helper/FavIconUtils.java new file mode 100644 index 000000000..d884c1c6e --- /dev/null +++ b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/helper/FavIconUtils.java @@ -0,0 +1,67 @@ +package de.luhmer.owncloudnewsreader.helper; + +import android.util.Log; + +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; + +public class FavIconUtils { + + private static String TAG = FavIconUtils.class.getCanonicalName(); + + public static String fixFavIconUrl(String favIconUrl) { + if(favIconUrl == null) { + return null; + } + + try { + favIconUrl = decodeSpecialChars(favIconUrl); + }catch(Exception ex) { + Log.e(TAG, ex.toString()); + } + return fixSvgIcons(favIconUrl); + } + + protected static String decodeSpecialChars(String favIconUrl) throws UnsupportedEncodingException { + + String before = favIconUrl; + int idx = favIconUrl.indexOf("?"); + String path = favIconUrl; + if(idx > 0) { + path = favIconUrl.substring(0, idx); + } + favIconUrl = favIconUrl.replace(path, URLDecoder.decode(path, StandardCharsets.UTF_8.name())); + + /* + URL url = Objects.requireNonNull(HttpUrl.parse(favIconUrl)).url(); + + String pathDecoded = URLDecoder.decode(url.getPath(), StandardCharsets.UTF_8.name()); + String portPostfix = (url.getPort() != -1) ? String.valueOf(url.getPort()) : ""; + + // some urls use specials chars which for some reason cause issues in Glide + // e.g. + // https://i2.wp.com/stadt-bremerhaven.de/wp-content/uploads/2014/12/logo-gro%C3%9F-549c81bbv1_site_icon.png?fit=32%2C32 + // https://i2.wp.com//stadt-bremerhaven.de/wp-content/uploads/2014/12/logo-groß-549c81bbv1_site_icon.png?fit=32%2C32 + + favIconUrl = String.format("%s://%s%s/%s", url.getProtocol(), url.getHost(), portPostfix, pathDecoded); + + + if(url.getQuery() != null) { + favIconUrl = favIconUrl + "?" + url.getQuery(); + } + + */ + //Log.d(TAG, "before: " + before); + //Log.d(TAG, "after: " + favIconUrl); + + return favIconUrl; + } + + protected static String fixSvgIcons(String favIconUrl) { + if(favIconUrl.endsWith(".svg")) { + favIconUrl = String.format("https://images.weserv.nl?url=%s&output=webp", favIconUrl); + } + return favIconUrl; + } +} diff --git a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/helper/ImageHandler.java b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/helper/ImageHandler.java index 92de80ff8..cba5c96a7 100644 --- a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/helper/ImageHandler.java +++ b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/helper/ImageHandler.java @@ -21,13 +21,10 @@ package de.luhmer.owncloudnewsreader.helper; +import android.content.Context; import android.util.Log; -import com.nostra13.universalimageloader.core.ImageLoader; - import java.net.MalformedURLException; -import java.net.URI; -import java.net.URISyntaxException; import java.net.URL; import java.util.ArrayList; import java.util.List; @@ -202,11 +199,11 @@ private static String getFileName(String url) { } } - public static void clearCache() + public static void clearCache(Context context) { - if(ImageLoader.getInstance().isInited()) { - ImageLoader.getInstance().clearDiskCache(); - ImageLoader.getInstance().clearMemoryCache(); - } + new Thread(() -> { + GlideApp.get(context).clearMemory(); + GlideApp.get(context).clearDiskCache(); + }).start(); } } diff --git a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/helper/NewsFileUtils.java b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/helper/NewsFileUtils.java index 62dc60bf0..0c13954e4 100644 --- a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/helper/NewsFileUtils.java +++ b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/helper/NewsFileUtils.java @@ -25,8 +25,6 @@ import android.os.Environment; import android.util.Log; -import com.nostra13.universalimageloader.utils.StorageUtils; - import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; @@ -115,10 +113,10 @@ public static void clearWebArchiveCache(Context context) { } public static String getCacheDirPath(Context context) { - return StorageUtils.getCacheDirectory(context).getPath(); + //return context.getCacheDir().getAbsolutePath(); + return context.getExternalCacheDir().getAbsolutePath(); } - public static String getPathPodcasts(Context context) { return context.getExternalFilesDir(Environment.DIRECTORY_MUSIC).getAbsolutePath()+ "/podcasts"; } @@ -133,11 +131,6 @@ public static boolean isExternalStorageWritable() { } - - - - - /* Method below are copied from https://github.com/apache/commons-io/blob/master/src/main/java/org/apache/commons/io/FileUtils.java */ diff --git a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/helper/NextcloudGlideModule.java b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/helper/NextcloudGlideModule.java new file mode 100644 index 000000000..4c585baaa --- /dev/null +++ b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/helper/NextcloudGlideModule.java @@ -0,0 +1,47 @@ +package de.luhmer.owncloudnewsreader.helper; + +import android.content.Context; +import android.content.SharedPreferences; +import android.graphics.drawable.Drawable; + +import com.bumptech.glide.GlideBuilder; +import com.bumptech.glide.annotation.GlideModule; +import com.bumptech.glide.load.engine.cache.InternalCacheDiskCacheFactory; +import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions; +import com.bumptech.glide.module.AppGlideModule; +import com.github.technoir42.glide.debug.indicator.DebugIndicatorTransitionFactory; + +import javax.inject.Inject; + +import de.luhmer.owncloudnewsreader.NewsReaderApplication; +import de.luhmer.owncloudnewsreader.SettingsActivity; +import de.luhmer.owncloudnewsreader.di.ApiProvider; + +@GlideModule +public class NextcloudGlideModule extends AppGlideModule { + private String TAG = NextcloudGlideModule.class.getCanonicalName(); + + protected @Inject ApiProvider mApi; + protected @Inject SharedPreferences mPrefs; + + @Override + public void applyOptions(Context context, GlideBuilder builder) { + // #00ff00 Memory Cache (Green) + // #0066ff Disk Cache (Blue) + // #ff0000 Remote (Red) + // #ffff00 Local (Yellow) + builder.setDefaultTransitionOptions(Drawable.class, DrawableTransitionOptions.with(DebugIndicatorTransitionFactory.DEFAULT)); + + ((NewsReaderApplication) context.getApplicationContext()).getAppComponent().injectGlideModule(this); + + String cacheSize = mPrefs.getString(SettingsActivity.SP_MAX_CACHE_SIZE,"500"); + int diskCacheSizeBytes = Integer.parseInt(cacheSize)*1024*1024; + + // Glide uses DiskLruCacheWrapper as the default DiskCache. DiskLruCacheWrapper is a fixed + // size disk cache with LRU eviction. The default disk cache size is 250 MB and is placed + // in a specific directory in the Application’s cache folder. + + builder.setDiskCache(new InternalCacheDiskCacheFactory(context, diskCacheSizeBytes)); + // builder.setDiskCache(new ExternalCacheDiskCacheFactory(context)); + } +} \ No newline at end of file diff --git a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/helper/SquareRoundedBitmapDisplayer.java b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/helper/SquareRoundedBitmapDisplayer.java deleted file mode 100644 index 62d2f80e6..000000000 --- a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/helper/SquareRoundedBitmapDisplayer.java +++ /dev/null @@ -1,72 +0,0 @@ -package de.luhmer.owncloudnewsreader.helper; - -import android.graphics.Bitmap; -import android.graphics.BitmapShader; -import android.graphics.Canvas; -import android.graphics.Matrix; -import android.graphics.Paint; -import android.graphics.RectF; -import android.graphics.Shader; -import android.util.Log; - -import com.nostra13.universalimageloader.core.process.BitmapProcessor; - -public class SquareRoundedBitmapDisplayer implements BitmapProcessor { - - private final static String TAG = SquareRoundedBitmapDisplayer.class.getCanonicalName(); - protected final int cornerRadius; - protected final int margin; - protected final Integer width; - - - public SquareRoundedBitmapDisplayer(int cornerRadiusPixels) { - this(cornerRadiusPixels, 0); - } - - public SquareRoundedBitmapDisplayer(int cornerRadiusPixels, int marginPixels) { - this(cornerRadiusPixels, marginPixels, null); - } - - public SquareRoundedBitmapDisplayer(int cornerRadiusPixels, int marginPixels, Integer width) { - this.cornerRadius = cornerRadiusPixels; - this.margin = marginPixels; - this.width = width; - } - - - @Override - public Bitmap process(Bitmap bitmap) { - Paint maskPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - Paint borderPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - - RectF dst; - borderPaint.setStyle(Paint.Style.STROKE); - borderPaint.setStrokeWidth(16); - borderPaint.setColor(0xcc220088); - - // float scaleFactor = (float) width / bitmap.getWidth(); - int side = Math.min(bitmap.getWidth(), bitmap.getHeight()); - //noinspection SuspiciousNameCombination - int height = width; - - Log.d(TAG, "scale bitmap " + bitmap.getWidth() + "x" + bitmap.getHeight() + " -> " + width + "x" + height); - - Matrix matrix = new Matrix(); - RectF src = new RectF(0, 0, side, side); - src.offset((bitmap.getWidth() - side) / 2f, (bitmap.getHeight() - side) / 2f); - dst = new RectF(0, 0, width, height); - dst.inset(borderPaint.getStrokeWidth() / 4f, borderPaint.getStrokeWidth() / 4f); // Adjust the factor here to fit into the bounds - matrix.setRectToRect(src, dst, Matrix.ScaleToFit.CENTER); - - Shader shader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); - shader.setLocalMatrix(matrix); - maskPaint.setShader(shader); - matrix.mapRect(src); - - Bitmap dstBmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); - Canvas canvas = new Canvas(dstBmp); - canvas.drawRoundRect(dst, cornerRadius, cornerRadius, maskPaint); - bitmap.recycle(); - return dstBmp; - } -} diff --git a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/notification/NextcloudNotificationManager.java b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/notification/NextcloudNotificationManager.java index 68e3b9ba1..ccdc1925a 100644 --- a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/notification/NextcloudNotificationManager.java +++ b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/notification/NextcloudNotificationManager.java @@ -13,6 +13,7 @@ import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; +import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Build; import android.service.notification.StatusBarNotification; @@ -23,13 +24,17 @@ import android.support.v4.media.session.PlaybackStateCompat; import android.text.TextUtils; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.core.app.NotificationCompat; import androidx.core.content.FileProvider; import androidx.media.app.NotificationCompat.MediaStyle; import androidx.media.session.MediaButtonReceiver; -import com.nostra13.universalimageloader.core.ImageLoader; -import com.nostra13.universalimageloader.core.assist.ImageSize; +import com.bumptech.glide.Glide; +import com.bumptech.glide.load.engine.DiskCacheStrategy; +import com.bumptech.glide.request.target.CustomTarget; +import com.bumptech.glide.request.transition.Transition; import java.io.File; import java.util.ArrayList; @@ -54,30 +59,36 @@ public static void showNotificationDownloadSingleImageComplete(Context context, String channelDownloadImage = context.getString(R.string.action_img_download); NotificationManager notificationManager = getNotificationManagerAndCreateChannel(context, channelDownloadImage); - // Load image, decode it to Bitmap and return Bitmap to callback - ImageSize targetSize = new ImageSize(1024,512); // result Bitmap will be fit to this size - Bitmap bitmap = ImageLoader.getInstance().loadImageSync("file://" + imagePath.getAbsolutePath(), targetSize); - - // Uri imageUri = Uri.parse(imagePath); - Uri imageUri = FileProvider.getUriForFile(context, - BuildConfig.APPLICATION_ID + ".provider", - imagePath); - - Intent intent = new Intent(); - intent.setAction(Intent.ACTION_VIEW); - intent.setDataAndType(imageUri, "image/*"); - intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE); - + //Bitmap bitmap = ImageLoader.getInstance().loadImageSync("file://" + imagePath.getAbsolutePath(), targetSize); + Glide.with(context).asBitmap().load("file://" + imagePath.getAbsolutePath()).diskCacheStrategy(DiskCacheStrategy.NONE).into(new CustomTarget(1024, 512) { + @Override + public void onResourceReady(@NonNull Bitmap bitmap, @Nullable Transition transition) { + // Uri imageUri = Uri.parse(imagePath); + Uri imageUri = FileProvider.getUriForFile(context, + BuildConfig.APPLICATION_ID + ".provider", + imagePath); + + Intent intent = new Intent(); + intent.setAction(Intent.ACTION_VIEW); + intent.setDataAndType(imageUri, "image/*"); + intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE); + + NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(context, channelDownloadImage) + .setSmallIcon(R.drawable.ic_notification) + .setContentTitle(context.getString(R.string.toast_img_saved) + " - " + imagePath.getName()) + .setContentIntent(pendingIntent) + .setAutoCancel(true) + .setStyle(new NotificationCompat.BigPictureStyle().bigPicture(bitmap)); + + notificationManager.notify(ID_DownloadSingleImageComplete, mBuilder.build()); + } - NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(context, channelDownloadImage) - .setSmallIcon(R.drawable.ic_notification) - .setContentTitle(context.getString(R.string.toast_img_saved) + " - " + imagePath.getName()) - .setContentIntent(pendingIntent) - .setAutoCancel(true) - .setStyle(new NotificationCompat.BigPictureStyle().bigPicture(bitmap)); + @Override + public void onLoadCleared(@Nullable Drawable placeholder) { - notificationManager.notify(ID_DownloadSingleImageComplete, mBuilder.build()); + } + }); } @@ -98,28 +109,6 @@ public static NotificationCompat.Builder buildNotificationDownloadImageService(C .setOngoing(true); } - public static void showNotificationSaveSingleCachedImageService(Context context, String channelId, File file) { - NotificationManager notificationManager = getNotificationManagerAndCreateChannel(context, channelId); - - Uri imageUri = FileProvider.getUriForFile(context, - BuildConfig.APPLICATION_ID + ".provider", - file.getAbsoluteFile()); - - Intent intent = new Intent(); - intent.setAction(Intent.ACTION_VIEW); - intent.setDataAndType(imageUri, "image/*"); - intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE); - - NotificationCompat.Builder mNotificationDownloadImages = new NotificationCompat.Builder(context, channelId) - .setContentTitle(context.getResources().getString(R.string.app_name)) - .setContentText(context.getString(R.string.toast_img_saved) + " - " + file.getName()) - .setSmallIcon(R.drawable.ic_notification) - .setContentIntent(pendingIntent); - - - notificationManager.notify(1235, mNotificationDownloadImages.build()); - } public static NotificationCompat.Builder buildNotificationDownloadWebPageService(Context context, String channelId) { getNotificationManagerAndCreateChannel(context, channelId); diff --git a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/reader/InsertIntoDatabase.java b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/reader/InsertIntoDatabase.java index 41030719f..77ea11ab6 100644 --- a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/reader/InsertIntoDatabase.java +++ b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/reader/InsertIntoDatabase.java @@ -28,6 +28,7 @@ import de.luhmer.owncloudnewsreader.database.DatabaseConnectionOrm; import de.luhmer.owncloudnewsreader.database.model.Feed; import de.luhmer.owncloudnewsreader.database.model.Folder; +import de.luhmer.owncloudnewsreader.helper.FavIconUtils; public class InsertIntoDatabase { private static final String TAG = "InsertRssItemIntoDb"; @@ -84,10 +85,13 @@ public static void InsertFeedsIntoDatabase(List feeds, DatabaseConnectionO newFeed.setAvgColour(oldFeed.getAvgColour()); // Set the notification channel after sync again newFeed.setNotificationChannel(oldFeed.getNotificationChannel()); + + // fix favicon url + newFeed.setFaviconUrl(FavIconUtils.fixFavIconUrl(newFeed.getFaviconUrl())); + dbConn.updateFeed(newFeed); break; } - } if(!found) { diff --git a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/reader/OkHttpImageDownloader.java b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/reader/OkHttpImageDownloader.java deleted file mode 100644 index 8afae922a..000000000 --- a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/reader/OkHttpImageDownloader.java +++ /dev/null @@ -1,40 +0,0 @@ -package de.luhmer.owncloudnewsreader.reader; - -import android.content.Context; - -import com.nostra13.universalimageloader.core.assist.ContentLengthInputStream; -import com.nostra13.universalimageloader.core.download.BaseImageDownloader; - -import java.io.IOException; -import java.io.InputStream; - -import okhttp3.HttpUrl; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.ResponseBody; - -public class OkHttpImageDownloader extends BaseImageDownloader { - - private static final String TAG = "OkHttpImageDownloader"; - - private final OkHttpClient imageClient; - - public OkHttpImageDownloader(Context context, OkHttpClient imageClient) { - super(context); - this.imageClient = imageClient; - } - - @Override - public InputStream getStreamFromNetwork(String imageUri, Object extra) throws IOException { - HttpUrl httpUrl = HttpUrl.parse(imageUri); - - Request request = new Request.Builder() - .url(httpUrl) - .build(); - - ResponseBody responseBody = imageClient.newCall(request).execute().body(); - InputStream inputStream = responseBody.byteStream(); - - return new ContentLengthInputStream(inputStream, (int) responseBody.contentLength()); - } -} diff --git a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/services/DownloadImagesService.java b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/services/DownloadImagesService.java index 7542fc460..2b62226b4 100644 --- a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/services/DownloadImagesService.java +++ b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/services/DownloadImagesService.java @@ -30,7 +30,7 @@ import androidx.core.app.JobIntentService; import androidx.core.app.NotificationCompat; -import com.nostra13.universalimageloader.core.ImageLoader; +import com.bumptech.glide.RequestManager; import java.util.ArrayList; import java.util.List; @@ -44,6 +44,7 @@ import de.luhmer.owncloudnewsreader.database.model.Feed; import de.luhmer.owncloudnewsreader.database.model.RssItem; import de.luhmer.owncloudnewsreader.helper.FavIconHandler; +import de.luhmer.owncloudnewsreader.helper.GlideApp; import de.luhmer.owncloudnewsreader.helper.ImageHandler; import de.luhmer.owncloudnewsreader.notification.NextcloudNotificationManager; @@ -118,7 +119,7 @@ protected void onHandleWork(@NonNull Intent intent) { FavIconHandler favIconHandler = new FavIconHandler(getApplicationContext()); for(Feed feed : feedList) { try { - favIconHandler.preCacheFavIcon(feed, getApplicationContext()); + favIconHandler.preCacheFavIcon(feed); } catch(IllegalStateException ex) { Log.e(TAG, ex.getMessage()); } @@ -150,9 +151,11 @@ protected void onHandleWork(@NonNull Intent intent) { private void downloadImages(List linksToImages) { try { + RequestManager glide = GlideApp.with(this.getApplicationContext()); + while(linksToImages.size() > 0) { String link = linksToImages.remove(0); - new DownloadImageHandler(link).downloadSync(); + new DownloadImageHandler(link).preloadSync(glide); updateNotificationProgress(linksToImages.size()); } @@ -170,7 +173,6 @@ private void updateNotificationProgress(int remainingImagesCount) { int count = maxCount - remainingImagesCount; if(maxCount == count) { mNotificationManager.cancel(NOTIFICATION_ID); - //RemoveOldImages(); } else { mNotificationDownloadImages .setContentText((count + 1) + "/" + maxCount + " - " + getString(R.string.notification_download_images_offline)) @@ -179,9 +181,4 @@ private void updateNotificationProgress(int remainingImagesCount) { mNotificationManager.notify(NOTIFICATION_ID, mNotificationDownloadImages.build()); } } - - private void RemoveOldImages() { - ImageLoader.getInstance().clearDiskCache(); - } - } From cfb358baf34088daec4a6374c1d72abcd910dace Mon Sep 17 00:00:00 2001 From: David Luhmer Date: Mon, 12 Sep 2022 17:00:55 +0200 Subject: [PATCH 2/8] add missing gradle dependencies --- News-Android-App/build.gradle | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/News-Android-App/build.gradle b/News-Android-App/build.gradle index ca3431a22..40495a112 100644 --- a/News-Android-App/build.gradle +++ b/News-Android-App/build.gradle @@ -141,11 +141,15 @@ dependencies { implementation "androidx.recyclerview:recyclerview:1.1.0" implementation "androidx.browser:browser:1.4.0" implementation "androidx.cardview:cardview:1.0.0" - //implementation 'de.mrmaffen:holocircularprogressbar:1.0.1' - implementation 'com.nostra13.universalimageloader:universal-image-loader:1.9.5' + // implementation 'de.mrmaffen:holocircularprogressbar:1.0.1' + // implementation 'com.nostra13.universalimageloader:universal-image-loader:1.9.5' implementation 'com.google.code.gson:gson:2.9.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' + implementation 'com.github.bumptech.glide:glide:4.13.2' + annotationProcessor 'com.github.bumptech.glide:compiler:4.13.2' + debugImplementation "com.github.technoir42:glide-debug-indicator:0.9.1" + implementation 'com.sothree.slidinguppanel:library:3.4.0' implementation 'org.greenrobot:eventbus:3.2.0' From f4a5cd5ecd0642cc339512ae0caa4b21a45f3b22 Mon Sep 17 00:00:00 2001 From: David Luhmer Date: Mon, 12 Sep 2022 17:02:34 +0200 Subject: [PATCH 3/8] fix wrong import --- .../notification/NextcloudNotificationManager.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/notification/NextcloudNotificationManager.java b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/notification/NextcloudNotificationManager.java index ccdc1925a..4f5fe03c2 100644 --- a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/notification/NextcloudNotificationManager.java +++ b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/notification/NextcloudNotificationManager.java @@ -31,7 +31,6 @@ import androidx.media.app.NotificationCompat.MediaStyle; import androidx.media.session.MediaButtonReceiver; -import com.bumptech.glide.Glide; import com.bumptech.glide.load.engine.DiskCacheStrategy; import com.bumptech.glide.request.target.CustomTarget; import com.bumptech.glide.request.transition.Transition; @@ -48,6 +47,7 @@ import de.luhmer.owncloudnewsreader.database.DatabaseConnectionOrm; import de.luhmer.owncloudnewsreader.database.model.RssItem; import de.luhmer.owncloudnewsreader.helper.DatabaseUtils; +import de.luhmer.owncloudnewsreader.helper.GlideApp; import de.luhmer.owncloudnewsreader.helper.NotificationActionReceiver; public class NextcloudNotificationManager { @@ -60,7 +60,7 @@ public static void showNotificationDownloadSingleImageComplete(Context context, NotificationManager notificationManager = getNotificationManagerAndCreateChannel(context, channelDownloadImage); //Bitmap bitmap = ImageLoader.getInstance().loadImageSync("file://" + imagePath.getAbsolutePath(), targetSize); - Glide.with(context).asBitmap().load("file://" + imagePath.getAbsolutePath()).diskCacheStrategy(DiskCacheStrategy.NONE).into(new CustomTarget(1024, 512) { + GlideApp.with(context).asBitmap().load("file://" + imagePath.getAbsolutePath()).diskCacheStrategy(DiskCacheStrategy.NONE).into(new CustomTarget(1024, 512) { @Override public void onResourceReady(@NonNull Bitmap bitmap, @Nullable Transition transition) { // Uri imageUri = Uri.parse(imagePath); From c50046ada7d18babcc4867d84848ea39907fde0e Mon Sep 17 00:00:00 2001 From: David Luhmer Date: Mon, 12 Sep 2022 17:12:19 +0200 Subject: [PATCH 4/8] cleanup --- .../adapter/RssItemHeadlineThumbnailViewHolder.java | 6 ------ .../adapter/RssItemThumbnailViewHolder.java | 5 ----- .../async_tasks/DownloadImageHandler.java | 1 - .../java/de/luhmer/owncloudnewsreader/di/ApiProvider.java | 2 +- .../de/luhmer/owncloudnewsreader/helper/FavIconHandler.java | 4 ---- .../notification/NextcloudNotificationManager.java | 1 - 6 files changed, 1 insertion(+), 18 deletions(-) diff --git a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/adapter/RssItemHeadlineThumbnailViewHolder.java b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/adapter/RssItemHeadlineThumbnailViewHolder.java index d5f529573..cba73b7d6 100644 --- a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/adapter/RssItemHeadlineThumbnailViewHolder.java +++ b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/adapter/RssItemHeadlineThumbnailViewHolder.java @@ -24,15 +24,10 @@ public class RssItemHeadlineThumbnailViewHolder extends RssItemViewHolder { - //private final DisplayImageOptions displayImageOptionsThumbnail; Drawable feedIcon = VectorDrawableCompat.create(itemView.getResources(), R.drawable.feed_icon, null); - - RssItemHeadlineThumbnailViewHolder(@NonNull SubscriptionDetailListItemHeadlineThumbnailBinding binding, SharedPreferences sharedPreferences) { super(binding, sharedPreferences); - - //int widthThumbnail = Math.round(88f * binding.imgViewThumbnail.getContext().getResources().getDisplayMetrics().density); } @Override @@ -96,7 +91,6 @@ public void bind(@NonNull RssItem rssItem) { String mediaThumbnail = rssItem.getMediaThumbnail(); if (mediaThumbnail != null && !mediaThumbnail.isEmpty()) { binding.imgViewThumbnail.setVisibility(View.VISIBLE); - //ImageLoader.getInstance().displayImage(mediaThumbnail, binding.imgViewThumbnail, displayImageOptionsThumbnail); mGlide .load(mediaThumbnail) diff --git a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/adapter/RssItemThumbnailViewHolder.java b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/adapter/RssItemThumbnailViewHolder.java index e6f83d198..62225e5a3 100644 --- a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/adapter/RssItemThumbnailViewHolder.java +++ b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/adapter/RssItemThumbnailViewHolder.java @@ -25,14 +25,11 @@ import de.luhmer.owncloudnewsreader.databinding.SubscriptionDetailListItemThumbnailBinding; public class RssItemThumbnailViewHolder extends RssItemViewHolder { - //private final DisplayImageOptions displayImageOptionsThumbnail; Drawable feedIcon = VectorDrawableCompat.create(itemView.getResources(), R.drawable.feed_icon, null); RssItemThumbnailViewHolder(@NonNull SubscriptionDetailListItemThumbnailBinding binding, SharedPreferences sharedPreferences) { super(binding, sharedPreferences); - - //int width = Math.round(88f * binding.imgViewThumbnail.getContext().getResources().getDisplayMetrics().density); } @Override @@ -93,8 +90,6 @@ public void bind(@NonNull RssItem rssItem) { String mediaThumbnail = rssItem.getMediaThumbnail(); if (mediaThumbnail != null && !mediaThumbnail.isEmpty()) { binding.imgViewThumbnail.setVisibility(View.VISIBLE); - //ImageLoader.getInstance().displayImage(mediaThumbnail, binding.imgViewThumbnail, displayImageOptionsThumbnail); - // ImageLoader.getInstance().displayImage(mediaThumbnail, binding.imgViewThumbnail, displayImageOptionsThumbnail); mGlide .load(mediaThumbnail) diff --git a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/async_tasks/DownloadImageHandler.java b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/async_tasks/DownloadImageHandler.java index 86df4f069..8def1ad4b 100644 --- a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/async_tasks/DownloadImageHandler.java +++ b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/async_tasks/DownloadImageHandler.java @@ -59,7 +59,6 @@ public void preloadSync(RequestManager glide) { e.printStackTrace(); } NotifyDownloadFinished(null); - //ImageLoader.getInstance().loadImageSync(mImageUrl.toString(), displayImageOptions); } private void NotifyDownloadFinished(Bitmap bitmap) { diff --git a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/di/ApiProvider.java b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/di/ApiProvider.java index dd72a9a85..bbf2927b2 100644 --- a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/di/ApiProvider.java +++ b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/di/ApiProvider.java @@ -65,7 +65,7 @@ public void initApi(@NonNull NextcloudAPI.ApiConnectedListener apiConnectedListe boolean useSSO = mPrefs.getBoolean(SettingsActivity.SW_USE_SINGLE_SIGN_ON, false); if(useSSO) { // OkHttpClient client = new OkHttpClient.Builder().build(); - //initImageLoader(mPrefs, client, context); + // initImageLoader(mPrefs, client, context); initSsoApi(apiConnectedListener); } else { if(mPrefs.contains(SettingsActivity.EDT_OWNCLOUDROOTPATH_STRING)) { diff --git a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/helper/FavIconHandler.java b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/helper/FavIconHandler.java index 293ad185c..4bc5cc0a8 100644 --- a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/helper/FavIconHandler.java +++ b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/helper/FavIconHandler.java @@ -53,7 +53,6 @@ public class FavIconHandler { public FavIconHandler(Context context) { mPlaceHolder = FavIconHandler.getResourceIdForRightDefaultFeedIcon(); - //int widthFavIcon = Math.round(20f * context.getResources().getDisplayMetrics().density); mContext = context; mGlide = GlideApp.with(context); } @@ -76,10 +75,7 @@ public void loadFavIconForFeed(@Nullable String favIconUrl, ImageView imgView) { .apply(requestOptions) .onlyRetrieveFromCache(true) // disable loading of favicons from network (usually those favicons are broken) .into(imgView); - //ImageLoader.getInstance().displayImage(favIconUrl, imgView, displayImageOptions); } - - } /** diff --git a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/notification/NextcloudNotificationManager.java b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/notification/NextcloudNotificationManager.java index 4f5fe03c2..5db5603ac 100644 --- a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/notification/NextcloudNotificationManager.java +++ b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/notification/NextcloudNotificationManager.java @@ -59,7 +59,6 @@ public static void showNotificationDownloadSingleImageComplete(Context context, String channelDownloadImage = context.getString(R.string.action_img_download); NotificationManager notificationManager = getNotificationManagerAndCreateChannel(context, channelDownloadImage); - //Bitmap bitmap = ImageLoader.getInstance().loadImageSync("file://" + imagePath.getAbsolutePath(), targetSize); GlideApp.with(context).asBitmap().load("file://" + imagePath.getAbsolutePath()).diskCacheStrategy(DiskCacheStrategy.NONE).into(new CustomTarget(1024, 512) { @Override public void onResourceReady(@NonNull Bitmap bitmap, @Nullable Transition transition) { From ad3be213303489dc3ff9bd37f8e28cbe7a54e560 Mon Sep 17 00:00:00 2001 From: David Luhmer Date: Mon, 12 Sep 2022 17:34:21 +0200 Subject: [PATCH 5/8] disable glide indicators in prod build --- .../helper/NextcloudGlideModule.java | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/helper/NextcloudGlideModule.java b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/helper/NextcloudGlideModule.java index 4c585baaa..ef843e807 100644 --- a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/helper/NextcloudGlideModule.java +++ b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/helper/NextcloudGlideModule.java @@ -9,7 +9,7 @@ import com.bumptech.glide.load.engine.cache.InternalCacheDiskCacheFactory; import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions; import com.bumptech.glide.module.AppGlideModule; -import com.github.technoir42.glide.debug.indicator.DebugIndicatorTransitionFactory; +// import com.github.technoir42.glide.debug.indicator.DebugIndicatorTransitionFactory; import javax.inject.Inject; @@ -26,12 +26,6 @@ public class NextcloudGlideModule extends AppGlideModule { @Override public void applyOptions(Context context, GlideBuilder builder) { - // #00ff00 Memory Cache (Green) - // #0066ff Disk Cache (Blue) - // #ff0000 Remote (Red) - // #ffff00 Local (Yellow) - builder.setDefaultTransitionOptions(Drawable.class, DrawableTransitionOptions.with(DebugIndicatorTransitionFactory.DEFAULT)); - ((NewsReaderApplication) context.getApplicationContext()).getAppComponent().injectGlideModule(this); String cacheSize = mPrefs.getString(SettingsActivity.SP_MAX_CACHE_SIZE,"500"); @@ -43,5 +37,14 @@ public void applyOptions(Context context, GlideBuilder builder) { builder.setDiskCache(new InternalCacheDiskCacheFactory(context, diskCacheSizeBytes)); // builder.setDiskCache(new ExternalCacheDiskCacheFactory(context)); + + + // #00ff00 Memory Cache (Green) + // #0066ff Disk Cache (Blue) + // #ff0000 Remote (Red) + // #ffff00 Local (Yellow) + + // enable caching indicators for Glide + // builder.setDefaultTransitionOptions(Drawable.class, DrawableTransitionOptions.with(DebugIndicatorTransitionFactory.DEFAULT)); } } \ No newline at end of file From ac746b46828213f9494e085f0a291a2422104b69 Mon Sep 17 00:00:00 2001 From: David Luhmer Date: Sat, 3 Dec 2022 10:49:51 +0100 Subject: [PATCH 6/8] update dependencies --- News-Android-App/build.gradle | 5 +---- .../de/luhmer/owncloudnewsreader/NewsDetailFragment.java | 1 - .../luhmer/owncloudnewsreader/PodcastFragmentActivity.java | 3 ++- build.gradle | 2 +- gradle.properties | 4 ++-- 5 files changed, 6 insertions(+), 9 deletions(-) diff --git a/News-Android-App/build.gradle b/News-Android-App/build.gradle index 40495a112..682576c5c 100644 --- a/News-Android-App/build.gradle +++ b/News-Android-App/build.gradle @@ -63,8 +63,6 @@ android { } } - - flavorDimensions "default" productFlavors { @@ -79,19 +77,18 @@ android { } } - sourceSets { main { aidl.srcDirs = ['src/main/java'] } } + packagingOptions { resources { excludes += ['META-INF/LICENSE.txt', 'META-INF/NOTICE.txt', 'LICENSE.txt', 'META-INF/services/javax.annotation.processing.Processor'] } } - // Gradle automatically adds 'android.test.runner' as a dependency. useLibrary 'android.test.runner' useLibrary 'android.test.base' diff --git a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/NewsDetailFragment.java b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/NewsDetailFragment.java index 44c7d4446..94c0b4f3e 100644 --- a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/NewsDetailFragment.java +++ b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/NewsDetailFragment.java @@ -304,7 +304,6 @@ private void applyWebSettings() { webSettings.setJavaScriptCanOpenWindowsAutomatically(false); webSettings.setSupportMultipleWindows(false); webSettings.setSupportZoom(false); - webSettings.setAppCacheEnabled(true); webSettings.setMediaPlaybackRequiresUserGesture(true); syncIncognitoState(); diff --git a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/PodcastFragmentActivity.java b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/PodcastFragmentActivity.java index 1a2217e7b..227bd02d5 100644 --- a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/PodcastFragmentActivity.java +++ b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/PodcastFragmentActivity.java @@ -20,6 +20,7 @@ import androidx.appcompat.app.AppCompatActivity; import com.nextcloud.android.sso.helper.VersionCheckHelper; +import com.nextcloud.android.sso.model.FilesAppType; import com.sothree.slidinguppanel.SlidingUpPanelLayout; import org.greenrobot.eventbus.EventBus; @@ -74,7 +75,7 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { //if (mApi.getAPI() instanceof Proxy) { // doesn't work.. retrofit is also a "proxy" boolean useSSO = mPrefs.getBoolean(SettingsActivity.SW_USE_SINGLE_SIGN_ON, false); if(useSSO) { - VersionCheckHelper.verifyMinVersion(this, MIN_NEXTCLOUD_FILES_APP_VERSION_CODE); + VersionCheckHelper.verifyMinVersion(this, MIN_NEXTCLOUD_FILES_APP_VERSION_CODE, FilesAppType.PROD); } mPostDelayHandler.stopRunningPostDelayHandler(); diff --git a/build.gradle b/build.gradle index 7f25808b4..15eea118a 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ buildscript { google() } dependencies { - classpath 'com.android.tools.build:gradle:7.2.2' + classpath 'com.android.tools.build:gradle:7.3.1' } } diff --git a/gradle.properties b/gradle.properties index f74cdd4fc..0ba8016b3 100644 --- a/gradle.properties +++ b/gradle.properties @@ -18,8 +18,8 @@ # org.gradle.parallel=true ANDROID_BUILD_MIN_SDK_VERSION=21 -ANDROID_BUILD_TARGET_SDK_VERSION=31 -ANDROID_BUILD_SDK_VERSION=31 +ANDROID_BUILD_TARGET_SDK_VERSION=33 +ANDROID_BUILD_SDK_VERSION=33 android.useAndroidX=true android.enableJetifier=true org.gradle.jvmargs=-Xmx4096m From 19be03cd920ea2a63b3335c72ead42271120fa16 Mon Sep 17 00:00:00 2001 From: David Luhmer Date: Sat, 3 Dec 2022 13:21:03 +0100 Subject: [PATCH 7/8] use SVG renderer to display SVG favicons Signed-off-by: David Luhmer --- .../glide/samples/svg/SvgDecoder.java | 42 ++++++++++++++ .../samples/svg/SvgDrawableTranscoder.java | 27 +++++++++ .../helper/FavIconHandler.java | 58 +++++++++++-------- .../helper/FavIconUtils.java | 14 +++-- .../helper/NextcloudGlideModule.java | 29 ++++++++-- 5 files changed, 136 insertions(+), 34 deletions(-) create mode 100644 News-Android-App/src/main/java/com/bumptech/glide/samples/svg/SvgDecoder.java create mode 100644 News-Android-App/src/main/java/com/bumptech/glide/samples/svg/SvgDrawableTranscoder.java diff --git a/News-Android-App/src/main/java/com/bumptech/glide/samples/svg/SvgDecoder.java b/News-Android-App/src/main/java/com/bumptech/glide/samples/svg/SvgDecoder.java new file mode 100644 index 000000000..5dbe506c9 --- /dev/null +++ b/News-Android-App/src/main/java/com/bumptech/glide/samples/svg/SvgDecoder.java @@ -0,0 +1,42 @@ +package com.bumptech.glide.samples.svg; + +import static com.bumptech.glide.request.target.Target.SIZE_ORIGINAL; + +import androidx.annotation.NonNull; + +import com.bumptech.glide.load.Options; +import com.bumptech.glide.load.ResourceDecoder; +import com.bumptech.glide.load.engine.Resource; +import com.bumptech.glide.load.resource.SimpleResource; +import com.caverock.androidsvg.SVG; +import com.caverock.androidsvg.SVGParseException; + +import java.io.IOException; +import java.io.InputStream; + +/** + * Decodes an SVG internal representation from an {@link InputStream}. + */ +public class SvgDecoder implements ResourceDecoder { + + @Override + public boolean handles(@NonNull InputStream source, @NonNull Options options) { + return true; + } + + public Resource decode(@NonNull InputStream source, int width, int height, @NonNull Options options) + throws IOException { + try { + SVG svg = SVG.getFromInputStream(source); + if (width != SIZE_ORIGINAL) { + svg.setDocumentWidth(width); + } + if (height != SIZE_ORIGINAL) { + svg.setDocumentHeight(height); + } + return new SimpleResource<>(svg); + } catch (SVGParseException ex) { + throw new IOException("Cannot load SVG from stream", ex); + } + } +} \ No newline at end of file diff --git a/News-Android-App/src/main/java/com/bumptech/glide/samples/svg/SvgDrawableTranscoder.java b/News-Android-App/src/main/java/com/bumptech/glide/samples/svg/SvgDrawableTranscoder.java new file mode 100644 index 000000000..cf543c0f8 --- /dev/null +++ b/News-Android-App/src/main/java/com/bumptech/glide/samples/svg/SvgDrawableTranscoder.java @@ -0,0 +1,27 @@ +package com.bumptech.glide.samples.svg; + +import android.graphics.Picture; +import android.graphics.drawable.PictureDrawable; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.bumptech.glide.load.Options; +import com.bumptech.glide.load.engine.Resource; +import com.bumptech.glide.load.resource.SimpleResource; +import com.bumptech.glide.load.resource.transcode.ResourceTranscoder; +import com.caverock.androidsvg.SVG; + +/** + * Convert the {@link SVG}'s internal representation to an Android-compatible one ({@link Picture}). + */ +public class SvgDrawableTranscoder implements ResourceTranscoder { + @Nullable + @Override + public Resource transcode(@NonNull Resource toTranscode, @NonNull Options options) { + SVG svg = toTranscode.get(); + Picture picture = svg.renderToPicture(); + PictureDrawable drawable = new PictureDrawable(picture); + return new SimpleResource<>(drawable); + } +} \ No newline at end of file diff --git a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/helper/FavIconHandler.java b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/helper/FavIconHandler.java index 4bc5cc0a8..d640bb6a8 100644 --- a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/helper/FavIconHandler.java +++ b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/helper/FavIconHandler.java @@ -47,9 +47,9 @@ public class FavIconHandler { private static final String TAG = FavIconHandler.class.getCanonicalName(); - private RequestManager mGlide; - private Context mContext; - private int mPlaceHolder; + private final RequestManager mGlide; + private final Context mContext; + private final int mPlaceHolder; public FavIconHandler(Context context) { mPlaceHolder = FavIconHandler.getResourceIdForRightDefaultFeedIcon(); @@ -57,34 +57,38 @@ public FavIconHandler(Context context) { mGlide = GlideApp.with(context); } - public void loadFavIconForFeed(@Nullable String favIconUrl, ImageView imgView) { + public void loadFavIconForFeed(@Nullable String favIconUrl, ImageView imgView) { RequestOptions requestOptions = new RequestOptions(); requestOptions = requestOptions.transforms(new RoundedCorners(6)); - if(favIconUrl == null) { + if (favIconUrl == null) { mGlide - .load(mPlaceHolder) - .apply(requestOptions) - .into(imgView); + .load(mPlaceHolder) + .apply(requestOptions) + .into(imgView); } else { mGlide - .load(favIconUrl) - .diskCacheStrategy(DiskCacheStrategy.DATA) - .placeholder(mPlaceHolder) - .error(mPlaceHolder) - .apply(requestOptions) - .onlyRetrieveFromCache(true) // disable loading of favicons from network (usually those favicons are broken) - .into(imgView); + .load(favIconUrl) + .diskCacheStrategy(DiskCacheStrategy.DATA) + .placeholder(mPlaceHolder) + .error(mPlaceHolder) + .apply(requestOptions) + .onlyRetrieveFromCache(true) // disable loading of favicons from network (usually those favicons are broken) + .into(imgView); } } + boolean isSVG(String url) { + return url.contains("svg"); + } + /** * Version of loadFacIconForFeed that applies a vertical offset to the icon ImageView, * to compensate for font size scaling alignment issue * - * @param favIconUrl URL of icon to load/display - * @param imgView ImageView object to use for icon display - * @param offset Y translation to apply to ImageView + * @param favIconUrl URL of icon to load/display + * @param imgView ImageView object to use for icon display + * @param offset Y translation to apply to ImageView */ public void loadFavIconForFeed(String favIconUrl, ImageView imgView, int offset) { loadFavIconForFeed(favIconUrl, imgView); @@ -97,7 +101,6 @@ public static int getResourceIdForRightDefaultFeedIcon() { } else { return R.drawable.default_feed_icon_light; } - } public void preCacheFavIcon(final Feed feed) throws IllegalStateException { @@ -107,17 +110,24 @@ public void preCacheFavIcon(final Feed feed) throws IllegalStateException { } String favIconUrl = feed.getFaviconUrl(); - Log.v(TAG, "Loading image: " + favIconUrl); + + // pre caching doesn't work for SVG icons + if (isSVG(favIconUrl)) { + return; + } + + // Log.v(TAG, "Pre caching favicon: " + favIconUrl); + mGlide .asBitmap() .load(favIconUrl) .diskCacheStrategy(DiskCacheStrategy.DATA) .apply(RequestOptions.overrideOf(Target.SIZE_ORIGINAL)) .into(new SimpleTarget() { - @Override - public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition transition) { - UpdateAvgColorOfFeed(feed.getId(), resource, mContext); - Log.d(TAG, "Successfully downloaded image for url: " + favIconUrl); + @Override + public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition transition) { + UpdateAvgColorOfFeed(feed.getId(), resource, mContext); + Log.d(TAG, "Successfully downloaded image for url: " + favIconUrl); } @Override diff --git a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/helper/FavIconUtils.java b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/helper/FavIconUtils.java index d884c1c6e..f02bcf706 100644 --- a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/helper/FavIconUtils.java +++ b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/helper/FavIconUtils.java @@ -1,26 +1,32 @@ package de.luhmer.owncloudnewsreader.helper; -import android.util.Log; - import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.nio.charset.StandardCharsets; public class FavIconUtils { - private static String TAG = FavIconUtils.class.getCanonicalName(); + private static final String TAG = FavIconUtils.class.getCanonicalName(); public static String fixFavIconUrl(String favIconUrl) { - if(favIconUrl == null) { + if (favIconUrl == null) { return null; } + if (favIconUrl.startsWith("https://i2.wp.com/stadt-bremerhaven.de/wp-content/uploads/2014/12/logo")) { + // Fix favicon for cachys blog... + return "https://stadt-bremerhaven.de/wp-content/uploads/2018/08/sblogo-150x150.jpg"; + } + return favIconUrl; + + /* try { favIconUrl = decodeSpecialChars(favIconUrl); }catch(Exception ex) { Log.e(TAG, ex.toString()); } return fixSvgIcons(favIconUrl); + */ } protected static String decodeSpecialChars(String favIconUrl) throws UnsupportedEncodingException { diff --git a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/helper/NextcloudGlideModule.java b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/helper/NextcloudGlideModule.java index ef843e807..ef1df4b8e 100644 --- a/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/helper/NextcloudGlideModule.java +++ b/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/helper/NextcloudGlideModule.java @@ -2,14 +2,21 @@ import android.content.Context; import android.content.SharedPreferences; -import android.graphics.drawable.Drawable; +import android.graphics.drawable.PictureDrawable; +import androidx.annotation.NonNull; + +import com.bumptech.glide.Glide; import com.bumptech.glide.GlideBuilder; +import com.bumptech.glide.Registry; import com.bumptech.glide.annotation.GlideModule; import com.bumptech.glide.load.engine.cache.InternalCacheDiskCacheFactory; -import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions; import com.bumptech.glide.module.AppGlideModule; -// import com.github.technoir42.glide.debug.indicator.DebugIndicatorTransitionFactory; +import com.bumptech.glide.samples.svg.SvgDecoder; +import com.bumptech.glide.samples.svg.SvgDrawableTranscoder; +import com.caverock.androidsvg.SVG; + +import java.io.InputStream; import javax.inject.Inject; @@ -19,17 +26,19 @@ @GlideModule public class NextcloudGlideModule extends AppGlideModule { - private String TAG = NextcloudGlideModule.class.getCanonicalName(); + private final String TAG = NextcloudGlideModule.class.getCanonicalName(); protected @Inject ApiProvider mApi; protected @Inject SharedPreferences mPrefs; @Override public void applyOptions(Context context, GlideBuilder builder) { + super.applyOptions(context, builder); + ((NewsReaderApplication) context.getApplicationContext()).getAppComponent().injectGlideModule(this); - String cacheSize = mPrefs.getString(SettingsActivity.SP_MAX_CACHE_SIZE,"500"); - int diskCacheSizeBytes = Integer.parseInt(cacheSize)*1024*1024; + String cacheSize = mPrefs.getString(SettingsActivity.SP_MAX_CACHE_SIZE, "500"); + int diskCacheSizeBytes = Integer.parseInt(cacheSize) * 1024 * 1024; // Glide uses DiskLruCacheWrapper as the default DiskCache. DiskLruCacheWrapper is a fixed // size disk cache with LRU eviction. The default disk cache size is 250 MB and is placed @@ -47,4 +56,12 @@ public void applyOptions(Context context, GlideBuilder builder) { // enable caching indicators for Glide // builder.setDefaultTransitionOptions(Drawable.class, DrawableTransitionOptions.with(DebugIndicatorTransitionFactory.DEFAULT)); } + + @Override + public void registerComponents(@NonNull Context context, @NonNull Glide glide, @NonNull Registry registry) { + super.registerComponents(context, glide, registry); + registry + .register(SVG.class, PictureDrawable.class, new SvgDrawableTranscoder()) + .append(InputStream.class, SVG.class, new SvgDecoder()); + } } \ No newline at end of file From 168c8dcf2b2c0398afab16b95442858f444f34c7 Mon Sep 17 00:00:00 2001 From: David Luhmer Date: Tue, 13 Dec 2022 16:22:03 +0100 Subject: [PATCH 8/8] add missing dependency Signed-off-by: David Luhmer --- News-Android-App/build.gradle | 1 + gradle/wrapper/gradle-wrapper.properties | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/News-Android-App/build.gradle b/News-Android-App/build.gradle index dfc29d6d3..8b1ead60d 100644 --- a/News-Android-App/build.gradle +++ b/News-Android-App/build.gradle @@ -146,6 +146,7 @@ dependencies { implementation 'com.github.bumptech.glide:glide:4.13.2' annotationProcessor 'com.github.bumptech.glide:compiler:4.13.2' debugImplementation "com.github.technoir42:glide-debug-indicator:0.9.1" + implementation 'com.caverock:androidsvg-aar:1.4' implementation 'com.sothree.slidinguppanel:library:3.4.0' diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index c61f873ed..b4ded3371 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip