Move reaction overlay UI into a stub.

This commit is contained in:
Alex Hart 2021-01-27 16:34:59 -04:00 committed by GitHub
parent f6cd190245
commit 1b448c2bdf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 183 additions and 48 deletions

View file

@ -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> reminderView;
private Stub<UnverifiedBannerView> unverifiedBannerView;
private Stub<ReviewBannerView> 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> reminderView;
private Stub<UnverifiedBannerView> unverifiedBannerView;
private Stub<ReviewBannerView> 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<ConversationReactionOverlay> 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();
}
});
}

View file

@ -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<ConversationReactionOverlay> 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<ConversationReactionOverlay> 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;
}
}

View file

@ -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) {

View file

@ -221,5 +221,15 @@
</org.thoughtcrime.securesms.components.InputAwareLayout>
<include layout="@layout/conversation_reaction_scrubber" />
<ViewStub
android:id="@+id/conversation_reaction_scrubber_stub"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintTop_toTopOf="@+id/status_bar_guideline"
app:layout_constraintBottom_toBottomOf="@+id/navigation_bar_guideline"
app:layout_constraintStart_toStartOf="@+id/parent_start_guideline"
app:layout_constraintEnd_toEndOf="@+id/parent_end_guideline"
android:inflatedId="@+id/conversation_reaction_scrubber"
android:layout="@layout/conversation_reaction_scrubber" />
</org.thoughtcrime.securesms.components.InsetAwareConstraintLayout>