-
Notifications
You must be signed in to change notification settings - Fork 272
Tapping on an image opens pinch-to-zoom view #1491
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| document.addEventListener("DOMContentLoaded", function() { | ||
| document.addEventListener("click", function(e) { | ||
| var target = e.target; | ||
| if (target.tagName === "IMG" && target.src) { | ||
| e.preventDefault(); | ||
| e.stopPropagation(); | ||
| hostImageController.onImageClicked(target.src); | ||
| } | ||
| }, true); | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,136 @@ | ||
| package fr.gaulupeau.apps.Poche.ui; | ||
|
|
||
| import android.graphics.Bitmap; | ||
| import android.graphics.BitmapFactory; | ||
| import android.os.Bundle; | ||
| import android.util.Log; | ||
| import android.view.View; | ||
| import android.widget.ProgressBar; | ||
|
|
||
| import androidx.appcompat.app.AppCompatActivity; | ||
|
|
||
| import com.github.panpf.zoomimage.ZoomImageView; | ||
|
|
||
| import java.io.ByteArrayOutputStream; | ||
| import java.io.File; | ||
| import java.io.InputStream; | ||
| import java.net.URL; | ||
|
|
||
| import fr.gaulupeau.apps.InThePoche.R; | ||
| import fr.gaulupeau.apps.Poche.network.ImageCacheUtils; | ||
|
|
||
| public class ImageViewActivity extends AppCompatActivity { | ||
|
|
||
| public static final String EXTRA_IMAGE_URL = "ImageViewActivity.imageUrl"; | ||
| public static final String EXTRA_ARTICLE_ID = "ImageViewActivity.articleId"; | ||
|
|
||
| private static final String TAG = ImageViewActivity.class.getSimpleName(); | ||
|
|
||
| @Override | ||
| protected void onCreate(Bundle savedInstanceState) { | ||
| super.onCreate(savedInstanceState); | ||
| setContentView(R.layout.activity_image_view); | ||
|
|
||
| ZoomImageView photoView = findViewById(R.id.photoView); | ||
| ProgressBar progressBar = findViewById(R.id.progressBar); | ||
|
|
||
| String imageUrl = getIntent().getStringExtra(EXTRA_IMAGE_URL); | ||
| long articleId = getIntent().getLongExtra(EXTRA_ARTICLE_ID, -1); | ||
|
|
||
| if (imageUrl == null || imageUrl.isEmpty()) { | ||
| Log.w(TAG, "onCreate() no image URL"); | ||
| finish(); | ||
| return; | ||
| } | ||
|
|
||
| photoView.setOnClickListener(v -> finish()); | ||
|
|
||
| new Thread(() -> { | ||
| Bitmap bitmap = loadBitmap(imageUrl, articleId); | ||
| runOnUiThread(() -> { | ||
| progressBar.setVisibility(View.GONE); | ||
| if (bitmap != null) { | ||
| photoView.setImageBitmap(bitmap); | ||
| } else { | ||
| Log.w(TAG, "onCreate() failed to load image"); | ||
| finish(); | ||
| } | ||
| }); | ||
| }).start(); | ||
| } | ||
|
|
||
| private Bitmap loadBitmap(String imageUrl, long articleId) { | ||
| // Try loading from local cache first | ||
| if (articleId >= 0) { | ||
| try { | ||
| File file = ImageCacheUtils.getCachedImageFile(imageUrl, articleId); | ||
| if (file != null) { | ||
| Bitmap bitmap = decodeFileScaled(file.getAbsolutePath()); | ||
| if (bitmap != null) return bitmap; | ||
| } | ||
| } catch (Exception e) { | ||
| Log.w(TAG, "loadBitmap() cache load failed", e); | ||
| } | ||
| } | ||
|
|
||
| // Try loading from file:// URL (cached images served to WebView) | ||
| if (imageUrl.startsWith("file://")) { | ||
| try { | ||
| String path = imageUrl.substring("file://".length()); | ||
| Bitmap bitmap = decodeFileScaled(path); | ||
| if (bitmap != null) return bitmap; | ||
| } catch (Exception e) { | ||
| Log.w(TAG, "loadBitmap() file URL load failed", e); | ||
| } | ||
| } | ||
|
|
||
| // Fall back to loading from network | ||
| try { | ||
| URL url = new URL(imageUrl); | ||
| try (InputStream is = url.openStream()) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you change this to use the OkhttpClient here? WallabagConnection.createClient() could be reused (automatically reusing the app's global network settings). |
||
| ByteArrayOutputStream buffer = new ByteArrayOutputStream(); | ||
| byte[] chunk = new byte[8192]; | ||
| int n; | ||
| while ((n = is.read(chunk)) != -1) { | ||
| buffer.write(chunk, 0, n); | ||
| } | ||
| return decodeBytesScaled(buffer.toByteArray()); | ||
| } | ||
| } catch (Exception e) { | ||
| Log.w(TAG, "loadBitmap() remote load failed", e); | ||
| } | ||
|
|
||
| return null; | ||
| } | ||
|
|
||
| // Canvas hardware-accelerated draw limit is ~100MB; cap at 4096px (64MB @ ARGB_8888). | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 64 MB is a lot, maybe we need to catch any OOM errors when we use this high resolution? This might be an issue esp. with low-end devices. |
||
| private static final int MAX_BITMAP_DIMENSION = 4096; | ||
|
|
||
| private static Bitmap decodeFileScaled(String path) { | ||
| BitmapFactory.Options options = new BitmapFactory.Options(); | ||
| options.inJustDecodeBounds = true; | ||
| BitmapFactory.decodeFile(path, options); | ||
| options.inSampleSize = computeSampleSize(options.outWidth, options.outHeight); | ||
| options.inJustDecodeBounds = false; | ||
| return BitmapFactory.decodeFile(path, options); | ||
| } | ||
|
|
||
| private static Bitmap decodeBytesScaled(byte[] data) { | ||
| BitmapFactory.Options options = new BitmapFactory.Options(); | ||
| options.inJustDecodeBounds = true; | ||
| BitmapFactory.decodeByteArray(data, 0, data.length, options); | ||
| options.inSampleSize = computeSampleSize(options.outWidth, options.outHeight); | ||
| options.inJustDecodeBounds = false; | ||
| return BitmapFactory.decodeByteArray(data, 0, data.length, options); | ||
| } | ||
|
|
||
| private static int computeSampleSize(int width, int height) { | ||
| int sampleSize = 1; | ||
| while (width > 0 && height > 0 | ||
| && (width / sampleSize > MAX_BITMAP_DIMENSION | ||
| || height / sampleSize > MAX_BITMAP_DIMENSION)) { | ||
| sampleSize *= 2; | ||
| } | ||
| return sampleSize; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| <?xml version="1.0" encoding="utf-8"?> | ||
| <FrameLayout | ||
| xmlns:android="http://schemas.android.com/apk/res/android" | ||
| android:layout_width="match_parent" | ||
| android:layout_height="match_parent" | ||
| android:background="#000000"> | ||
|
|
||
| <com.github.panpf.zoomimage.ZoomImageView | ||
| android:id="@+id/photoView" | ||
| android:layout_width="match_parent" | ||
| android:layout_height="match_parent" /> | ||
|
|
||
| <ProgressBar | ||
| android:id="@+id/progressBar" | ||
| android:layout_width="wrap_content" | ||
| android:layout_height="wrap_content" | ||
| android:layout_gravity="center" | ||
| android:indeterminateTint="@android:color/white" /> | ||
|
|
||
| </FrameLayout> |
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I hope this file did not serve any purpose. |
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should add the
android:configChanges="keyboardHidden|orientation|screenSize"here, analog to the other activities, so that this image view acitvity does not get recreated when we rotate the phone while viewing the image.