diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java index 904c679cdc..ee6dbeadbd 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java @@ -269,7 +269,6 @@ import org.thoughtcrime.securesms.util.SmsUtil; import org.thoughtcrime.securesms.util.SpanUtil; import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.TextSecurePreferences.MediaKeyboardMode; -import org.thoughtcrime.securesms.util.ThemeUtil; import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.ViewUtil; import org.thoughtcrime.securesms.util.WindowUtil; @@ -344,26 +343,26 @@ public class ConversationActivity extends PassphraseRequiredActivity private static final int SMS_DEFAULT = 11; private static final int MEDIA_SENDER = 12; - private GlideRequests glideRequests; - protected ComposeText composeText; - private AnimatingToggle buttonToggle; - private SendButton sendButton; - private ImageButton attachButton; - protected ConversationTitleView titleView; - private TextView charactersLeft; - private ConversationFragment fragment; - private Button unblockButton; - private Button makeDefaultSmsButton; - private Button registerButton; - private InputAwareLayout container; - protected Stub reminderView; - private Stub unverifiedBannerView; - private Stub reviewBanner; - private TypingStatusTextWatcher typingTextWatcher; - private ConversationSearchBottomBar searchNav; - private MenuItem searchViewItem; - private MessageRequestsBottomView messageRequestBottomView; - private ConversationReactionOverlay reactionOverlay; + private GlideRequests glideRequests; + protected ComposeText composeText; + private AnimatingToggle buttonToggle; + private SendButton sendButton; + private ImageButton attachButton; + protected ConversationTitleView titleView; + private TextView charactersLeft; + private ConversationFragment fragment; + private Button unblockButton; + private Button makeDefaultSmsButton; + private Button registerButton; + private InputAwareLayout container; + protected Stub reminderView; + private Stub unverifiedBannerView; + private Stub reviewBanner; + private TypingStatusTextWatcher typingTextWatcher; + private ConversationSearchBottomBar searchNav; + private MenuItem searchViewItem; + private MessageRequestsBottomView messageRequestBottomView; + private ConversationReactionDelegate reactionDelegate; private AttachmentManager attachmentManager; private AudioRecorder audioRecorder; @@ -594,8 +593,8 @@ public class ConversationActivity extends PassphraseRequiredActivity container.hideAttachedInput(true); } - if (reactionOverlay != null && reactionOverlay.isShowing()) { - reactionOverlay.hide(); + if (reactionDelegate.isShowing()) { + reactionDelegate.hide(); } } @@ -608,7 +607,7 @@ public class ConversationActivity extends PassphraseRequiredActivity @Override public boolean dispatchTouchEvent(MotionEvent ev) { - return reactionOverlay.applyTouchEvent(ev) || super.dispatchTouchEvent(ev); + return reactionDelegate.applyTouchEvent(ev) || super.dispatchTouchEvent(ev); } @Override @@ -1030,8 +1029,8 @@ public class ConversationActivity extends PassphraseRequiredActivity @Override public void onBackPressed() { Log.d(TAG, "onBackPressed()"); - if (reactionOverlay.isShowing()) { - reactionOverlay.hide(); + if (reactionDelegate.isShowing()) { + reactionDelegate.hide(); } else if (container.isInputOpen()) { container.hideCurrentInput(composeText); } else { @@ -1919,7 +1918,6 @@ public class ConversationActivity extends PassphraseRequiredActivity panelParent = findViewById(R.id.conversation_activity_panel_parent); searchNav = findViewById(R.id.conversation_search_nav); messageRequestBottomView = findViewById(R.id.conversation_activity_message_request_bottom_bar); - reactionOverlay = findViewById(R.id.conversation_reaction_scrubber); mentionsSuggestions = ViewUtil.findStubById(this, R.id.conversation_mention_suggestions_stub); wallpaper = findViewById(R.id.conversation_wallpaper); wallpaperDim = findViewById(R.id.conversation_wallpaper_dim); @@ -1927,6 +1925,10 @@ public class ConversationActivity extends PassphraseRequiredActivity ImageButton quickCameraToggle = findViewById(R.id.quick_camera_toggle); ImageButton inlineAttachmentButton = findViewById(R.id.inline_attachment_button); + Stub reactionOverlayStub = ViewUtil.findStubById(this, R.id.conversation_reaction_scrubber_stub); + reactionDelegate = new ConversationReactionDelegate(reactionOverlayStub); + + noLongerMemberBanner = findViewById(R.id.conversation_no_longer_member_banner); requestingMemberBanner = findViewById(R.id.conversation_requesting_banner); cancelJoinRequest = findViewById(R.id.conversation_cancel_request); @@ -1984,7 +1986,7 @@ public class ConversationActivity extends PassphraseRequiredActivity inlineAttachmentButton.setOnClickListener(v -> handleAddAttachment()); - reactionOverlay.setOnReactionSelectedListener(this); + reactionDelegate.setOnReactionSelectedListener(this); joinGroupCallButton.setOnClickListener(v -> handleVideo(getRecipient())); } @@ -2212,7 +2214,7 @@ public class ConversationActivity extends PassphraseRequiredActivity public void onReactionSelected(MessageRecord messageRecord, String emoji) { final Context context = getApplicationContext(); - reactionOverlay.hide(); + reactionDelegate.hide(); SignalExecutors.BOUNDED.execute(() -> { ReactionRecord oldRecord = Stream.of(messageRecord.getReactions()) @@ -2238,14 +2240,14 @@ public class ConversationActivity extends PassphraseRequiredActivity if (oldRecord != null && hasAddedCustomEmoji) { final Context context = getApplicationContext(); - reactionOverlay.hide(); + reactionDelegate.hide(); SignalExecutors.BOUNDED.execute(() -> MessageSender.sendReactionRemoval(context, messageRecord.getId(), messageRecord.isMms(), oldRecord)); } else { - reactionOverlay.hideAllButMask(); + reactionDelegate.hideAllButMask(); ReactWithAnyEmojiBottomSheetDialogFragment.createForMessageRecord(messageRecord, reactWithAnyEmojiStartPage) .show(getSupportFragmentManager(), "BOTTOM"); @@ -2254,7 +2256,7 @@ public class ConversationActivity extends PassphraseRequiredActivity @Override public void onReactWithAnyEmojiDialogDismissed() { - reactionOverlay.hideMask(); + reactionDelegate.hideMask(); } @Override @@ -3128,7 +3130,7 @@ public class ConversationActivity extends PassphraseRequiredActivity @Override public void onReactionsDialogDismissed() { - reactionOverlay.hideMask(); + reactionDelegate.hideMask(); } // Listeners @@ -3359,14 +3361,14 @@ public class ConversationActivity extends PassphraseRequiredActivity @NonNull Toolbar.OnMenuItemClickListener toolbarListener, @NonNull ConversationReactionOverlay.OnHideListener onHideListener) { - reactionOverlay.setOnToolbarItemClickedListener(toolbarListener); - reactionOverlay.setOnHideListener(onHideListener); - reactionOverlay.show(this, maskTarget, recipient.get(), messageRecord, inputAreaHeight()); + reactionDelegate.setOnToolbarItemClickedListener(toolbarListener); + reactionDelegate.setOnHideListener(onHideListener); + reactionDelegate.show(this, maskTarget, recipient.get(), messageRecord, inputAreaHeight()); } @Override public void onListVerticalTranslationChanged(float translationY) { - reactionOverlay.setListVerticalTranslation(translationY); + reactionDelegate.setListVerticalTranslation(translationY); } @Override @@ -3386,22 +3388,22 @@ public class ConversationActivity extends PassphraseRequiredActivity @Override public void handleReactionDetails(@NonNull View maskTarget) { - reactionOverlay.showMask(maskTarget, titleView.getMeasuredHeight(), inputAreaHeight()); + reactionDelegate.showMask(maskTarget, titleView.getMeasuredHeight(), inputAreaHeight()); } @Override public void onCursorChanged() { - if (!reactionOverlay.isShowing()) { + if (!reactionDelegate.isShowing()) { return; } SimpleTask.run(() -> { //noinspection CodeBlock2Expr return DatabaseFactory.getMmsSmsDatabase(this) - .checkMessageExists(reactionOverlay.getMessageRecord()); + .checkMessageExists(reactionDelegate.getMessageRecord()); }, messageExists -> { if (!messageExists) { - reactionOverlay.hide(); + reactionDelegate.hide(); } }); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationReactionDelegate.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationReactionDelegate.java new file mode 100644 index 0000000000..3465859dcf --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationReactionDelegate.java @@ -0,0 +1,126 @@ +package org.thoughtcrime.securesms.conversation; + +import android.app.Activity; +import android.graphics.PointF; +import android.view.MotionEvent; +import android.view.View; + +import androidx.annotation.NonNull; +import androidx.appcompat.widget.Toolbar; + +import org.thoughtcrime.securesms.database.model.MessageRecord; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.util.views.Stub; + +/** + * Delegate class that mimics the ConversationReactionOverlay public API + * + * This allows us to properly stub out the ConversationReactionOverlay View class while still + * respecting listeners and other positional information that can be set BEFORE we want to actually + * resolve the view. + */ +final class ConversationReactionDelegate { + + private final Stub overlayStub; + private final PointF lastSeenDownPoint = new PointF(); + + private ConversationReactionOverlay.OnReactionSelectedListener onReactionSelectedListener; + private Toolbar.OnMenuItemClickListener onToolbarItemClickedListener; + private ConversationReactionOverlay.OnHideListener onHideListener; + private float translationY; + + ConversationReactionDelegate(@NonNull Stub overlayStub) { + this.overlayStub = overlayStub; + } + + boolean isShowing() { + return overlayStub.resolved() && overlayStub.get().isShowing(); + } + + void show(@NonNull Activity activity, + @NonNull View maskTarget, + @NonNull Recipient conversationRecipient, + @NonNull MessageRecord messageRecord, + int maskPaddingBottom) + { + resolveOverlay().show(activity, maskTarget, conversationRecipient, messageRecord, maskPaddingBottom, lastSeenDownPoint); + } + + void showMask(@NonNull View maskTarget, int maskPaddingTop, int maskPaddingBottom) { + resolveOverlay().showMask(maskTarget, maskPaddingTop, maskPaddingBottom); + } + + void hide() { + overlayStub.get().hide(); + } + + void hideAllButMask() { + overlayStub.get().hideAllButMask(); + } + + void hideMask() { + overlayStub.get().hideMask(); + } + + void setOnReactionSelectedListener(@NonNull ConversationReactionOverlay.OnReactionSelectedListener onReactionSelectedListener) { + this.onReactionSelectedListener = onReactionSelectedListener; + + if (overlayStub.resolved()) { + overlayStub.get().setOnReactionSelectedListener(onReactionSelectedListener); + } + } + + void setOnToolbarItemClickedListener(@NonNull Toolbar.OnMenuItemClickListener onToolbarItemClickedListener) { + this.onToolbarItemClickedListener = onToolbarItemClickedListener; + + if (overlayStub.resolved()) { + overlayStub.get().setOnToolbarItemClickedListener(onToolbarItemClickedListener); + } + } + + void setOnHideListener(@NonNull ConversationReactionOverlay.OnHideListener onHideListener) { + this.onHideListener = onHideListener; + + if (overlayStub.resolved()) { + overlayStub.get().setOnHideListener(onHideListener); + } + } + + void setListVerticalTranslation(float translationY) { + this.translationY = translationY; + + if (overlayStub.resolved()) { + overlayStub.get().setListVerticalTranslation(translationY); + } + } + + @NonNull MessageRecord getMessageRecord() { + if (!overlayStub.resolved()) { + throw new IllegalStateException("Cannot call getMessageRecord right now."); + } + + return overlayStub.get().getMessageRecord(); + } + + boolean applyTouchEvent(@NonNull MotionEvent motionEvent) { + if (!overlayStub.resolved() || !overlayStub.get().isShowing()) { + if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) { + lastSeenDownPoint.set(motionEvent.getX(), motionEvent.getY()); + } + return false; + } else { + return overlayStub.get().applyTouchEvent(motionEvent); + } + } + + private @NonNull ConversationReactionOverlay resolveOverlay() { + ConversationReactionOverlay overlay = overlayStub.get(); + + overlay.setListVerticalTranslation(translationY); + overlay.setOnHideListener(onHideListener); + overlay.setOnToolbarItemClickedListener(onToolbarItemClickedListener); + overlay.setOnReactionSelectedListener(onReactionSelectedListener); + + return overlay; + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationReactionOverlay.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationReactionOverlay.java index 8ed563704f..5ec83cf092 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationReactionOverlay.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationReactionOverlay.java @@ -56,7 +56,6 @@ public final class ConversationReactionOverlay extends RelativeLayout { private final Boundary horizontalEmojiBoundary = new Boundary(); private final Boundary verticalScrubBoundary = new Boundary(); private final PointF deadzoneTouchPoint = new PointF(); - private final PointF lastSeenDownPoint = new PointF(); private Activity activity; private Recipient conversationRecipient; @@ -149,7 +148,8 @@ public final class ConversationReactionOverlay extends RelativeLayout { @NonNull View maskTarget, @NonNull Recipient conversationRecipient, @NonNull MessageRecord messageRecord, - int maskPaddingBottom) + int maskPaddingBottom, + @NonNull PointF lastSeenDownPoint) { if (overlayState != OverlayState.HIDDEN) { @@ -292,10 +292,7 @@ public final class ConversationReactionOverlay extends RelativeLayout { public boolean applyTouchEvent(@NonNull MotionEvent motionEvent) { if (!isShowing()) { - if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) { - lastSeenDownPoint.set(motionEvent.getX(), motionEvent.getY()); - } - return false; + throw new IllegalStateException("Touch events should only be propagated to this method if we are displaying the scrubber."); } if ((motionEvent.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) != 0) { diff --git a/app/src/main/res/layout/conversation_activity.xml b/app/src/main/res/layout/conversation_activity.xml index 183e423fd8..ab7746c1a5 100644 --- a/app/src/main/res/layout/conversation_activity.xml +++ b/app/src/main/res/layout/conversation_activity.xml @@ -221,5 +221,15 @@ - + +