From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: alina sireneva <alina@tei.su>
Date: Sat, 16 May 2026 17:07:55 +0400
Subject: faster gallery handling in attach sheet

---
 .../telegram/messenger/MediaController.java   | 44 +++++++++++++++----
 .../ui/Cells/PhotoAttachPhotoCell.java        |  1 +
 .../ChatAttachAlertPhotoLayout.java           |  1 +
 .../java/org/telegram/ui/PhotoViewer.java     | 15 +++++++
 4 files changed, 53 insertions(+), 8 deletions(-)

diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/MediaController.java b/TMessagesProj/src/main/java/org/telegram/messenger/MediaController.java
index 0000000..0000000 100644
--- a/TMessagesProj/src/main/java/org/telegram/messenger/MediaController.java
+++ b/TMessagesProj/src/main/java/org/telegram/messenger/MediaController.java
@@ -77,7 +77,6 @@ import com.google.android.exoplayer2.C;
 import com.google.android.exoplayer2.ExoPlayer;
 import com.google.android.exoplayer2.Player;
 import com.google.android.exoplayer2.extractor.jpeg.MotionPhotoDescription;
-import com.google.android.exoplayer2.extractor.jpeg.XmpMotionPhotoDescriptionParser;
 import com.google.android.exoplayer2.ui.AspectRatioFrameLayout;
 import com.google.android.gms.cast.MediaMetadata;
 import com.google.android.gms.common.images.WebImage;
@@ -623,7 +622,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
                 final ExifInterface ei = new ExifInterface(new File(path));
                 final String xmp = ei.getAttribute(ExifInterface.TAG_XMP);
                 if (xmp != null) {
-                    final MotionPhotoDescription motionPhoto = XmpMotionPhotoDescriptionParser.parse(xmp);
+                    final MotionPhotoDescription motionPhoto = desu.inugram.helpers.media.GalleryHelper.lookupXmp(imageId, dateTaken, xmp.getBytes());
                     if (motionPhoto != null) {
                         MotionPhotoDescription.ContainerItem photo = null, video = null;
                         for (int i = 0; i < motionPhoto.items.size(); ++i) {
@@ -1241,13 +1240,15 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
 
         private void scheduleReloadRunnable() {
             AndroidUtilities.runOnUIThread(refreshGalleryRunnable = () -> {
-                if (PhotoViewer.getInstance().isVisible()) {
+                if (PhotoViewer.getInstance().isVisible() && !desu.inugram.helpers.media.GalleryHelper.shouldRefreshVisiblePhotoViewer()) {
                     scheduleReloadRunnable();
                     return;
                 }
                 refreshGalleryRunnable = null;
-                loadGalleryPhotosAlbums(0);
-            }, 2000);
+                if (!desu.inugram.helpers.media.GalleryHelper.tryConsumeIncremental(0)) {
+                    loadGalleryPhotosAlbums(0);
+                }
+            }, 200);
         }
 
         @Override
@@ -1258,6 +1259,18 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
             }
             scheduleReloadRunnable();
         }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri, int flags) {
+            // ContentObserver.onChange(boolean, Uri, int) only exists since API 30, but some older
+            // frameworks still dispatch to this signature — super.onChange would NoSuchMethodError there.
+            if (Build.VERSION.SDK_INT < 30) {
+                onChange(selfChange);
+                return;
+            }
+            desu.inugram.helpers.media.GalleryHelper.recordEvent(uri, flags);
+            super.onChange(selfChange, uri, flags);
+        }
     }
 
     private static class GalleryObserverExternal extends ContentObserver {
@@ -1273,8 +1286,22 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
             }
             AndroidUtilities.runOnUIThread(refreshGalleryRunnable = () -> {
                 refreshGalleryRunnable = null;
-                loadGalleryPhotosAlbums(0);
-            }, 2000);
+                if (!desu.inugram.helpers.media.GalleryHelper.tryConsumeIncremental(0)) {
+                    loadGalleryPhotosAlbums(0);
+                }
+            }, 200);
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri, int flags) {
+            // ContentObserver.onChange(boolean, Uri, int) only exists since API 30, but some older
+            // frameworks still dispatch to this signature — super.onChange would NoSuchMethodError there.
+            if (Build.VERSION.SDK_INT < 30) {
+                onChange(selfChange);
+                return;
+            }
+            desu.inugram.helpers.media.GalleryHelper.recordEvent(uri, flags);
+            super.onChange(selfChange, uri, flags);
         }
     }
 
