Skip to content

Commit eb3b214

Browse files
committed
Added d-pad navigation inside chats. (#464)
1 parent 3451f38 commit eb3b214

2 files changed

Lines changed: 261 additions & 4 deletions

File tree

TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java

Lines changed: 257 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -964,6 +964,9 @@ public int getColor(int key) {
964964
public int highlightMessageQuoteOffset = -1;
965965
private int scrollToMessagePosition = -10000;
966966
private Runnable unselectRunnable;
967+
private int dpadSelectedMessageId = Integer.MAX_VALUE;
968+
private int dpadConsumedKeyCode;
969+
private boolean dpadComposerFocused;
967970

968971
private String currentPicturePath;
969972

@@ -7062,6 +7065,10 @@ public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
70627065
if (!wasManualScroll && dy != 0) {
70637066
wasManualScroll = true;
70647067
}
7068+
if (isDpadMessageSelected()) {
7069+
clearDpadSelection();
7070+
}
7071+
dpadComposerFocused = false;
70657072
}
70667073
if (dy != 0) {
70677074
invalidateMergedVisibleBlurredPositionsAndSources(BLUR_INVALIDATE_FLAG_SCROLL);
@@ -16942,6 +16949,241 @@ private void removeSelectedMessageHighlight() {
1694216949
highlightPollOptionId = null;
1694316950
}
1694416951

16952+
private boolean isDpadMessageSelected() {
16953+
return dpadSelectedMessageId != Integer.MAX_VALUE;
16954+
}
16955+
16956+
private boolean isComposerActivelyFocused() {
16957+
return chatActivityEnterView != null && chatActivityEnterView.getEditField() != null
16958+
&& chatActivityEnterView.getEditField().isFocused()
16959+
&& (chatActivityEnterView.isKeyboardVisible() || chatActivityEnterView.getEditField().length() > 0);
16960+
}
16961+
16962+
private boolean isDpadNavigationKey(int keyCode) {
16963+
return keyCode == KeyEvent.KEYCODE_DPAD_UP || keyCode == KeyEvent.KEYCODE_DPAD_DOWN
16964+
|| keyCode == KeyEvent.KEYCODE_DPAD_CENTER || keyCode == KeyEvent.KEYCODE_ENTER
16965+
|| keyCode == KeyEvent.KEYCODE_NUMPAD_ENTER;
16966+
}
16967+
16968+
private void clearDpadSelection() {
16969+
if (dpadSelectedMessageId == Integer.MAX_VALUE) {
16970+
return;
16971+
}
16972+
dpadSelectedMessageId = Integer.MAX_VALUE;
16973+
updateVisibleRows();
16974+
}
16975+
16976+
private int findDpadSelectableIndex(int fromIndex, int step) {
16977+
for (int i = fromIndex; i >= 0 && i < messages.size(); i += step) {
16978+
MessageObject messageObject = messages.get(i);
16979+
if (messageObject != null && messageObject.getId() != 0) {
16980+
return i;
16981+
}
16982+
}
16983+
return -1;
16984+
}
16985+
16986+
private int dpadTopmostVisibleIndex() {
16987+
if (chatListView == null) {
16988+
return -1;
16989+
}
16990+
int best = -1;
16991+
float bestY = Float.MAX_VALUE;
16992+
float clipTop = chatListViewPaddingTop - chatListViewPaddingVisibleOffset;
16993+
for (int a = 0, n = chatListView.getChildCount(); a < n; a++) {
16994+
View child = chatListView.getChildAt(a);
16995+
MessageObject messageObject = null;
16996+
if (child instanceof ChatMessageCell) {
16997+
messageObject = ((ChatMessageCell) child).getMessageObject();
16998+
} else if (child instanceof ChatActionCell) {
16999+
messageObject = ((ChatActionCell) child).getMessageObject();
17000+
}
17001+
if (messageObject == null || messageObject.getId() == 0) {
17002+
continue;
17003+
}
17004+
int index = messages.indexOf(messageObject);
17005+
if (index < 0) {
17006+
continue;
17007+
}
17008+
if (child.getY() + child.getMeasuredHeight() <= clipTop) {
17009+
continue;
17010+
}
17011+
if (child.getY() < bestY) {
17012+
bestY = child.getY();
17013+
best = index;
17014+
}
17015+
}
17016+
return best;
17017+
}
17018+
17019+
private boolean dpadEnterListFromHeader() {
17020+
if (chatListView == null || messages.isEmpty()) {
17021+
return dpadFocusEnterView();
17022+
}
17023+
int index = dpadTopmostVisibleIndex();
17024+
if (index < 0) {
17025+
index = findDpadSelectableIndex(messages.size() - 1, -1);
17026+
}
17027+
if (index < 0) {
17028+
return dpadFocusEnterView();
17029+
}
17030+
if (chatActivityEnterView != null) {
17031+
chatActivityEnterView.setFieldFocused(false);
17032+
}
17033+
dpadComposerFocused = false;
17034+
actionBar.clearFocus();
17035+
chatListView.requestFocus();
17036+
dpadSelectedMessageId = messages.get(index).getId();
17037+
dpadEnsureSelectionVisible();
17038+
updateVisibleRows();
17039+
return true;
17040+
}
17041+
17042+
private boolean dpadEnterListFromComposer() {
17043+
if (chatListView == null || messages.isEmpty()) {
17044+
return false;
17045+
}
17046+
int index = findDpadSelectableIndex(0, 1);
17047+
if (index < 0) {
17048+
return false;
17049+
}
17050+
if (chatActivityEnterView != null) {
17051+
chatActivityEnterView.setFieldFocused(false);
17052+
chatActivityEnterView.closeKeyboard();
17053+
}
17054+
dpadComposerFocused = false;
17055+
chatListView.requestFocus();
17056+
dpadSelectedMessageId = messages.get(index).getId();
17057+
dpadEnsureSelectionVisible();
17058+
updateVisibleRows();
17059+
return true;
17060+
}
17061+
17062+
private boolean dpadMoveSelection(int step) {
17063+
if (!isDpadMessageSelected()) {
17064+
return false;
17065+
}
17066+
int currentIndex = -1;
17067+
for (int i = 0; i < messages.size(); i++) {
17068+
MessageObject messageObject = messages.get(i);
17069+
if (messageObject != null && messageObject.getId() == dpadSelectedMessageId) {
17070+
currentIndex = i;
17071+
break;
17072+
}
17073+
}
17074+
if (currentIndex < 0) {
17075+
clearDpadSelection();
17076+
return false;
17077+
}
17078+
int nextIndex = findDpadSelectableIndex(currentIndex + step, step);
17079+
if (nextIndex < 0) {
17080+
if (step < 0) {
17081+
clearDpadSelection();
17082+
return dpadFocusEnterView();
17083+
}
17084+
dpadEnsureSelectionVisible();
17085+
return true;
17086+
}
17087+
dpadSelectedMessageId = messages.get(nextIndex).getId();
17088+
dpadEnsureSelectionVisible();
17089+
updateVisibleRows();
17090+
return true;
17091+
}
17092+
17093+
private void dpadEnsureSelectionVisible() {
17094+
if (chatListView == null || chatLayoutManager == null || chatAdapter == null || !isDpadMessageSelected()) {
17095+
return;
17096+
}
17097+
int index = -1;
17098+
for (int i = 0; i < messages.size(); i++) {
17099+
MessageObject messageObject = messages.get(i);
17100+
if (messageObject != null && messageObject.getId() == dpadSelectedMessageId) {
17101+
index = i;
17102+
break;
17103+
}
17104+
}
17105+
if (index < 0) {
17106+
return;
17107+
}
17108+
BaseCell cell = findMessageCell(dpadSelectedMessageId, false);
17109+
float topBound = chatListViewPaddingTop - chatListViewPaddingVisibleOffset + AndroidUtilities.dp(12);
17110+
float bottomBound = chatListView.getMeasuredHeight() - blurredViewBottomOffset - AndroidUtilities.dp(12);
17111+
if (cell == null) {
17112+
int position = chatAdapter.messagesStartRow + index;
17113+
chatLayoutManager.scrollToPositionWithOffset(position, (int) (chatListView.getMeasuredHeight() - chatListViewPaddingTop) / 2);
17114+
return;
17115+
}
17116+
float cellTop = cell.getY();
17117+
float cellBottom = cell.getY() + cell.getMeasuredHeight();
17118+
if (cellTop < topBound) {
17119+
chatListView.smoothScrollBy(0, (int) (cellTop - topBound));
17120+
} else if (cellBottom > bottomBound) {
17121+
chatListView.smoothScrollBy(0, (int) (cellBottom - bottomBound));
17122+
}
17123+
}
17124+
17125+
private boolean dpadFocusEnterView() {
17126+
if (chatActivityEnterView == null || chatActivityEnterView.getVisibility() != View.VISIBLE || !chatActivityEnterView.isFieldFocusAllowed() || chatActivityEnterView.getEditField() == null) {
17127+
return false;
17128+
}
17129+
clearDpadSelection();
17130+
dpadComposerFocused = true;
17131+
actionBar.clearFocus();
17132+
chatActivityEnterView.getEditField().requestFocus();
17133+
chatActivityEnterView.openKeyboard();
17134+
return true;
17135+
}
17136+
17137+
private boolean dpadOpenSelectedMessageMenu() {
17138+
if (!isDpadMessageSelected()) {
17139+
return false;
17140+
}
17141+
BaseCell cell = findMessageCell(dpadSelectedMessageId, true);
17142+
if (cell == null) {
17143+
return false;
17144+
}
17145+
return createMenu(cell, true, true, cell.getWidth() / 2f, cell.getHeight() / 2f, true);
17146+
}
17147+
17148+
private boolean dpadHandleKeyDown(int keyCode) {
17149+
if (chatListView == null || inPreviewMode || isInsideContainer || actionBar.isActionModeShowed() || isReport()) {
17150+
return false;
17151+
}
17152+
if (scrimPopupWindow != null && scrimPopupWindow.isShowing()) {
17153+
return false;
17154+
}
17155+
if (textSelectionHelper != null && textSelectionHelper.isInSelectionMode()) {
17156+
return false;
17157+
}
17158+
if (chatActivityEnterView != null && (chatActivityEnterView.isPopupShowing() || chatActivityEnterView.isRecordingAudioVideo())) {
17159+
return false;
17160+
}
17161+
switch (keyCode) {
17162+
case KeyEvent.KEYCODE_DPAD_DOWN:
17163+
if (!isDpadMessageSelected()) {
17164+
if (dpadComposerFocused || isComposerActivelyFocused()) {
17165+
return false;
17166+
}
17167+
return dpadEnterListFromHeader();
17168+
}
17169+
return dpadMoveSelection(-1);
17170+
case KeyEvent.KEYCODE_DPAD_UP:
17171+
if (!isDpadMessageSelected()) {
17172+
if (dpadComposerFocused && chatActivityEnterView != null && !chatActivityEnterView.isKeyboardVisible()
17173+
&& chatActivityEnterView.getEditField() != null && chatActivityEnterView.getEditField().length() == 0) {
17174+
return dpadEnterListFromComposer();
17175+
}
17176+
return false;
17177+
}
17178+
return dpadMoveSelection(1);
17179+
case KeyEvent.KEYCODE_DPAD_CENTER:
17180+
case KeyEvent.KEYCODE_ENTER:
17181+
case KeyEvent.KEYCODE_NUMPAD_ENTER:
17182+
return dpadOpenSelectedMessageMenu();
17183+
}
17184+
return false;
17185+
}
17186+
1694517187
private AlertDialog progressDialog;
1694617188
private int nextScrollToMessageId;
1694717189
private int nextScrollFromMessageId;
@@ -18849,6 +19091,16 @@ public boolean dispatchKeyEvent(KeyEvent event) {
1884919091
forwardingPreviewView.dismiss(true);
1885019092
return true;
1885119093
}
19094+
if (isDpadNavigationKey(event.getKeyCode())) {
19095+
if (event.getAction() == KeyEvent.ACTION_DOWN && dpadHandleKeyDown(event.getKeyCode())) {
19096+
dpadConsumedKeyCode = event.getKeyCode();
19097+
return true;
19098+
}
19099+
if (event.getAction() == KeyEvent.ACTION_UP && dpadConsumedKeyCode == event.getKeyCode()) {
19100+
dpadConsumedKeyCode = 0;
19101+
return true;
19102+
}
19103+
}
1885219104
return super.dispatchKeyEvent(event);
1885319105
}
1885419106

@@ -34954,8 +35206,9 @@ private void updateVisibleRows(boolean suppressUpdateMessageObject) {
3495435206
if (cell != scrimView) {
3495535207
cell.setCheckPressed(!disableSelection, disableSelection && selected);
3495635208
}
34957-
cell.setHighlighted(highlightMessageId != Integer.MAX_VALUE && messageObject != null && messageObject.getId() == highlightMessageId);
34958-
if (highlightMessageId != Integer.MAX_VALUE) {
35209+
boolean dpadSelected = dpadSelectedMessageId != Integer.MAX_VALUE && messageObject != null && messageObject.getId() == dpadSelectedMessageId;
35210+
cell.setHighlighted(dpadSelected || (highlightMessageId != Integer.MAX_VALUE && messageObject != null && messageObject.getId() == highlightMessageId));
35211+
if (highlightMessageId != Integer.MAX_VALUE && !dpadSelected) {
3495935212
startMessageUnselect();
3496035213
}
3496135214
if (cell.isHighlighted() && highlightMessageQuote != null) {
@@ -37779,7 +38032,7 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
3777938032
messageCell.setShowTopic(true);
3778038033
messageCell.setMessageObject(message, groupedMessages, pinnedBottom, pinnedTop, firstInChat, lastInChatList);
3778138034
messageCell.setSpoilersSuppressed(chatListView.getScrollState() != RecyclerView.SCROLL_STATE_IDLE);
37782-
messageCell.setHighlighted(highlightMessageId != Integer.MAX_VALUE && message.getId() == highlightMessageId);
38035+
messageCell.setHighlighted(dpadSelectedMessageId != Integer.MAX_VALUE && message.getId() == dpadSelectedMessageId || highlightMessageId != Integer.MAX_VALUE && message.getId() == highlightMessageId);
3778338036
if (messageCell.isHighlighted() && highlightMessageQuote != null) {
3778438037
final long now = System.currentTimeMillis();
3778538038
messageCell.setHighlightedText(highlightMessageQuote, true, highlightMessageQuoteOffset, highlightMessageQuoteFirst || now - highlightMessageQuoteFirstTime < 200);
@@ -38182,7 +38435,7 @@ public void onViewAttachedToWindow(RecyclerView.ViewHolder holder) {
3818238435
}
3818338436

3818438437
if (!inPreviewMode || !messageCell.isHighlighted()) {
38185-
messageCell.setHighlighted(highlightMessageId != Integer.MAX_VALUE && (messageCell.getMessageObject() != null && messageCell.getMessageObject().getId() == highlightMessageId || messageCell.getCurrentMessagesGroup() != null && messageCell.getCurrentMessagesGroup().contains(highlightMessageId)));
38438+
messageCell.setHighlighted(dpadSelectedMessageId != Integer.MAX_VALUE && messageCell.getMessageObject() != null && messageCell.getMessageObject().getId() == dpadSelectedMessageId || highlightMessageId != Integer.MAX_VALUE && (messageCell.getMessageObject() != null && messageCell.getMessageObject().getId() == highlightMessageId || messageCell.getCurrentMessagesGroup() != null && messageCell.getCurrentMessagesGroup().contains(highlightMessageId)));
3818638439
if (messageCell.isHighlighted() && highlightMessageQuote != null) {
3818738440
final long now = System.currentTimeMillis();
3818838441
if (!messageCell.setHighlightedText(highlightMessageQuote, true, highlightMessageQuoteOffset, highlightMessageQuoteFirst || now - highlightMessageQuoteFirstTime < 200) && showNoQuoteAlert) {

TMessagesProj/src/main/java/org/telegram/ui/Components/ChatActivityEnterView.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10383,6 +10383,10 @@ public EditTextCaption getEditField() {
1038310383
return messageEditText;
1038410384
}
1038510385

10386+
public boolean isFieldFocusAllowed() {
10387+
return messageEditText != null && (sendPlainEnabled || isEditingMessage());
10388+
}
10389+
1038610390
public SenderSelectView getSenderSelectView() {
1038710391
return senderSelectView;
1038810392
}

0 commit comments

Comments
 (0)