Giter Site home page Giter Site logo

photocompare's Introduction

Basic structure

ListAllImageFoldersActivity

Lists all image media folders, using

getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI);

It's important to note that the Media external/internal URI has nothing to do with storage location, but actually refer to internal = private to the app, and external = public folders

ListImagesInFolderActivity

CompareImagesActivity

  • Has two instances of the layout/view_image_details.xml, along with a widget.Switch to en-/disable sync between the two
  • Manages a list of selected images per folder (see "Data and file storage overview") ** Image selection is NOT a cache, as it cannot be rebuilt from other information

The main actions are

  • Handle image swiping with a ViewPager, disallowing the index currently in use on the other ViewPager
  • Sync zoom/pan to other view
  • Manage image selection
  • Provide actions to share selected images

The "pan and zoom listener" on a PhotoView is toggled in two cases:

  • During loading of a new image ** Disabled when starting to load a new high-resolution image ** Re-enabled once in the onApplyZoomPanMatrix() implementation of the ImageDetailViewImpl
  • When applying a display matrix ** Disabled at the start of PhotoViewMediator.copyPanAndZoom() ** Re-enabled in the finally block of above method

Synchronizing pan and zoom

When a user changes pan/zoom on one of the images:

  1. The SubsamplingScaleImageView triggers the registered StateChangedListener; this will be a ImageViewListener
  2. The ImageViewListener notifies all registered ImageViewEventListener a. The CrossViewEventHandler sync's pan/zoom to the other image. There will be 2 instances, one sync'ing top to bottom, and one bottom to top. b. The ZoomPanRestoreHandler remembers pan/zoom so those settings can be applied to the next image when swiping
  3. The CrossViewEventHandler calls PhotoViewMediator.onPanOrZoomChanged()
  4. The PhotoViewMediator checks if synchronization is currently active or not. a. If not active, the PhotoViewMediator updates its internal offsets b. If active, the PhotoViewMediator maps the pan/zoom to the other ImageDetailView
  5. ImageDetailView receives a setPanAndZoomState() with the new settings

Relevant points to be aware of:

  • SubsamplingScaleImageView does not enfore maxScale when using setScaleAndCenter(), but does so on user interaction

SelectedImagesActivity

The main actions are

  • Remove images from selection
  • Share images

Libraries

The only non-Android library used are Glide and Subsampling Scale Image View.

Subsampling Scale Image View

Based on 06. State of the documentation, view state synchronization is based on OnImageEventListener (basic image readiness) and OnStateChangedListener (pan and zoom). The class

ImageViewListener implements SubsamplingScaleImageView.OnImageEventListener, SubsamplingScaleImageView.OnStateChangedListener 

wraps these two, and passes the events to our own ImageViewEventListener, so that multiple listeners can react to event.

Fling

SubsamplingScaleImageView sets a GestureDetector internally, which runs if panEnabled is true. The resulting pan has ORIGIN_FLING, thus it's important that ImageViewListener does not restrict event handling on "origin".

Glide / PhotoView / ViewPager and OOM

According to this github issue,

If you want to zoom an image you need to tell Glide to load a bigger image, otherwise it'll be blurry: .override(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL).

However the ViewPager will load 1 off-screen view per side, ie with a top/bottom view that 6 "original size" images in memory. An image from a EOS 77D is 7-9MB, ie for images alone a heap of around 50MB is required. When scrolling, garbage collection of destroyed views might be a bit delayed, thus increasing required heap size yet even more.

According to this stackoverflow, the standard heap size on an Android device is in the area of 16-24 MB, while android:largeHeap="true" increases that to 48-128MB.

Resources used for this app

photocompare's People

Contributors

sniederb avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

photocompare's Issues

App can't display 64MP images

When attempting to display/compare 64MP images, the image simply doesn't render.

The trace

2020-01-23 09:57:57.795 15064-15064/? E/AndroidRuntime: FATAL EXCEPTION: main
    Process: ch.want.imagecompare, PID: 15064
    java.lang.RuntimeException: Canvas: trying to draw too large(128288256bytes) bitmap.
        at android.view.DisplayListCanvas.throwIfCannotDraw(DisplayListCanvas.java:229)
        at android.view.RecordingCanvas.drawBitmap(RecordingCanvas.java:98)
        at com.bumptech.glide.load.resource.bitmap.GlideBitmapDrawable.draw(GlideBitmapDrawable.java:101)
        at android.widget.ImageView.onDraw(ImageView.java:1360)
        at android.view.View.draw(View.java:20207)
        at android.view.View.updateDisplayListIfDirty(View.java:19082)
        at android.view.View.draw(View.java:19935)
        at android.view.ViewGroup.drawChild(ViewGroup.java:4333)
        at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4112)
        at android.view.View.draw(View.java:20210)
        at androidx.viewpager.widget.ViewPager.draw(ViewPager.java:2426)
        at android.view.View.updateDisplayListIfDirty(View.java:19082)
        at android.view.View.draw(View.java:19935)
        at android.view.ViewGroup.drawChild(ViewGroup.java:4333)
        at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4112)
        at android.view.View.updateDisplayListIfDirty(View.java:19073)

