diff --git a/OsmAnd/src/net/osmand/plus/mapcontextmenu/builders/cards/ImageCard.java b/OsmAnd/src/net/osmand/plus/mapcontextmenu/builders/cards/ImageCard.java index 50fccbffda2..7fd8f1b1e0f 100644 --- a/OsmAnd/src/net/osmand/plus/mapcontextmenu/builders/cards/ImageCard.java +++ b/OsmAnd/src/net/osmand/plus/mapcontextmenu/builders/cards/ImageCard.java @@ -327,7 +327,24 @@ public void update() { progress.setVisibility(View.VISIBLE); image.setImageBitmap(null); } else if (!downloaded) { - MenuBuilder.execute(new DownloadImageTask(getMyApplication(), imageUrl, getDownloadImageListener())); + MenuBuilder.execute(new DownloadImageTask(getMyApplication(), imageUrl, new DownloadImageListener() { + @Override + public void onStartDownloading() { + downloading = true; + update(); + } + + @Override + public void onFinishDownloading(Bitmap bitmap) { + downloading = false; + downloaded = true; + ImageCard.this.bitmap = bitmap; + if (bitmap != null && Algorithms.isEmpty(getImageHiresUrl())) { + ImageCard.this.imageHiresUrl = getUrl(); + } + update(); + } + })); } else { progress.setVisibility(View.GONE); image.setImageBitmap(bitmap); @@ -370,25 +387,4 @@ public void update() { } } } - - private DownloadImageListener getDownloadImageListener() { - return new DownloadImageListener() { - @Override - public void onStartDownloading() { - downloading = true; - update(); - } - - @Override - public void onFinishDownloading(Bitmap bitmap) { - downloading = false; - downloaded = true; - ImageCard.this.bitmap = bitmap; - if (bitmap != null && Algorithms.isEmpty(getImageHiresUrl())) { - ImageCard.this.imageHiresUrl = getUrl(); - } - update(); - } - }; - } } diff --git a/OsmAnd/src/net/osmand/plus/mapcontextmenu/builders/cards/UrlImageCard.java b/OsmAnd/src/net/osmand/plus/mapcontextmenu/builders/cards/UrlImageCard.java index 6d0c7ee0a16..d2c43de8150 100644 --- a/OsmAnd/src/net/osmand/plus/mapcontextmenu/builders/cards/UrlImageCard.java +++ b/OsmAnd/src/net/osmand/plus/mapcontextmenu/builders/cards/UrlImageCard.java @@ -1,8 +1,9 @@ package net.osmand.plus.mapcontextmenu.builders.cards; -import android.view.View; import android.view.View.OnClickListener; +import androidx.annotation.Nullable; + import net.osmand.plus.activities.MapActivity; import net.osmand.util.Algorithms; @@ -14,13 +15,10 @@ public UrlImageCard(MapActivity mapActivity, JSONObject imageObject) { super(mapActivity, imageObject); if (!Algorithms.isEmpty(getSuitableUrl())) { - OnClickListener onClickListener = new OnClickListener() { - @Override - public void onClick(View v) { - openUrl(getMapActivity(), getMyApplication(), getTitle(), getSuitableUrl(), - isExternalLink() || Algorithms.isEmpty(getImageHiresUrl()), - !Algorithms.isEmpty(getImageHiresUrl())); - } + OnClickListener onClickListener = v -> { + boolean hasImageUrl = !Algorithms.isEmpty(getImageHiresUrl()); + boolean externalLink = isExternalLink() || !hasImageUrl; + openUrl(mapActivity, app, getTitle(), getSuitableUrl(), externalLink, hasImageUrl); }; if (!Algorithms.isEmpty(buttonText)) { this.onButtonClickListener = onClickListener; @@ -30,13 +28,40 @@ public void onClick(View v) { } } - private String getSuitableUrl() { - String url; - if (Algorithms.isEmpty(getImageHiresUrl())) { - url = getUrl(); - } else { - url = getImageHiresUrl(); - } - return url; + /** + * Returns the thumbnail URL for the image. + * In this implementation, it always returns `null` due to the nature of the OSM "image" tag. + * Since the tag can contain any URL pointing to any external service, generating a reliable + * thumbnail format (a highly compressed low-resolution image) is not feasible. + * In such cases, the best approach is to directly load the full-size image instead. + */ + @Nullable + @Override + public String getThumbnailUrl() { + return null; + } + + /** + * Returns the URL for displaying the image in the gallery. + * Instead of using the "hires" image URL (if available), this method provides a lower-quality + * "image" URL in this case to avoid potential crashes. + * Some high-resolution images may be too large to be properly rendered on a canvas bitmap. + * To prevent crashes, this implementation uses URL with lower-quality image. + */ @Nullable + @Override + public String getGalleryFullSizeUrl() { + return getImageUrl(); + } + + /** + * Returns a suitable URL for opening in a browser. + * This may be a high-resolution image URL (if available) or the URL stored in the OSM "image" tag. + * The link can either point directly to an image file or lead to a webpage hosting the image + * on an external service. + */ + @Nullable + public String getSuitableUrl() { + String hiresUrl = getImageHiresUrl(); + return Algorithms.isEmpty(hiresUrl) ? getUrl() : hiresUrl; } } diff --git a/OsmAnd/src/net/osmand/plus/mapcontextmenu/gallery/GalleryPhotoPagerFragment.java b/OsmAnd/src/net/osmand/plus/mapcontextmenu/gallery/GalleryPhotoPagerFragment.java index b1230e48b44..ebd824756de 100644 --- a/OsmAnd/src/net/osmand/plus/mapcontextmenu/gallery/GalleryPhotoPagerFragment.java +++ b/OsmAnd/src/net/osmand/plus/mapcontextmenu/gallery/GalleryPhotoPagerFragment.java @@ -36,6 +36,7 @@ import net.osmand.plus.base.BaseOsmAndFragment; import net.osmand.plus.helpers.AndroidUiHelper; import net.osmand.plus.mapcontextmenu.builders.cards.ImageCard; +import net.osmand.plus.mapcontextmenu.builders.cards.UrlImageCard; import net.osmand.plus.mapcontextmenu.gallery.GalleryController.DownloadMetadataListener; import net.osmand.plus.utils.AndroidUtils; import net.osmand.plus.utils.ColorUtilities; @@ -389,6 +390,7 @@ private void shareImage() { } public void showContextWidgetMenu(@NonNull View view) { + ImageCard card = getSelectedImageCard(); List items = new ArrayList<>(); UiUtilities uiUtilities = app.getUIUtilities(); int iconColor = ColorUtilities.getDefaultIconColor(app, nightMode); @@ -398,23 +400,28 @@ public void showContextWidgetMenu(@NonNull View view) { .setOnClickListener(item -> GalleryDetailsFragment.showInstance(getMapActivity(), selectedPosition)) .create()); - items.add(new PopUpMenuItem.Builder(app) - .setIcon(uiUtilities.getPaintedIcon(R.drawable.ic_action_external_link, iconColor)) - .setTitleId(R.string.open_in_browser) - .setOnClickListener(item -> { - FragmentActivity activity = getActivity(); - ImageCard card = getSelectedImageCard(); - if (activity != null && card instanceof WikiImageCard wikiImageCard) { - AndroidUtils.openUrl(activity, wikiImageCard.getWikiImage().getUrlWithCommonAttributions(), nightMode); - } - }) - .create()); + if (card instanceof WikiImageCard || card instanceof UrlImageCard urlCard && urlCard.getSuitableUrl() != null) { + items.add(new PopUpMenuItem.Builder(app) + .setIcon(uiUtilities.getPaintedIcon(R.drawable.ic_action_external_link, iconColor)) + .setTitleId(R.string.open_in_browser) + .setOnClickListener(item -> { + FragmentActivity activity = getActivity(); + if (activity != null) { + if (card instanceof WikiImageCard wikiImageCard) { + AndroidUtils.openUrl(activity, wikiImageCard.getWikiImage().getUrlWithCommonAttributions(), nightMode); + } else { + UrlImageCard urlImageCard = (UrlImageCard) card; + AndroidUtils.openUrl(activity, urlImageCard.getSuitableUrl(), nightMode); + } + } + }) + .create()); + } items.add(new PopUpMenuItem.Builder(app) .setIcon(uiUtilities.getPaintedIcon(R.drawable.ic_action_gsave_dark, iconColor)) .setTitleId(R.string.shared_string_download) .setOnClickListener(item -> { - ImageCard card = getSelectedImageCard(); String downloadUrl = card.getImageHiresUrl(); if (Algorithms.isEmpty(downloadUrl)) { downloadUrl = card.getImageUrl(); diff --git a/OsmAnd/src/net/osmand/plus/mapcontextmenu/gallery/GalleryPhotoViewerFragment.java b/OsmAnd/src/net/osmand/plus/mapcontextmenu/gallery/GalleryPhotoViewerFragment.java index 40decb0e415..b8551d3825f 100644 --- a/OsmAnd/src/net/osmand/plus/mapcontextmenu/gallery/GalleryPhotoViewerFragment.java +++ b/OsmAnd/src/net/osmand/plus/mapcontextmenu/gallery/GalleryPhotoViewerFragment.java @@ -83,20 +83,25 @@ public boolean onSingleTapConfirmed(@NonNull MotionEvent e) { } private void downloadThumbnail(@NonNull ImageCard imageCard) { - Picasso.get() - .load(imageCard.getThumbnailUrl()) - .into(imageView, new Callback() { - @Override - public void onSuccess() { - downloadHiResImage(imageCard); - } - - @Override - public void onError(Exception e) { - downloadHiResImage(imageCard); - LOG.error(e); - } - }); + String thumbnailUrl = imageCard.getThumbnailUrl(); + if (thumbnailUrl != null) { + Picasso.get() + .load(thumbnailUrl) + .into(imageView, new Callback() { + @Override + public void onSuccess() { + downloadHiResImage(imageCard); + } + + @Override + public void onError(Exception e) { + downloadHiResImage(imageCard); + LOG.error(e); + } + }); + } else { + downloadHiResImage(imageCard); + } } private void downloadHiResImage(@NonNull ImageCard imageCard) {