mirror of
https://github.com/ArcaneChat/android.git
synced 2026-07-03 14:05:24 +02:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d8ba2652ce | |||
| d0d1a9c902 | |||
| f39be17803 |
@@ -72,6 +72,8 @@ import androidx.lifecycle.ViewModelProvider;
|
||||
import androidx.media3.session.MediaController;
|
||||
import androidx.media3.session.SessionCommand;
|
||||
import androidx.media3.session.SessionToken;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.b44t.messenger.DcChat;
|
||||
import com.b44t.messenger.DcContact;
|
||||
@@ -90,6 +92,7 @@ import org.thoughtcrime.securesms.components.HidingLinearLayout;
|
||||
import org.thoughtcrime.securesms.components.InputAwareLayout;
|
||||
import org.thoughtcrime.securesms.components.InputPanel;
|
||||
import org.thoughtcrime.securesms.components.KeyboardAwareLinearLayout.OnKeyboardShownListener;
|
||||
import org.thoughtcrime.securesms.components.MentionAdapter;
|
||||
import org.thoughtcrime.securesms.components.ScaleStableImageView;
|
||||
import org.thoughtcrime.securesms.components.SendButton;
|
||||
import org.thoughtcrime.securesms.components.audioplay.AudioPlaybackViewModel;
|
||||
@@ -208,6 +211,9 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
private boolean isEditing = false;
|
||||
private boolean switchedProfile = false;
|
||||
|
||||
private RecyclerView mentionSuggestions;
|
||||
private MentionAdapter mentionAdapter;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle state, boolean ready) {
|
||||
this.context = ApplicationContext.getInstance(getApplicationContext());
|
||||
@@ -956,6 +962,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
inputPanel = ViewUtil.findById(this, R.id.bottom_panel);
|
||||
backgroundView = ViewUtil.findById(this, R.id.conversation_background);
|
||||
messageRequestBottomView = ViewUtil.findById(this, R.id.conversation_activity_message_request_bottom_bar);
|
||||
mentionSuggestions = ViewUtil.findById(this, R.id.mention_suggestions);
|
||||
|
||||
ImageButton quickCameraToggle = ViewUtil.findById(this, R.id.quick_camera_toggle);
|
||||
|
||||
@@ -1057,6 +1064,11 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
recipient = new Recipient(this, dcChat);
|
||||
glideRequests = GlideApp.with(this);
|
||||
|
||||
mentionAdapter = new MentionAdapter(glideRequests);
|
||||
mentionSuggestions.setLayoutManager(new LinearLayoutManager(this));
|
||||
mentionSuggestions.setAdapter(mentionAdapter);
|
||||
mentionAdapter.setOnMentionClickListener(contact -> insertMention(contact));
|
||||
|
||||
setComposePanelVisibility(true);
|
||||
initializeContactRequest();
|
||||
}
|
||||
@@ -1560,6 +1572,77 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
}
|
||||
}
|
||||
|
||||
private void updateMentionSuggestions() {
|
||||
if (!dcChat.isMultiUser()) {
|
||||
mentionSuggestions.setVisibility(View.GONE);
|
||||
return;
|
||||
}
|
||||
|
||||
Editable text = composeText.getText();
|
||||
if (text == null) {
|
||||
mentionSuggestions.setVisibility(View.GONE);
|
||||
return;
|
||||
}
|
||||
|
||||
int cursorPos = composeText.getSelectionStart();
|
||||
if (cursorPos <= 0) {
|
||||
mentionSuggestions.setVisibility(View.GONE);
|
||||
return;
|
||||
}
|
||||
|
||||
String textBeforeCursor = text.toString().substring(0, cursorPos);
|
||||
int atIndex = textBeforeCursor.lastIndexOf('@');
|
||||
if (atIndex < 0) {
|
||||
mentionSuggestions.setVisibility(View.GONE);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check that there's no space between "@" and the cursor
|
||||
String query = textBeforeCursor.substring(atIndex + 1);
|
||||
if (query.contains(" ") || query.contains("\n")) {
|
||||
mentionSuggestions.setVisibility(View.GONE);
|
||||
return;
|
||||
}
|
||||
|
||||
DcContext dcContext = DcHelper.getContext(context);
|
||||
int[] contactIds = dcContext.getChatContacts(chatId);
|
||||
List<DcContact> matched = new ArrayList<>();
|
||||
String lowerQuery = query.toLowerCase();
|
||||
for (int id : contactIds) {
|
||||
if (id <= DcContact.DC_CONTACT_ID_LAST_SPECIAL) continue;
|
||||
DcContact contact = dcContext.getContact(id);
|
||||
String displayName = contact.getDisplayName().toLowerCase();
|
||||
String addr = contact.getAddr().toLowerCase();
|
||||
if (displayName.contains(lowerQuery) || addr.contains(lowerQuery)) {
|
||||
matched.add(contact);
|
||||
}
|
||||
}
|
||||
|
||||
if (matched.isEmpty()) {
|
||||
mentionSuggestions.setVisibility(View.GONE);
|
||||
} else {
|
||||
mentionAdapter.setContacts(matched);
|
||||
mentionSuggestions.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
private void insertMention(DcContact contact) {
|
||||
Editable text = composeText.getText();
|
||||
if (text == null) return;
|
||||
|
||||
int cursorPos = composeText.getSelectionStart();
|
||||
if (cursorPos <= 0) return;
|
||||
|
||||
String textBeforeCursor = text.toString().substring(0, cursorPos);
|
||||
int atIndex = textBeforeCursor.lastIndexOf('@');
|
||||
if (atIndex < 0) return;
|
||||
|
||||
String mention = "@" + contact.getDisplayName() + " ";
|
||||
text.replace(atIndex, cursorPos, mention);
|
||||
composeText.setSelection(atIndex + mention.length());
|
||||
mentionSuggestions.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
private class ComposeKeyPressedListener implements OnKeyListener, OnClickListener, TextWatcher, OnFocusChangeListener {
|
||||
|
||||
int beforeLength;
|
||||
@@ -1593,6 +1676,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
if (composeText.getTextTrimmed().length() == 0 || beforeLength == 0) {
|
||||
composeText.postDelayed(ConversationActivity.this::updateToggleButtonState, 50);
|
||||
}
|
||||
updateMentionSuggestions();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
package org.thoughtcrime.securesms.components;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.b44t.messenger.DcContact;
|
||||
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class MentionAdapter extends RecyclerView.Adapter<MentionAdapter.ViewHolder> {
|
||||
|
||||
public interface OnMentionClickListener {
|
||||
void onMentionClicked(DcContact contact);
|
||||
}
|
||||
|
||||
private final List<DcContact> contacts = new ArrayList<>();
|
||||
private final GlideRequests glideRequests;
|
||||
private OnMentionClickListener listener;
|
||||
|
||||
public MentionAdapter(@NonNull GlideRequests glideRequests) {
|
||||
this.glideRequests = glideRequests;
|
||||
}
|
||||
|
||||
public void setContacts(List<DcContact> newContacts) {
|
||||
contacts.clear();
|
||||
contacts.addAll(newContacts);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public void setOnMentionClickListener(OnMentionClickListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View view = LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.mention_list_item, parent, false);
|
||||
return new ViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
||||
DcContact contact = contacts.get(position);
|
||||
Context context = holder.itemView.getContext();
|
||||
|
||||
holder.displayName.setText(contact.getDisplayName());
|
||||
String addr = contact.getAddr();
|
||||
String name = contact.getName();
|
||||
if (!name.isEmpty() && !name.equals(addr)) {
|
||||
holder.address.setText(addr);
|
||||
holder.address.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
holder.address.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
holder.avatar.setAvatar(glideRequests, new Recipient(context, contact), false);
|
||||
|
||||
holder.itemView.setOnClickListener(v -> {
|
||||
if (listener != null) {
|
||||
listener.onMentionClicked(contact);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return contacts.size();
|
||||
}
|
||||
|
||||
static class ViewHolder extends RecyclerView.ViewHolder {
|
||||
final AvatarImageView avatar;
|
||||
final TextView displayName;
|
||||
final TextView address;
|
||||
|
||||
ViewHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
avatar = itemView.findViewById(R.id.mention_avatar);
|
||||
displayName = itemView.findViewById(R.id.mention_display_name);
|
||||
address = itemView.findViewById(R.id.mention_address);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -53,6 +53,14 @@
|
||||
android:paddingEnd="16dp"
|
||||
android:focusable="true" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/mention_suggestions"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
android:background="?attr/input_panel_bg_color"
|
||||
android:elevation="4dp" />
|
||||
|
||||
<include layout="@layout/conversation_input_panel"/>
|
||||
|
||||
<FrameLayout
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:background="?attr/conversation_list_item_background"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingEnd="12dp">
|
||||
|
||||
<org.thoughtcrime.securesms.components.AvatarImageView
|
||||
android:id="@+id/mention_avatar"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:layout_marginEnd="10dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/mention_display_name"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="end"
|
||||
android:textSize="15sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/mention_address"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="end"
|
||||
android:textSize="13sp"
|
||||
android:paddingStart="8dp"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
|
||||
</LinearLayout>
|
||||
Reference in New Issue
Block a user