indicates a case of bumptech/glide#3775

Activity stops on Android 11 with heic images

Open a HEIC image in the compare activity, and zoom in. After 1-2s, the activity closes automatically and the app restores to the "list images in folder" activity.

2021-01-07 20:08:22.923 13192-13192/? I/crash_dump32: performing dump of process 11163 (target tid = 12869)
2021-01-07 20:08:23.105 13192-13192/? A/DEBUG: *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
2021-01-07 20:08:23.107 13192-13192/? A/DEBUG: Build fingerprint: 'google/sdk_gphone_x86_arm/generic_x86_arm:11/RSR1.201013.001/6903271:userdebug/dev-keys'
2021-01-07 20:08:23.108 13192-13192/? A/DEBUG: Revision: '0'
2021-01-07 20:08:23.108 13192-13192/? A/DEBUG: ABI: 'x86'
2021-01-07 20:08:23.119 13192-13192/? A/DEBUG: Timestamp: 2021-01-07 20:08:23+0100
2021-01-07 20:08:23.119 13192-13192/? A/DEBUG: pid: 11163, tid: 12869, name: HeifDecode  >>> ch.want.imagecompare <<<
2021-01-07 20:08:23.119 13192-13192/? A/DEBUG: uid: 10153
2021-01-07 20:08:23.119 13192-13192/? A/DEBUG: signal 6 (SIGABRT), code -1 (SI_QUEUE), fault addr --------
2021-01-07 20:08:23.120 13192-13192/? A/DEBUG: Abort message: 'decStrong() called on 0xc7b85cf0 too many times'
2021-01-07 20:08:23.120 13192-13192/? A/DEBUG:     eax 00000000  ebx 00002b9b  ecx 00003245  edx 00000006
2021-01-07 20:08:23.121 13192-13192/? A/DEBUG:     edi f4a5681e  esi c3ad09c0
2021-01-07 20:08:23.121 13192-13192/? A/DEBUG:     ebp f7517b90  esp c3ad0968  eip f7517b99
2021-01-07 20:08:23.348 13192-13192/? A/DEBUG: backtrace:
2021-01-07 20:08:23.348 13192-13192/? A/DEBUG:       #00 pc 00000b99  [vdso] (__kernel_vsyscall+9)
2021-01-07 20:08:23.351 13192-13192/? A/DEBUG:       #01 pc 0005ad68  /apex/com.android.runtime/lib/bionic/libc.so (syscall+40) (BuildId: 6e3a0180fa6637b68c0d181c343e6806)
2021-01-07 20:08:23.351 13192-13192/? A/DEBUG:       #02 pc 00076511  /apex/com.android.runtime/lib/bionic/libc.so (abort+209) (BuildId: 6e3a0180fa6637b68c0d181c343e6806)
2021-01-07 20:08:23.352 13192-13192/? A/DEBUG:       #03 pc 00639a4d  /apex/com.android.art/lib/libart.so (art::Runtime::Abort(char const*)+2477) (BuildId: 8191579dfafff37a5cbca70f9a73020f)
2021-01-07 20:08:23.352 13192-13192/? A/DEBUG:       #04 pc 00025a23  /apex/com.android.art/lib/libartbase.so (std::__1::__function::__func<void (*)(char const*), std::__1::allocator<void (*)(char const*)>, void (char const*)>::operator()(char const*&&)+35) (BuildId: 41e9e0cbb5db4bb6875333d66af6569f)
2021-01-07 20:08:23.352 13192-13192/? A/DEBUG:       #05 pc 0001588f  /system/lib/libbase.so (android::base::SetAborter(std::__1::function<void (char const*)>&&)::$_3::__invoke(char const*)+79) (BuildId: 3abc3ce4c3b633a64b14c50cb931a64b)
2021-01-07 20:08:23.363 13192-13192/? A/DEBUG:       #06 pc 00006dbd  /system/lib/liblog.so (__android_log_assert+285) (BuildId: bbac430fc6349b937996bb914e70c060)
2021-01-07 20:08:23.363 13192-13192/? A/DEBUG:       #07 pc 000102a2  /system/lib/libutils.so (android::RefBase::decStrong(void const*) const+146) (BuildId: ab4be013cda31e8c45d48aa23a89d0f8)
2021-01-07 20:08:23.363 13192-13192/? A/DEBUG:       #08 pc 00004de4  /system/lib/libheif.so (android::HeifDecoderImpl::decodeAsync()+436) (BuildId: 49a068f457bf8577f622fb97089c3c5d)
2021-01-07 20:08:23.364 13192-13192/? A/DEBUG:       #09 pc 00004c23  /system/lib/libheif.so (android::HeifDecoderImpl::DecodeThread::threadLoop()+35) (BuildId: 49a068f457bf8577f622fb97089c3c5d)
2021-01-07 20:08:23.364 13192-13192/? A/DEBUG:       #10 pc 00015116  /system/lib/libutils.so (android::Thread::_threadLoop(void*)+374) (BuildId: ab4be013cda31e8c45d48aa23a89d0f8)
2021-01-07 20:08:23.365 13192-13192/? A/DEBUG:       #11 pc 00098fee  /system/lib/libandroid_runtime.so (android::AndroidRuntime::javaThreadShell(void*)+174) (BuildId: 588f2cd5873ff4273bb25b25edb82606)
2021-01-07 20:08:23.367 13192-13192/? A/DEBUG:       #12 pc 000147d9  /system/lib/libutils.so (thread_data_t::trampoline(thread_data_t const*)+457) (BuildId: ab4be013cda31e8c45d48aa23a89d0f8)
2021-01-07 20:08:23.371 13192-13192/? A/DEBUG:       #13 pc 000e6974  /apex/com.android.runtime/lib/bionic/libc.so (__pthread_start(void*)+100) (BuildId: 6e3a0180fa6637b68c0d181c343e6806)
2021-01-07 20:08:23.373 13192-13192/? A/DEBUG:       #14 pc 00078567  /apex/com.android.runtime/lib/bionic/libc.so (__start_thread+71) (BuildId: 6e3a0180fa6637b68c0d181c343e6806)

