-
Notifications
You must be signed in to change notification settings - Fork 34
feat(chat): force gallery refresh on attach open and add quick-send popup #95
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
Changes from all commits
3c258b7
89f468a
be9c835
867f45d
00b3ab3
0e1a0c1
6c84cb7
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 |
|---|---|---|
|
|
@@ -360,6 +360,7 @@ | |
| import xyz.nextalone.nnngram.helpers.TranslateHelper.Status; | ||
| import xyz.nextalone.nnngram.translate.LanguageDetectorTimeout; | ||
| import xyz.nextalone.nnngram.ui.BackButtonRecentMenu; | ||
| import xyz.nextalone.nnngram.ui.QuickSendMediaPopup; | ||
| import xyz.nextalone.nnngram.ui.TranslatorSettingsPopupWrapper; | ||
| import xyz.nextalone.nnngram.ui.sortList.items.TextStyleItems; | ||
| import xyz.nextalone.nnngram.utils.Defines; | ||
|
|
@@ -507,6 +508,9 @@ public class ChatActivity extends BaseFragment implements | |
| private ChatNotificationsPopupWrapper chatNotificationsPopupWrapper; | ||
| // private ChatActivitySideControlsButtonsLayout topButtonsLayout; | ||
| private ChatActivitySideControlsButtonsLayout sideControlsButtonsLayout; | ||
| private QuickSendMediaPopup quickSendMediaPopup; | ||
| private long quickSendPauseTimeSec; | ||
| private final java.util.HashSet<Long> quickSendDismissedIds = new java.util.HashSet<>(); | ||
| private ActionBarMenuItem.Item hideTitleItem; | ||
| /* private float pagedownButtonEnterProgress; | ||
| private float searchUpDownEnterProgress; | ||
|
|
@@ -7353,6 +7357,12 @@ public void setAlpha(float alpha) { | |
| sideControlsButtonsLayout.setOnLongClickListener(this::onSideControlButtonOnLongClick); | ||
| contentView.addView(sideControlsButtonsLayout, LayoutHelper.createFrame(57, 300, Gravity.RIGHT | Gravity.BOTTOM)); | ||
|
|
||
| quickSendMediaPopup = new QuickSendMediaPopup(context); | ||
| // Sits at the bottom-right of the chat, right-aligned with the | ||
| // scroll-to-bottom button and just above it, so it reads as a | ||
| // proper bottom-right affordance above the input bar. | ||
| contentView.addView(quickSendMediaPopup, LayoutHelper.createFrame(72, 72, Gravity.RIGHT | Gravity.BOTTOM, 0, 0, 6, 60)); | ||
|
|
||
| contentView.addView(topPanelLayout, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP)); | ||
|
|
||
| updateMessageListAccessibilityVisibility(); | ||
|
|
@@ -11109,6 +11119,9 @@ private void updatePagedownButtonsPosition() { | |
| - getTopicTabsSideSize(TopicsTabsView.Position.BOTTOM) | ||
| - dp(ChatInputViewsContainer.INPUT_BUBBLE_BOTTOM + 4); | ||
| sideControlsButtonsLayout.setTranslationY(baseTranslationY2); | ||
| if (quickSendMediaPopup != null) { | ||
| quickSendMediaPopup.setTranslationY(baseTranslationY2); | ||
| } | ||
| } | ||
|
|
||
| if (suggestEmojiPanel != null) { | ||
|
|
@@ -29757,6 +29770,8 @@ public void onShow(Bulletin bulletin) { | |
| if (starReactionsOverlay != null) { | ||
| starReactionsOverlay.bringToFront(); | ||
| } | ||
|
|
||
| checkQuickSendMediaPopup(); | ||
| } | ||
|
|
||
| public float getPullingDownOffset() { | ||
|
|
@@ -29772,6 +29787,157 @@ public void checkAdjustResize() { | |
| } | ||
| } | ||
|
|
||
| private void checkQuickSendMediaPopup() { | ||
| if (!Config.quickSendMediaPopup || quickSendMediaPopup == null) { | ||
| return; | ||
| } | ||
| if (chatMode != 0 || inPreviewMode || inBubbleMode || isReport() || isInsideContainer) { | ||
| return; | ||
| } | ||
| if (getParentActivity() == null || currentEncryptedChat != null) { | ||
| // Skip secret chats to avoid surprising users with a separate send path. | ||
| return; | ||
| } | ||
| if (currentChat != null && !ChatObject.canSendPhoto(currentChat)) { | ||
| return; | ||
| } | ||
| if (!xyz.nextalone.nnngram.utils.PermissionUtils.isImagesPermissionGranted()) { | ||
| return; | ||
| } | ||
| final long sinceSec; | ||
| if (quickSendPauseTimeSec > 0) { | ||
| // Only show photos added while we were away from this chat. | ||
| sinceSec = quickSendPauseTimeSec; | ||
| } else { | ||
|
Comment on lines
+29807
to
+29814
|
||
| // First time entering: look back 3 minutes for a freshly taken shot. | ||
| sinceSec = System.currentTimeMillis() / 1000L - 180L; | ||
| } | ||
| final long persistedDismissedId = xyz.nextalone.nnngram.config.ConfigManager | ||
| .getLongOrDefault(xyz.nextalone.nnngram.utils.Defines.quickSendMediaLastDismissedId, 0L); | ||
| QuickSendMediaPopup.queryLatestImage(sinceSec, entry -> { | ||
| if (entry == null || quickSendMediaPopup == null || paused) { | ||
| return; | ||
| } | ||
| if (entry.id <= persistedDismissedId) { | ||
| // Previously dismissed across sessions - MediaStore imageIds increase | ||
| // monotonically, so anything at or below the watermark is old news. | ||
| return; | ||
| } | ||
| if (quickSendDismissedIds.contains(entry.id)) { | ||
| return; | ||
| } | ||
| if (quickSendMediaPopup.isShowingFor(entry.id)) { | ||
| return; | ||
| } | ||
| // The photo is about to be surfaced once; persist the dismissal | ||
| // watermark immediately so that *any* exit path (send, cancel | ||
| // from the preview, 10s timeout, leaving the chat, process | ||
| // restart) counts as "already seen" and the same image is not | ||
| // surfaced a second time. | ||
| rememberQuickSendDismissed(entry); | ||
| quickSendMediaPopup.show(entry, new QuickSendMediaPopup.Delegate() { | ||
| @Override | ||
| public void onSend(QuickSendMediaPopup.QuickSendMediaEntry e) { | ||
| openQuickSendPreview(e); | ||
| } | ||
|
|
||
| @Override | ||
| public void onUserDismiss(QuickSendMediaPopup.QuickSendMediaEntry e) { | ||
| // Already persisted on show(); nothing extra to do. | ||
| } | ||
| }); | ||
| }); | ||
| } | ||
|
|
||
| private void rememberQuickSendDismissed(QuickSendMediaPopup.QuickSendMediaEntry e) { | ||
| if (e == null) return; | ||
| quickSendDismissedIds.add(e.id); | ||
| if (e.dateAdded > 0) { | ||
| quickSendPauseTimeSec = Math.max(quickSendPauseTimeSec, e.dateAdded); | ||
| } | ||
| long prev = xyz.nextalone.nnngram.config.ConfigManager | ||
| .getLongOrDefault(xyz.nextalone.nnngram.utils.Defines.quickSendMediaLastDismissedId, 0L); | ||
| if (e.id > prev) { | ||
| xyz.nextalone.nnngram.config.ConfigManager | ||
| .putLong(xyz.nextalone.nnngram.utils.Defines.quickSendMediaLastDismissedId, e.id); | ||
| } | ||
| } | ||
|
|
||
| private void openQuickSendPreview(QuickSendMediaPopup.QuickSendMediaEntry e) { | ||
| if (e == null || getParentActivity() == null) { | ||
| return; | ||
| } | ||
| if (!checkSlowModeAlert()) { | ||
| return; | ||
| } | ||
| final String path = e.path; | ||
| if (path == null) { | ||
| // No file path (rare, scoped-storage edge case) - fall back to direct send via URI. | ||
| sendQuickMediaEntry(e); | ||
| return; | ||
| } | ||
| Pair<Integer, Integer> orientation = AndroidUtilities.getImageOrientation(path); | ||
| final MediaController.PhotoEntry photoEntry = | ||
| new MediaController.PhotoEntry(0, 0, 0, path, orientation.first, false, 0, 0, 0) | ||
| .setOrientation(orientation); | ||
| final ArrayList<Object> list = new ArrayList<>(); | ||
| list.add(photoEntry); | ||
|
|
||
| PhotoViewer.getInstance().setParentActivity(this, themeDelegate); | ||
| if (PhotoViewer.getInstance().isVisible()) { | ||
| PhotoViewer.getInstance().closePhoto(false, false); | ||
| } | ||
| PhotoViewer.getInstance().openPhotoForSelect(list, 0, 0, false, new PhotoViewer.EmptyPhotoViewerProvider() { | ||
| @Override | ||
| public void sendButtonPressed(int index, VideoEditedInfo videoEditedInfo, boolean notify, int scheduleDate, int scheduleRepeatPeriod, boolean forceDocument) { | ||
| // User confirmed from the preview: persist dismissal and send. | ||
| rememberQuickSendDismissed(e); | ||
| sendMedia(photoEntry, videoEditedInfo, notify, scheduleDate, scheduleRepeatPeriod, forceDocument, 0); | ||
| } | ||
|
|
||
| @Override | ||
| public boolean canScrollAway() { | ||
| return false; | ||
| } | ||
|
|
||
| @Override | ||
| public boolean canCaptureMorePhotos() { | ||
| return false; | ||
| } | ||
| }, this); | ||
| } | ||
|
|
||
| private void sendQuickMediaEntry(QuickSendMediaPopup.QuickSendMediaEntry e) { | ||
| if (e == null || (e.path == null && e.uri == null)) { | ||
| return; | ||
| } | ||
| if (!checkSlowModeAlert()) { | ||
| return; | ||
| } | ||
| rememberQuickSendDismissed(e); | ||
| SendMessagesHelper.prepareSendingPhoto( | ||
| getAccountInstance(), | ||
| e.path, | ||
| e.uri, | ||
| dialog_id, | ||
| replyingMessageObject, | ||
| getThreadMessage(), | ||
| replyingQuote, | ||
| null, | ||
| null, | ||
| null, | ||
| null, | ||
| 0, | ||
| null, | ||
| true, | ||
| 0, | ||
| chatMode, | ||
| quickReplyShortcut, | ||
| getQuickReplyId() | ||
| ); | ||
| afterMessageSend(); | ||
| } | ||
|
|
||
| @Override | ||
| public void finishFragment() { | ||
| super.finishFragment(); | ||
|
|
@@ -29840,6 +30006,10 @@ public void onPause() { | |
| if (contentView != null) { | ||
| contentView.onPause(); | ||
| } | ||
| if (quickSendMediaPopup != null) { | ||
| quickSendMediaPopup.dismiss(false); | ||
| } | ||
| quickSendPauseTimeSec = System.currentTimeMillis() / 1000L; | ||
| if (chatMode == 0 || chatMode == MODE_SAVED && getUserConfig().getClientUserId() == getSavedDialogId() || chatMode == MODE_SUGGESTIONS && ChatObject.isMonoForum(currentChat)) { | ||
| saveDraft(); | ||
| getMessagesController().cancelTyping(0, dialog_id, threadMessageId); | ||
|
|
||
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.
quickSendDismissedIds is an unbounded HashSet that accumulates every sent/dismissed MediaStore ID for the lifetime of the ChatActivity. Over long sessions this can grow without limit; consider capping it (e.g., LRU of last N IDs) or clearing it when leaving the chat / after a successful send.