@@ -6359,7 +6386,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
             AndroidUtilities.cancelRunOnUIThread(broadcastPhotosRunnable);
         }
         AndroidUtilities.runOnUIThread(broadcastPhotosRunnable = () -> {
-            if (PhotoViewer.getInstance().isVisible() && !forceBroadcastNewPhotos) {
+            if (PhotoViewer.getInstance().isVisible() && !forceBroadcastNewPhotos && !desu.inugram.helpers.media.GalleryHelper.shouldRefreshVisiblePhotoViewer()) {
                 broadcastNewPhotos(guid, mediaAlbumsSorted, photoAlbumsSorted, cameraAlbumIdFinal, allMediaAlbumFinal, allPhotosAlbumFinal, allVideosAlbumFinal, 1000);
                 return;
             }
@@ -6369,6 +6396,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
             allPhotosAlbumEntry = allPhotosAlbumFinal;
             allMediaAlbumEntry = allMediaAlbumFinal;
             allVideosAlbumEntry = allVideosAlbumFinal;
+            desu.inugram.helpers.media.GalleryHelper.markFullScanComplete(cameraAlbumIdFinal);
             NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.albumsDidLoad, guid, mediaAlbumsSorted, photoAlbumsSorted, cameraAlbumIdFinal);
         }, delay);
     }
diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/PhotoAttachPhotoCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/PhotoAttachPhotoCell.java
index 0000000..0000000 100644
--- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/PhotoAttachPhotoCell.java
+++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/PhotoAttachPhotoCell.java
@@ -512,6 +512,7 @@ public class PhotoAttachPhotoCell extends FrameLayout {
 
     public void setPhotoEntry(MediaController.PhotoEntry entry, boolean selectedMultiple, boolean needCheckShow, boolean last, boolean allowLivePhotos) {
         pressed = false;
+        desu.inugram.helpers.media.GalleryHelper.invalidateThumbnailIfChanged(imageView.getImageReceiver(), photoEntry, entry);
         photoEntry = entry;
         isLast = last;
         this.allowLivePhotos = allowLivePhotos;
diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlertPhotoLayout.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlertPhotoLayout.java
index 0000000..0000000 100644
--- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlertPhotoLayout.java
+++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlertPhotoLayout.java
@@ -4269,6 +4269,7 @@ public class ChatAttachAlertPhotoLayout extends ChatAttachAlert.AttachAlertLayou
                         }
                     }
                 }
+                desu.inugram.helpers.media.GalleryHelper.refreshPhotoViewer(galleryAlbumEntry);
                 updateAlbumsDropDown();
             }
         } else if (id == NotificationCenter.cameraInitied) {
diff --git a/TMessagesProj/src/main/java/org/telegram/ui/PhotoViewer.java b/TMessagesProj/src/main/java/org/telegram/ui/PhotoViewer.java
index 0000000..0000000 100644
--- a/TMessagesProj/src/main/java/org/telegram/ui/PhotoViewer.java
+++ b/TMessagesProj/src/main/java/org/telegram/ui/PhotoViewer.java
@@ -19454,6 +19454,21 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat
         return imagesArrLocals;
     }
 
+    public void inu_reloadCurrentPhoto(MediaController.PhotoEntry entry) {
+        if (currentEditMode != EDIT_MODE_NONE || currentIndex < 0 || currentIndex >= imagesArrLocals.size() || !(imagesArrLocals.get(currentIndex) instanceof MediaController.PhotoEntry)) {
+            return;
+        }
+        applyCaption();
+        entry.copyFrom((MediaController.PhotoEntry) imagesArrLocals.get(currentIndex));
+        imagesArrLocals.set(currentIndex, entry);
+        String key = centerImage.getImageKey();
+        if (key != null) {
+            ImageLoader.getInstance().removeImage(key);
+        }
+        centerImage.clearImage();
+        setImageIndex(currentIndex, false, false, true);
+    }
+
     @Keep
     public void setAnimationValue(float value) {
         animationValue = value;