This seems to only occur for HEIF (High Efficiency Image File Format) images, in HeifDecode:

    runtime.cc:655] Aborting thread:
    runtime.cc:655] "HeifDecode" prio=6 tid=28 Native
    runtime.cc:655]   | group="" sCount=0 dsCount=0 flags=0 obj=0x12e40020 self=0xe9018810
    runtime.cc:655]   | sysTid=12869 nice=-2 cgrp=top-app sched=0/0 handle=0xc3ad11e0
    runtime.cc:655]   | state=R schedstat=( 584296482 550657651 241 ) utm=34 stm=24 core=3 HZ=100
    runtime.cc:655]   | stack=0xc39d6000-0xc39d8000 stackSize=1008KB
    runtime.cc:655]   | held mutexes= "abort lock"
    runtime.cc:655]   native: #00 pc 00542d9e  /apex/com.android.art/lib/libart.so (art::DumpNativeStack(std::__1::basic_ostream<char, std::__1::char_traits<char> >&, int, BacktraceMap*, char const*, art::ArtMethod*, void*, bool)+110)
    runtime.cc:655]   native: #01 pc 006a0897  /apex/com.android.art/lib/libart.so (art::Thread::DumpStack(std::__1::basic_ostream<char, std::__1::char_traits<char> >&, bool, BacktraceMap*, bool) const+1015)
    runtime.cc:655]   native: #02 pc 0069a171  /apex/com.android.art/lib/libart.so (art::Thread::Dump(std::__1::basic_ostream<char, std::__1::char_traits<char> >&, bool, BacktraceMap*, bool) const+65)
    runtime.cc:655]   native: #03 pc 006522c5  /apex/com.android.art/lib/libart.so (art::AbortState::DumpThread(std::__1::basic_ostream<char, std::__1::char_traits<char> >&, art::Thread*) const+53)
    runtime.cc:655]   native: #04 pc 00639abb  /apex/com.android.art/lib/libart.so (art::Runtime::Abort(char const*)+2587)
    runtime.cc:655]   native: #05 pc 00025a23  /apex/com.android.art/lib/libartbase.so (std::__1::__function::__func<void (*)(char const*), std::__1::allocator<void (*)(char const*)>, void (char const*)>::operator()(char const*&&)+35)
2021-01-07 20:08:22.411 11163-12869/ch.want.imagecompare A/nt.imagecompar: runtime.cc:655]   native: #06 pc 0001588f  /system/lib/libbase.so (android::base::SetAborter(std::__1::function<void (char const*)>&&)::$_3::__invoke(char const*)+79)
    runtime.cc:655]   native: #07 pc 00006dbd  /system/lib/liblog.so (__android_log_assert+285)
    runtime.cc:655]   native: #08 pc 000102a2  /system/lib/libutils.so (android::RefBase::decStrong(void const*) const+146)
    runtime.cc:655]   native: #09 pc 00004de4  /system/lib/libheif.so (android::HeifDecoderImpl::decodeAsync()+436)
    runtime.cc:655]   native: #10 pc 00004c23  /system/lib/libheif.so (android::HeifDecoderImpl::DecodeThread::threadLoop()+35)
    runtime.cc:655]   native: #11 pc 00015116  /system/lib/libutils.so (android::Thread::_threadLoop(void*)+374)
    runtime.cc:655]   native: #12 pc 00098fee  /system/lib/libandroid_runtime.so (android::AndroidRuntime::javaThreadShell(void*)+174)
    runtime.cc:655]   native: #13 pc 000147d9  /system/lib/libutils.so (thread_data_t::trampoline(thread_data_t const*)+457)
    runtime.cc:655]   native: #14 pc 000e6974  /apex/com.android.runtime/lib/bionic/libc.so (__pthread_start(void*)+100)
    runtime.cc:655]   native: #15 pc 00078567  /apex/com.android.runtime/lib/bionic/libc.so (__start_thread+71)

