From ded271989b233d0dbfa71a27feafdeddcd0c98d4 Mon Sep 17 00:00:00 2001 From: k3b <1374583+k3b@users.noreply.github.com> Date: Mon, 3 Aug 2020 21:21:48 +0200 Subject: [PATCH] Allow show image from Uri that cannot be translated to file (i.e. WhatsApp photo) --- .../ImageDetailActivityViewPager.java | 41 +++++---- .../ImageDetailMetaDialogBuilder.java | 83 +++++++++++++------ .../ImagePagerAdapterFromCursor.java | 79 ++++++++++++------ .../ImagePagerAdapterFromCursorArray.java | 27 +++++- .../res/menu/menu_image_detail_non_file.xml | 52 ++++++++++++ .../metadata/android/en-US/changelogs/48.txt | 1 + 6 files changed, 213 insertions(+), 70 deletions(-) create mode 100644 app/src/main/res/menu/menu_image_detail_non_file.xml diff --git a/app/src/main/java/de/k3b/android/androFotoFinder/imagedetail/ImageDetailActivityViewPager.java b/app/src/main/java/de/k3b/android/androFotoFinder/imagedetail/ImageDetailActivityViewPager.java index 1f5a5bfe..a919eccc 100644 --- a/app/src/main/java/de/k3b/android/androFotoFinder/imagedetail/ImageDetailActivityViewPager.java +++ b/app/src/main/java/de/k3b/android/androFotoFinder/imagedetail/ImageDetailActivityViewPager.java @@ -750,6 +750,8 @@ private void defineMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu_image_detail_locked, menu); LockScreen.removeDangerousCommandsFromMenu(menu); + } else if (null == getCurrentIFile()) { + getMenuInflater().inflate(R.menu.menu_image_detail_non_file, menu); } else { getMenuInflater().inflate(R.menu.menu_image_detail, menu); getMenuInflater().inflate(R.menu.menu_image_commands, menu); @@ -873,24 +875,27 @@ private void onSlideShowNext() { } private void cmdShowDetails(final CharSequence title, final IFile file, final long currentImageId) { - - File missingRoot = getMissingRootDirFileOrNull( - "ImageDetailActivityViewPager.osRenameTo", file.getFile()); - if (missingRoot != null) { - // ask for needed permissions - requestRootUriDialog(missingRoot, title, - new FilePermissionActivity.IOnDirectoryPermissionGrantedHandler() { - @Override - public void afterGrant(FilePermissionActivity activity) { - ((ImageDetailActivityViewPager) activity).cmdShowDetails(title, file, currentImageId); - } - }); - return; + if (file != null) { + File missingRoot = getMissingRootDirFileOrNull( + "ImageDetailActivityViewPager.osRenameTo", file.getFile()); + if (missingRoot != null) { + // ask for needed permissions + requestRootUriDialog(missingRoot, title, + new FilePermissionActivity.IOnDirectoryPermissionGrantedHandler() { + @Override + public void afterGrant(FilePermissionActivity activity) { + ((ImageDetailActivityViewPager) activity).cmdShowDetails(title, file, currentImageId); + } + }); + return; + } } CharSequence countMsg = TagSql.getStatisticsMessage(this, R.string.show_photo, mGalleryContentQuery); - ImageDetailMetaDialogBuilder.createImageDetailDialog(this, file, currentImageId, + Uri uri = (file == null) ? getImageUri() : null; + + ImageDetailMetaDialogBuilder.createImageDetailDialog(this, file, uri, currentImageId, mGalleryContentQuery, mViewPager.getCurrentItem(), countMsg).show(); @@ -1277,6 +1282,14 @@ protected IFile getCurrentIFile() { return null; } + protected Uri getImageUri() { + if ((mViewPager != null) && (mAdapter != null)) { + int itemPosition = mViewPager.getCurrentItem(); + return this.mAdapter.getImageUri(itemPosition); + } + return null; + } + protected String getCurrentAbsolutPath() { IFile file = getCurrentIFile(); if (file != null) return file.getAbsolutePath(); diff --git a/app/src/main/java/de/k3b/android/androFotoFinder/imagedetail/ImageDetailMetaDialogBuilder.java b/app/src/main/java/de/k3b/android/androFotoFinder/imagedetail/ImageDetailMetaDialogBuilder.java index dfe77158..6110124a 100644 --- a/app/src/main/java/de/k3b/android/androFotoFinder/imagedetail/ImageDetailMetaDialogBuilder.java +++ b/app/src/main/java/de/k3b/android/androFotoFinder/imagedetail/ImageDetailMetaDialogBuilder.java @@ -23,6 +23,7 @@ import android.app.AlertDialog; import android.app.Dialog; import android.content.ContentValues; +import android.net.Uri; import android.widget.ScrollView; import android.widget.TextView; @@ -50,26 +51,29 @@ public class ImageDetailMetaDialogBuilder { private static final String NL = "\n"; - public static Dialog createImageDetailDialog(Activity context, IFile file, long imageId, + public static Dialog createImageDetailDialog(Activity context, IFile file, Uri imageUri, long imageId, QueryParameter query, long offset, Object... moreBlocks) { StringBuilder result = new StringBuilder(); + + Object fileId = (file == null) ? imageUri : file; + if (fileId == null) fileId = ""; result .append(imageId) - .append(":").append(file) + .append(":").append(fileId) .append("\n"); - appendExifInfo(result, context, file, imageId); + appendExifInfo(context, result, file, imageUri, imageId); appendQueryInfo(result, query, offset); if ((moreBlocks != null) && (moreBlocks.length > 0)) { for (Object subBlock : moreBlocks) { if (subBlock != null) { - append(result,"\n"); - append(result, subBlock.toString()); + append(result, "\n"); + append(result, fileId.toString()); } } } - return createImageDetailDialog(context, file.toString(), result.toString()); + return createImageDetailDialog(context, fileId.toString(), result.toString()); } public static Dialog createImageDetailDialog(Activity context, String title, String block, Object... moreBlocks) { @@ -125,15 +129,17 @@ private static void append(StringBuilder result, String block) { + TagSql.SQL_COL_LAST_MODIFIED + ",").toLowerCase(); - private static void appendExifInfo(StringBuilder result, Activity context, IFile jpegFile, long currentImageId) { + private static void appendExifInfo(Activity context, StringBuilder result, IFile jpegFile, Uri imageUri, long currentImageId) { try { - getExifInfo_android(result, jpegFile); + getExifInfo_android(context, result, jpegFile, imageUri); - addExif(result, jpegFile); + addExif(context, result, jpegFile, imageUri); - // #84 show long and short xmp file - addXmp(result, XmpFile.getSidecar(jpegFile, true)); - addXmp(result, XmpFile.getSidecar(jpegFile, false)); + if (jpegFile != null) { + // #84 show long and short xmp file + addXmp(result, XmpFile.getSidecar(jpegFile, true)); + addXmp(result, XmpFile.getSidecar(jpegFile, false)); + } if (currentImageId != 0) { @@ -142,7 +148,7 @@ private static void appendExifInfo(StringBuilder result, Activity context, IFile result.append(NL).append(line).append(NL); result.append(NL).append(TagSql.SQL_TABLE_EXTERNAL_CONTENT_URI_FILE).append(NL).append(NL); // sort by keys - List sortedKeys=new ArrayList(dbContent.keySet()); + List sortedKeys = new ArrayList(dbContent.keySet()); Collections.sort(sortedKeys); for (String key : sortedKeys) { Object value = dbContent.get(key); @@ -184,16 +190,28 @@ private static void appendDate(StringBuilder result, Object value, int factor) { private static String line = "------------------"; - private static void addExif(StringBuilder builder, IFile file) throws IOException { - if (file.exists()) { - builder.append(NL).append(file).append(NL).append(NL); - - PhotoPropertiesImageReader meta = new PhotoPropertiesImageReader().load( + private static void addExif(Activity context, StringBuilder builder, IFile file, Uri imageUri) throws IOException { + PhotoPropertiesImageReader meta = null; + Object fileId = file; + if (file != null && file.exists()) { + meta = new PhotoPropertiesImageReader().load( file, file.openInputStream(), null, "ImageDetailMetaDialogBuilder"); - if (meta != null) builder.append(meta.toString()); + } else if (imageUri != null) { + fileId = imageUri; + meta = new PhotoPropertiesImageReader().load( + (IFile) null, + context.getContentResolver().openInputStream(imageUri), + null, "ImageDetailMetaDialogBuilder(" + + imageUri + ")"); + } + + if (meta != null) { + builder.append(NL).append(fileId).append(NL).append(NL); + + builder.append(meta.toString()); builder.append(NL).append(line).append(NL); } else { - builder.append(NL).append(file).append(" not found.").append(NL); + builder.append(NL).append(fileId).append(" not found.").append(NL); } } @@ -209,13 +227,26 @@ private static void addXmp(StringBuilder builder, IFile file) throws IOException } } - private static void getExifInfo_android(StringBuilder builder, IFile filepath) throws IOException { - ExifInterfaceEx exif = ExifInterfaceEx.create(filepath, null, null, "ImageDetailMetaDialogBuilder.getExifInfo_android"); + private static void getExifInfo_android(Activity context, StringBuilder builder, IFile filepath, Uri imageUri) throws IOException { + Object fileId = filepath; + ExifInterfaceEx exif = null; + if (filepath != null && filepath.exists()) { + exif = ExifInterfaceEx.create(filepath, null, null, "ImageDetailMetaDialogBuilder.getExifInfo_android"); + } else if (imageUri != null) { + fileId = imageUri; + exif = ExifInterfaceEx.create( + (IFile) null, + context.getContentResolver().openInputStream(imageUri), + null, "ImageDetailMetaDialogBuilder.getExifInfo_android(" + + imageUri + ")"); + } - builder.append(NL).append(line).append(NL); - builder.append(NL).append(filepath).append(NL).append(NL); - if (exif.isValidJpgExifFormat()) builder.append(exif.getDebugString(NL)); + if (exif != null) { + builder.append(NL).append(line).append(NL); + builder.append(NL).append(fileId).append(NL).append(NL); + if (exif.isValidJpgExifFormat()) builder.append(exif.getDebugString(NL)); - builder.append(NL).append(line).append(NL); + builder.append(NL).append(line).append(NL); + } } } diff --git a/app/src/main/java/de/k3b/android/androFotoFinder/imagedetail/ImagePagerAdapterFromCursor.java b/app/src/main/java/de/k3b/android/androFotoFinder/imagedetail/ImagePagerAdapterFromCursor.java index b96662e8..9e53b358 100644 --- a/app/src/main/java/de/k3b/android/androFotoFinder/imagedetail/ImagePagerAdapterFromCursor.java +++ b/app/src/main/java/de/k3b/android/androFotoFinder/imagedetail/ImagePagerAdapterFromCursor.java @@ -24,6 +24,7 @@ import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.drawable.Drawable; +import android.net.Uri; import android.support.annotation.NonNull; import android.support.v4.view.PagerAdapter; import android.util.Log; @@ -51,6 +52,7 @@ import de.k3b.android.util.MenuUtils; import de.k3b.android.util.PhotoChangeNotifyer; import de.k3b.android.util.ResourceUtils; +import de.k3b.io.StringUtils; import de.k3b.io.filefacade.FileFacade; import de.k3b.io.filefacade.IFile; import de.k3b.media.PhotoPropertiesBulkUpdateService; @@ -197,10 +199,24 @@ public IFile getFile(int position) { return getiFile(position, fullFilePath, imageID); } - /** translates offset in adapter to id of image */ + /** + * translates offset in adapter to id of image + */ public long getImageId(int position) { Cursor cursor = getCursorAt(position); - return DBUtils.getLong(cursor, FotoSql.SQL_COL_PK,0); + return DBUtils.getLong(cursor, FotoSql.SQL_COL_PK, 0); + } + + /** + * gets uri that belongongs to current image + */ + public Uri getImageUri(int position) { + IFile file = getFullFilePath(position); + String path = (file != null) ? file.getAsUriString() : null; + if (!StringUtils.isNullOrEmpty(path)) { + return Uri.parse(path); + } + return null; } public Date getDatePhotoTaken(int position) { @@ -211,7 +227,7 @@ public Date getDatePhotoTaken(int position) { public boolean hasGeo(int position) { Cursor cursor = getCursorAt(position); - return !DBUtils.isNull(cursor,FotoSql.SQL_COL_GPS,true); + return !DBUtils.isNull(cursor, FotoSql.SQL_COL_GPS, true); } /** @@ -244,7 +260,7 @@ public View instantiateItem(ViewGroup container, int position) { IFile file = getFile(position); // , fullPhotoPath); if (file != null) { - return createViewWithContent(position, container, file, "instantiateItemFromCursor(#", size); + return createViewWithContent(position, container, file, null, "instantiateItemFromCursor(#", size); } } @@ -284,7 +300,7 @@ public int getPositionFromPath(IFile file) { @NonNull protected View createViewWithContent( - int position, ViewGroup container, IFile imageFile, String debugContext, int size) { + int position, ViewGroup container, IFile imageFile, Uri imageUri, String debugContext, int size) { final Context context = container.getContext(); final boolean useLayout = true; @@ -323,31 +339,40 @@ protected View createViewWithContent( String loadType; - // if image is big use memoryefficient, fast, low-quality thumbnail (old code) - if (size > Global.imageDetailThumbnailIfBiggerThan) { - loadType = "image too big using thumb "; - setImageFromThumbnail(photoView, imageFile); - } else { - try { - // #53 Optimisation: no need for thumbnail - saves cache memory but may throw OutOfMemoryError - loadType = "image small enough "; - Bitmap bitmap = HugeImageLoader.loadImage(imageFile, MAX_IMAGE_DIMENSION, MAX_IMAGE_DIMENSION); - // rotation is done by photoView - photoView.setImageBitmap(bitmap); - photoView.setImageReloadFile((IFile) null); - photoView.setDebugPrefix(imageFile.getName()); - } catch (OutOfMemoryError err) { - loadType = "small image out of memory using thumb "; + if (imageFile != null) { + // if image is big use memoryefficient, fast, low-quality thumbnail (old code) + if (size > Global.imageDetailThumbnailIfBiggerThan) { + loadType = "image too big using thumb "; setImageFromThumbnail(photoView, imageFile); + } else { + try { + // #53 Optimisation: no need for thumbnail - saves cache memory but may throw OutOfMemoryError + loadType = "image small enough "; + Bitmap bitmap = HugeImageLoader.loadImage(imageFile, MAX_IMAGE_DIMENSION, MAX_IMAGE_DIMENSION); + // rotation is done by photoView + photoView.setImageBitmap(bitmap); + photoView.setImageReloadFile((IFile) null); + photoView.setDebugPrefix(imageFile.getName()); + } catch (OutOfMemoryError err) { + loadType = "small image out of memory using thumb "; + setImageFromThumbnail(photoView, imageFile); + } } + final int rotationInDegrees = PhotoPropertiesBulkUpdateService.getRotationFromExifOrientation(imageFile, null); + if (Global.debugEnabledViewItem) { + Log.i(Global.LOG_CONTEXT, mDebugPrefix + debugContext + position + ", rotation=" + + rotationInDegrees + ", " + + loadType + ") => " + imageFile + " => " + photoView); + } + photoView.setRotationTo(rotationInDegrees); + } else if (imageUri != null) { + if (Global.debugEnabledViewItem) { + Log.i(Global.LOG_CONTEXT, mDebugPrefix + debugContext + ") => " + imageUri + " => " + photoView); + } + photoView.setImageURI(imageUri); + } else if (Global.debugEnabledViewItem) { + Log.w(Global.LOG_CONTEXT, mDebugPrefix + debugContext + ") no ifile and no imageUri " + photoView); } - final int rotationInDegrees = PhotoPropertiesBulkUpdateService.getRotationFromExifOrientation(imageFile, null); - if (Global.debugEnabledViewItem) { - Log.i(Global.LOG_CONTEXT, mDebugPrefix + debugContext + position +", rotation=" + - rotationInDegrees + ", " - + loadType + ") => " + imageFile + " => " + photoView); - } - photoView.setRotationTo(rotationInDegrees); container.addView(root, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); return root; diff --git a/app/src/main/java/de/k3b/android/androFotoFinder/imagedetail/ImagePagerAdapterFromCursorArray.java b/app/src/main/java/de/k3b/android/androFotoFinder/imagedetail/ImagePagerAdapterFromCursorArray.java index b609d302..da469db0 100644 --- a/app/src/main/java/de/k3b/android/androFotoFinder/imagedetail/ImagePagerAdapterFromCursorArray.java +++ b/app/src/main/java/de/k3b/android/androFotoFinder/imagedetail/ImagePagerAdapterFromCursorArray.java @@ -91,7 +91,9 @@ private IFile getLocalFullFilePath(int position) { return null; } - /** translates offset in adapter to id of image */ + /** + * translates offset in adapter to id of image + */ @Override public long getImageId(int position) { if (imageUri != null) return -1; @@ -99,6 +101,20 @@ public long getImageId(int position) { return super.getImageId(position); } + /** + * gets uri that belongongs to current image + */ + @Override + public Uri getImageUri(int position) { + Uri uri = super.getImageUri(position); + + if ((uri == null) && (this.imageUri != null)) { + uri = this.imageUri; + } + + return uri; + } + @Override public Date getDatePhotoTaken(int position) { if (imageUri != null) return null; @@ -111,11 +127,16 @@ public View instantiateItem(ViewGroup container, int position) { IFile file = getFile(position); if (file != null) { // special case image from ".nomedia" folder via absolute path not via content: uri - return createViewWithContent(position, container, file, "instantiateItemFromArray(#", 32767); + return createViewWithContent(position, container, file, null, "instantiateItemFromArray(#", 32767); + } + + if (this.imageUri != null) { + // special case where uri exists and cannot be translated to file uri. + return createViewWithContent(position, container, null, this.imageUri, "instantiateItemFromArray(" + imageUri, 32767); } // no array avaliable. Use original cursor baed implementation - return super.instantiateItem(container,position); + return super.instantiateItem(container, position); } /** internal helper. return -1 if position is not available */ @Override diff --git a/app/src/main/res/menu/menu_image_detail_non_file.xml b/app/src/main/res/menu/menu_image_detail_non_file.xml new file mode 100644 index 00000000..138b5bcc --- /dev/null +++ b/app/src/main/res/menu/menu_image_detail_non_file.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + diff --git a/fastlane/metadata/android/en-US/changelogs/48.txt b/fastlane/metadata/android/en-US/changelogs/48.txt index 5e0c703e..e9e07491 100644 --- a/fastlane/metadata/android/en-US/changelogs/48.txt +++ b/fastlane/metadata/android/en-US/changelogs/48.txt @@ -2,6 +2,7 @@ Changes from 0.8.3 to ??? * #169: Add sd-card-saf-write-permission support * #173: incremental media scanner +* Show image with uri that cannot be translated to file (i.e. WhatsApp show photo in gallery) * version bumbs android-9 (sdk-28), gradle-6.1.1, osmdroid-6.1.6 mapsforge:0.13.0 * new translation eu (Basque) * translation updates de, pt-BR