Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ public void setParentOnMeasureDisabled (boolean disabled) {

@Override
public void performDestroy () {
cancelPendingMessageClick();
avatarReceiver.destroy();
avatarsReceiver.performDestroy();
giveawayAvatarsReceiver.performDestroy();
Expand Down Expand Up @@ -288,6 +289,9 @@ public final int getCurrentHeight () {
}

public void setMessage (TGMessage message) {
if (this.msg != message) {
cancelPendingMessageClick();
}
int desiredHeight = message.getHeight();
int currentHeight = getCurrentHeight();

Expand Down Expand Up @@ -509,6 +513,7 @@ public void onAttachedToRecyclerView () {
}

public void onDetachedFromRecyclerView () {
cancelPendingMessageClick();
if (isAttached) {
isAttached = false;
avatarReceiver.detach();
Expand Down Expand Up @@ -536,6 +541,67 @@ public boolean isAttached () {
}

private float touchX, touchY;
private Runnable pendingMessageClick;
private long pendingMessageClickTime;
private long pendingMessageClickId;
private float pendingMessageClickX, pendingMessageClickY;

private void cancelPendingMessageClick () {
if (pendingMessageClick != null) {
removeCallbacks(pendingMessageClick);
pendingMessageClick = null;
}
pendingMessageClickTime = 0;
pendingMessageClickId = 0;
}

private boolean handleMessageTap (float x, float y, long eventTime) {
if (msg == null || !Settings.instance().isQuickReactionDoubleTapEnabled() || Settings.instance().getQuickReactions(msg.tdlib()).length == 0) {
if (onMessageClick(x, y)) {
ViewUtils.onClick(this);
return true;
}
return false;
}

int doubleTapSlop = ViewConfiguration.get(getContext()).getScaledDoubleTapSlop();
float dx = x - pendingMessageClickX;
float dy = y - pendingMessageClickY;
boolean isDoubleTap = pendingMessageClick != null && pendingMessageClickId == msg.getId() &&
eventTime - pendingMessageClickTime <= ViewConfiguration.getDoubleTapTimeout() &&
dx * dx + dy * dy <= doubleTapSlop * doubleTapSlop;
Comment on lines +570 to +572

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Recognize the double tap on the second down event

When the second finger-down occurs within the double-tap timeout but its corresponding ACTION_UP occurs after that deadline, the pending runnable fires while the second tap is still held and opens the message action instead of applying the reaction. Android defines this timeout between the first up and second down, but this check runs only from ACTION_UP and compares the second up time; cancel or mark the pending single click when the qualifying second ACTION_DOWN arrives.

Useful? React with 👍 / 👎.

if (isDoubleTap) {
cancelPendingMessageClick();
if (msg.performDoubleTapQuickReaction()) {
ViewUtils.onClick(this);
return true;
}
return false;
}

if (pendingMessageClick != null) {
Runnable previousClick = pendingMessageClick;
cancelPendingMessageClick();
previousClick.run();
return true;
}

TGMessage tappedMessage = msg;
pendingMessageClickTime = eventTime;
pendingMessageClickId = msg.getId();
pendingMessageClickX = x;
pendingMessageClickY = y;
pendingMessageClick = () -> {
pendingMessageClick = null;
pendingMessageClickTime = 0;
pendingMessageClickId = 0;
if (msg == tappedMessage && onMessageClick(x, y)) {
ViewUtils.onClick(this);
}
};
postDelayed(pendingMessageClick, ViewConfiguration.getDoubleTapTimeout());
return true;
}

private static void selectMessage (MessagesController m, TGMessage msg, float touchX, float touchY) {
long messageId = msg.findMessageIdUnder(touchX, touchY);
Expand Down Expand Up @@ -1622,10 +1688,7 @@ public boolean onTouchEvent (MotionEvent e) {
}
if ((flags & FLAG_CAUGHT_CLICK) != 0) {
flags &= ~FLAG_CAUGHT_CLICK;
if (onMessageClick(e.getX(), e.getY())) {
ViewUtils.onClick(this);
return true;
}
return handleMessageTap(e.getX(), e.getY(), e.getEventTime());
}
return false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3381,10 +3381,12 @@ public static int getAnchorHighlightMode (int accountId, TdApi.Chat chat, @Nulla
if (chat == null) {
return HIGHLIGHT_MODE_NONE;
}
if (topicId != null && topicId.getConstructor() == TdApi.MessageTopicForum.CONSTRUCTOR) {
return canGoUnread(chat, null, forumTopic) ? HIGHLIGHT_MODE_UNREAD : HIGHLIGHT_MODE_NONE;
}
boolean canGoUnread = canGoUnread(chat, threadInfo, forumTopic);
boolean isForumTopic = topicId != null && topicId.getConstructor() == TdApi.MessageTopicForum.CONSTRUCTOR;
// A pinned forum topic is opened before its ForumTopic object is loaded. In that case,
// falling back to chat-wide unread state both ignores the topic and skips its saved anchor.
boolean canGoUnread = isForumTopic ?
forumTopic != null && canGoUnread(chat, null, forumTopic) :
canGoUnread(chat, threadInfo, forumTopic);
Settings.SavedMessageId messageId = Settings.instance().getScrollMessageId(accountId, chat.id,
topicId
);
Expand Down
55 changes: 44 additions & 11 deletions app/src/main/java/org/thunderdog/challegram/data/TGMessage.java
Original file line number Diff line number Diff line change
Expand Up @@ -8896,6 +8896,46 @@ private boolean canSendReaction (TdApi.ReactionType reactionType) {
return canBeReacted() && !tdlib.isSelfChat(msg.chatId) && Td.isAvailable(messageAvailableReactions, reactionType);
}

private void performQuickReaction (View sourceView, TdApi.ReactionType reactionType, TGReaction reactionObj) {
boolean hasReaction = messageReactions.hasReaction(reactionType);
if (Config.DISABLE_ANONYMOUS_NON_OWNER_REACTIONS && !hasReaction && tdlib.isAnonymousAdminNonCreator(msg.chatId)) {
showContentHint(sourceView, null, R.string.error_ANONYMOUS_REACTIONS_DISABLED);
} else if (!Config.PROTECT_ANONYMOUS_REACTIONS || hasReaction || !canGetAddedReactions() || messagesController().callNonAnonymousProtection(getId() + reactionObj.hashCode(), null)) {
if (messageReactions.toggleReaction(reactionType, false, false, handler(sourceView, null, () -> {}))) {
scheduleSetReactionAnimation(new NextReactionAnimation(reactionObj, NextReactionAnimation.TYPE_QUICK));
}
}
}

public boolean performDoubleTapQuickReaction () {
if (!Settings.instance().isQuickReactionDoubleTapEnabled()) {
return false;
}
String[] quickReactions = Settings.instance().getQuickReactions(tdlib);
if (quickReactions.length == 0) {
return false;
}
TdApi.ReactionType reactionType = TD.toReactionType(quickReactions[0]);
TGReaction reactionObj = tdlib.getReaction(reactionType);
if (reactionObj == null) {
return false;
}
Runnable applyReaction = () -> {
if (!isDestroyed() && canSendReaction(reactionType)) {
View currentView = findCurrentView();
if (currentView != null) {
performQuickReaction(currentView, reactionType, reactionObj);
}
}
};
if (messageAvailableReactions == null) {
loadAvailableReactions(applyReaction);
} else {
applyReaction.run();
}
return true;
}

private void computeQuickButtons () {
if (!UI.inUiThread()) {
tdlib.ui().post(this::computeQuickButtons);
Expand Down Expand Up @@ -8935,7 +8975,8 @@ private void computeQuickButtons () {
}

final String[] quickReactions = Settings.instance().getQuickReactions(tdlib);
for (int a = 0; a < quickReactions.length; a++) {
final boolean doubleTapOnly = Settings.instance().isQuickReactionDoubleTapEnabled() && quickReactions.length == 1;
for (int a = 0; !doubleTapOnly && a < quickReactions.length; a++) {
final String reactionString = quickReactions[a];
TdApi.ReactionType reactionType = TD.toReactionType(reactionString);
final boolean canReact = canSendReaction(reactionType);
Expand All @@ -8945,16 +8986,8 @@ private void computeQuickButtons () {
reactionDrawable.setComplexReceiver(currentComplexReceiver);

final boolean isOdd = a % 2 == 1;
final SwipeQuickAction quickReaction = new SwipeQuickAction(reactionObj.getTitle(), reactionDrawable, () -> {
boolean hasReaction = messageReactions.hasReaction(reactionType);
if (Config.DISABLE_ANONYMOUS_NON_OWNER_REACTIONS && !hasReaction && tdlib.isAnonymousAdminNonCreator(msg.chatId)) {
showContentHint(findCurrentView(), null, R.string.error_ANONYMOUS_REACTIONS_DISABLED);
} else if (!Config.PROTECT_ANONYMOUS_REACTIONS || hasReaction || !canGetAddedReactions() || messagesController().callNonAnonymousProtection(getId() + reactionObj.hashCode(), null)) {
if (messageReactions.toggleReaction(reactionType, false, false, handler(findCurrentView(), null, () -> {}))) {
scheduleSetReactionAnimation(new NextReactionAnimation(reactionObj, NextReactionAnimation.TYPE_QUICK));
}
}
}, false, true);
final SwipeQuickAction quickReaction = new SwipeQuickAction(reactionObj.getTitle(), reactionDrawable,
() -> performQuickReaction(findCurrentView(), reactionType, reactionObj), false, true);

if (isOdd) {
rightQuickDefaultPosition += 1;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,11 @@ protected void setValuedSetting (ListItem item, SettingView v, boolean isUpdate)
if (view != null) {
view.setRadioEnabled(!quickReactions.isEmpty(), isUpdate);
}
} else if (viewId == R.id.btn_quick_reaction_double_tap) {
TogglerView view = v.getToggler();
if (view != null) {
view.setRadioEnabled(!quickReactions.isEmpty() && Settings.instance().isQuickReactionDoubleTapEnabled(), isUpdate);
}
}
}

Expand Down Expand Up @@ -320,8 +325,10 @@ private void buildCells () {
items.add(new ListItem(ListItem.TYPE_SHADOW_TOP));
} else if (type == TYPE_QUICK_REACTION) {
items.add(toggleItem = new ListItem(ListItem.TYPE_RADIO_SETTING, R.id.btn_quick_reaction_enabled, 0, R.string.QuickReactionEnable, isToggleSelected()));
items.add(new ListItem(ListItem.TYPE_SEPARATOR_FULL));
items.add(new ListItem(ListItem.TYPE_RADIO_SETTING, R.id.btn_quick_reaction_double_tap, 0, R.string.QuickReactionDoubleTap));
items.add(new ListItem(ListItem.TYPE_SHADOW_BOTTOM));
items.add(new ListItem(ListItem.TYPE_DESCRIPTION, 0, 0, Lang.getMarkdownString(this, R.string.QuickReactionEnableDesc), false));
items.add(new ListItem(ListItem.TYPE_DESCRIPTION, 0, 0, Lang.getMarkdownString(this, R.string.QuickReactionDoubleTapDesc), false));
items.add(new ListItem(ListItem.TYPE_SHADOW_TOP));
}

Expand Down Expand Up @@ -455,11 +462,26 @@ public void onClick (View v) {
quickReactions.add(tdlib.defaultEmojiReaction());
} else {
quickReactions.clear();
Settings.instance().setQuickReactionDoubleTapEnabled(false);
}
toggleItem.setSelected(isToggleSelected());
updateQuickReactionsSettings();
adapter.updateAllValuedSettingsById(R.id.btn_enabledReactionsCheckboxGroup);
adapter.updateValuedSettingById(R.id.btn_quick_reaction_enabled);
adapter.updateValuedSettingById(R.id.btn_quick_reaction_double_tap);
}

if (viewId == R.id.btn_quick_reaction_double_tap) {
if (quickReactions.isEmpty()) {
quickReactions.add(tdlib.defaultEmojiReaction());
toggleItem.setSelected(true);
updateQuickReactionsSettings();
adapter.updateAllValuedSettingsById(R.id.btn_enabledReactionsCheckboxGroup);
adapter.updateValuedSettingById(R.id.btn_quick_reaction_enabled);
}
Settings settings = Settings.instance();
settings.setQuickReactionDoubleTapEnabled(!settings.isQuickReactionDoubleTapEnabled());
adapter.updateValuedSettingById(R.id.btn_quick_reaction_double_tap);
}

if (v instanceof ReactionCheckboxSettingsView) {
Expand Down Expand Up @@ -518,10 +540,15 @@ public void onClick (View v) {
}
updateQuickReactionsSettings();

if (quickReactions.isEmpty()) {
Settings.instance().setQuickReactionDoubleTapEnabled(false);
}

toggleItem.setSelected(isToggleSelected());

adapter.updateAllValuedSettingsById(R.id.btn_enabledReactionsCheckboxGroup);
adapter.updateValuedSettingById(R.id.btn_quick_reaction_enabled);
adapter.updateValuedSettingById(R.id.btn_quick_reaction_double_tap);
break;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4797,7 +4797,6 @@ private void showForumTopicOptions (TdApi.ForumTopic forumTopic, SettingsAdapter
((ForumTopicIconModifier) item.getDrawModifier()).setPinned(item.getIntValue() == newDefaultForumTopicId);
}
}
topicsAdapter.notifyDataSetChanged();
updateTopicBar(true);
} else if (id == R.id.btn_editTopic) {
promptEditForumTopic(forumTopic, topicsAdapter);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2064,7 +2064,24 @@ public void onFocus () {
@Override
public void onActivityResult (int requestCode, int resultCode, Intent data) {
if (resultCode == Activity.RESULT_OK && (requestCode == Intents.ACTIVITY_RESULT_RINGTONE || requestCode == Intents.ACTIVITY_RESULT_RINGTONE_NOTIFICATION)) {
final Uri originalUri = data.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI);
if (data == null) {
return;
}
Uri originalUri = data.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI);
if (originalUri == null && data.getData() != null) {
// Some vendor ringtone pickers delegate to DocumentsUI and return the selected
// audio file as Intent.data instead of EXTRA_RINGTONE_PICKED_URI.
originalUri = data.getData();
}
if (originalUri == null && data.getClipData() != null && data.getClipData().getItemCount() > 0) {
originalUri = data.getClipData().getItemAt(0).getUri();
}
if (originalUri == null) {
originalUri = data.getParcelableExtra(Intent.EXTRA_STREAM);
}
if (originalUri == null && !data.hasExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI)) {
return;
}

String ringtoneUri;
String name;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,7 @@ public static String accountInfoPrefix (int accountId) {

private static final String KEY_QUICK_REACTION = "quick_reaction";
private static final String KEY_QUICK_REACTIONS = "quick_reactions";
private static final String KEY_QUICK_REACTION_DOUBLE_TAP = "quick_reaction_double_tap";
private static final String KEY_BIG_REACTIONS_IN_CHANNELS = "big_reactions_in_channels";
private static final String KEY_BIG_REACTIONS_IN_CHATS = "big_reactions_in_chats";

Expand Down Expand Up @@ -6872,6 +6873,14 @@ public String[] getQuickReactions (Tdlib tdlib) {
return quickReactions;
}

public void setQuickReactionDoubleTapEnabled (boolean enabled) {
putBoolean(KEY_QUICK_REACTION_DOUBLE_TAP, enabled);
}

public boolean isQuickReactionDoubleTapEnabled () {
return getBoolean(KEY_QUICK_REACTION_DOUBLE_TAP, false);
}

public void setBigReactionsInChannels (boolean inChannels) {
pmc.putBoolean(KEY_BIG_REACTIONS_IN_CHANNELS, inChannels);
}
Expand Down
2 changes: 2 additions & 0 deletions app/src/main/res/values-ru/frogram_strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,6 @@
<string name="MiniAppCloseConfirm">Несохранённые изменения могут быть потеряны.</string>
<string name="RichMessage">Форматированная публикация</string>
<string name="OpenRichMessage">Открыть публикацию полностью</string>
<string name="QuickReactionDoubleTap">Реакция по двойному нажатию</string>
<string name="QuickReactionDoubleTapDesc">Дважды нажмите на сообщение, чтобы поставить первую выбранную ниже реакцию. Если выбрана одна реакция, реакции по свайпу отключаются. В некоторых группах и каналах отдельные реакции могут быть недоступны.</string>
</resources>
1 change: 1 addition & 0 deletions app/src/main/res/values/ids.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1125,6 +1125,7 @@

<item type="id" name="btn_enabledReactionsCheckboxGroup" />
<item type="id" name="btn_quick_reaction_enabled" />
<item type="id" name="btn_quick_reaction_double_tap" />

<item type="id" name="icon_additionalPassword" />
<item type="id" name="icon_email" />
Expand Down
2 changes: 2 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2066,6 +2066,8 @@

<string name="QuickReactionEnable">Enable Quick Reaction</string>
<string name="QuickReactionEnableDesc">Some groups and channels may not allow specific reactions.</string>
<string name="QuickReactionDoubleTap">Reaction on double tap</string>
<string name="QuickReactionDoubleTapDesc">Double-tap a message to apply the first reaction selected below. When only one reaction is selected, swipe reactions are disabled. Some groups and channels may not allow specific reactions.</string>

<string name="ReactionsDisabledDesc">Allow members to react to group messages</string>
<string name="xReactionsLimit_one">%1$s reaction</string>
Expand Down
Loading