Feature request: filter image folders

There might be very many folders holding images on a phone, eg. when there is a local music library with album cover images. In such a case, PhotoCompare will display all folders. It would be nice if there were a way to filter image folders.

Less wasted space in photo comparison UI?

More a request than an issue, but could you be bothered to tidy the UI a bit to maximize the physical space for pictures?
Perhaps make the arrows a bit more transparent.
I love this app and use it several times per day, and would love to clear some more UI space for the pictures themselves.

Added a screenshot.

20231211_130912

Doesn't actually delete anything

I select some files, Show selection, Delete selected images, Confirm image delete, and it goes back to the main image selection screen, but the images I deleted still exist.

OnePlus 7 Pro shows image grid with two columns instead of three

The issue cannot be easily reproduced using AVD. This seems to be related either to OnePlus' OxygenOS or to issues with the screen dimensions from the AVD.

OnePlus 7 Pro specs:

  • 6.67 inches, 108.8 cm2
  • 1440 x 3120 pixels (QHD+), 19.5:9 ratio (~516 ppi density)
  • User can choose to switch to FHD+ (1080 x 2336), also OxygenOS might switch automatically!

How to configure android emulator to match actual device's screen? reports that display metrics from AVD don't always match physical devices.

App crash on folders with a large number of images

On any given device, folders with a large number of images can be opened, but choosing an image to start comparison results in an app crash.

07-21 12:21:27.772 E/ActivityManager( 2241): Transaction too large, intent: Intent { cmp=ch.want.imagecompare/.ui.compareimages.CompareImagesActivity (has extras) }, extras size: 896816, icicle size: 0
07-21 12:21:27.773 E/JavaBinder( 2241): !!! FAILED BINDER TRANSACTION !!!  (parcel size = 901120)
07-21 12:21:27.773 E/ActivityManager( 2241): Second failure launching ch.want.imagecompare/.ui.compareimages.CompareImagesActivity, giving up
07-21 12:21:27.773 E/ActivityManager( 2241): android.os.TransactionTooLargeException: data parcel size 901120 bytes
07-21 12:21:27.773 E/ActivityManager( 2241): 	at android.os.BinderProxy.transactNative(Native Method)
07-21 12:21:27.773 E/ActivityManager( 2241): 	at android.os.BinderProxy.transact(Unknown Source:86)
07-21 12:21:27.773 E/ActivityManager( 2241): 	at android.app.IApplicationThread$Stub$Proxy.scheduleTransaction(Unknown Source:28)
07-21 12:21:27.773 E/ActivityManager( 2241): 	at android.app.servertransaction.ClientTransaction.schedule(Unknown Source:2)
07-21 12:21:27.773 E/ActivityManager( 2241): 	at com.android.server.am.ClientLifecycleManager.scheduleTransaction(Unknown Source:4)
07-21 12:21:27.773 E/ActivityManager( 2241): 	at com.android.server.am.ActivityStackSupervisor.realStartActivityLocked(Unknown Source:619)
07-21 12:21:27.773 E/ActivityManager( 2241): 	at com.android.server.am.ActivityStackSupervisor.attachApplicationLocked(Unknown Source:94)
07-21 12:21:27.773 E/ActivityManager( 2241): 	at com.android.server.am.ActivityManagerService.attachApplicationLocked(Unknown Source:1233)
07-21 12:21:27.773 E/ActivityManager( 2241): 	at com.android.server.am.ActivityManagerService.attachApplication(Unknown Source:20)
07-21 12:21:27.773 E/ActivityManager( 2241): 	at android.app.IActivityManager$Stub.onTransact(Unknown Source:6041)
07-21 12:21:27.773 E/ActivityManager( 2241): 	at com.android.server.am.ActivityManagerService.onTransact(Unknown Source:119)
07-21 12:21:27.773 E/ActivityManager( 2241): 	at android.os.Binder.execTransact(Unknown Source:65)
07-21 12:21:27.773 I/ActivityManager( 2241): Process ch.want.imagecompare (pid 26988) has died: fore TOP 

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.