mirror of
https://github.com/ArcaneChat/android.git
synced 2026-07-03 14:05:24 +02:00
Parse invitation link from search (#4432)
This commit is contained in:
@@ -601,6 +601,13 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit
|
||||
}
|
||||
}
|
||||
|
||||
public void handleQrFromSearch(String rawQrString) {
|
||||
qrData = rawQrString;
|
||||
new QrCodeHandler(this)
|
||||
.handleQrData(
|
||||
rawQrString, SecurejoinSource.Scan, SecurejoinUiPath.QrIcon, relayLockLauncher);
|
||||
}
|
||||
|
||||
private void handleResetRelaying() {
|
||||
resetRelayingMessageContent(this);
|
||||
refreshTitle();
|
||||
|
||||
@@ -44,9 +44,11 @@ import org.thoughtcrime.securesms.components.AvatarView;
|
||||
import org.thoughtcrime.securesms.components.DeliveryStatusView;
|
||||
import org.thoughtcrime.securesms.components.FromTextView;
|
||||
import org.thoughtcrime.securesms.connect.DcHelper;
|
||||
import org.thoughtcrime.securesms.contacts.avatars.GeneratedContactPhoto;
|
||||
import org.thoughtcrime.securesms.database.model.ThreadRecord;
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.search.QrInviteData;
|
||||
import org.thoughtcrime.securesms.util.DateUtils;
|
||||
import org.thoughtcrime.securesms.util.ThemeUtil;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
@@ -216,6 +218,40 @@ public class ConversationListItem extends RelativeLayout
|
||||
avatar.setSeenRecently(false);
|
||||
}
|
||||
|
||||
public void bind(@NonNull QrInviteData inviteData, @NonNull GlideRequests glideRequests) {
|
||||
this.selectedThreads = Collections.emptySet();
|
||||
|
||||
fromView.setText(inviteData.getDisplayTitle());
|
||||
fromView.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
|
||||
subjectView.setVisibility(VISIBLE);
|
||||
subjectView.setText(inviteData.getDisplaySubtitle());
|
||||
subjectView.setTypeface(LIGHT_TYPEFACE);
|
||||
subjectView.setTextColor(
|
||||
ThemeUtil.getThemedColor(getContext(), R.attr.conversation_list_item_subject_color));
|
||||
|
||||
dateView.setText("");
|
||||
dateView.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
|
||||
archivedBadgeView.setVisibility(GONE);
|
||||
requestBadgeView.setVisibility(GONE);
|
||||
unreadIndicator.setVisibility(GONE);
|
||||
deliveryStatusIndicator.setNone();
|
||||
|
||||
setBatchState(false);
|
||||
|
||||
if (inviteData.getContactId() > 0) {
|
||||
DcContext dcContext = DcHelper.getContext(getContext());
|
||||
DcContact contact = dcContext.getContact(inviteData.getContactId());
|
||||
Recipient recipient = new Recipient(getContext(), contact);
|
||||
avatar.setAvatar(glideRequests, recipient, false);
|
||||
avatar.setSeenRecently(contact.wasSeenRecently());
|
||||
} else {
|
||||
avatar.setImageDrawable(
|
||||
new GeneratedContactPhoto("+")
|
||||
.asDrawable(getContext(), ThemeUtil.getDummyContactColor(getContext())));
|
||||
avatar.setSeenRecently(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unbind() {}
|
||||
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
package org.thoughtcrime.securesms.search;
|
||||
|
||||
import android.content.Context;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.b44t.messenger.DcContact;
|
||||
import com.b44t.messenger.DcContext;
|
||||
import com.b44t.messenger.DcLot;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
|
||||
public class QrInviteData {
|
||||
|
||||
private final String displayTitle;
|
||||
private final String displaySubtitle;
|
||||
private final String rawQrString;
|
||||
private final int contactId;
|
||||
|
||||
private QrInviteData(
|
||||
@NonNull String displayTitle,
|
||||
@NonNull String displaySubtitle,
|
||||
@NonNull String rawQrString,
|
||||
int contactId) {
|
||||
this.displayTitle = displayTitle;
|
||||
this.displaySubtitle = displaySubtitle;
|
||||
this.rawQrString = rawQrString;
|
||||
this.contactId = contactId;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static QrInviteData from(
|
||||
@NonNull Context context,
|
||||
@NonNull DcContext dcContext,
|
||||
@NonNull DcLot qrParsed,
|
||||
@NonNull String rawQrString) {
|
||||
int state = qrParsed.getState();
|
||||
String title;
|
||||
String subtitle;
|
||||
int contactId = 0;
|
||||
|
||||
switch (state) {
|
||||
case DcContext.DC_QR_ASK_VERIFYCONTACT:
|
||||
case DcContext.DC_QR_FPR_OK:
|
||||
case DcContext.DC_QR_ADDR:
|
||||
contactId = qrParsed.getId();
|
||||
DcContact contact = dcContext.getContact(contactId);
|
||||
title = contact.getDisplayName();
|
||||
subtitle = context.getString(R.string.start_chat);
|
||||
break;
|
||||
case DcContext.DC_QR_ASK_VERIFYGROUP:
|
||||
title = qrParsed.getText1();
|
||||
subtitle = context.getString(R.string.join_group);
|
||||
break;
|
||||
case DcContext.DC_QR_ASK_JOIN_BROADCAST:
|
||||
title = qrParsed.getText1();
|
||||
subtitle = context.getString(R.string.join_channel);
|
||||
break;
|
||||
case DcContext.DC_QR_ACCOUNT:
|
||||
case DcContext.DC_QR_LOGIN:
|
||||
title = qrParsed.getText1();
|
||||
subtitle = context.getString(R.string.add_transport);
|
||||
break;
|
||||
case DcContext.DC_QR_PROXY:
|
||||
title = qrParsed.getText1();
|
||||
subtitle = context.getString(R.string.proxy_use_proxy);
|
||||
break;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
return new QrInviteData(title, subtitle, rawQrString, contactId);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public String getDisplayTitle() {
|
||||
return displayTitle;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public String getDisplaySubtitle() {
|
||||
return displaySubtitle;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public String getRawQrString() {
|
||||
return rawQrString;
|
||||
}
|
||||
|
||||
public int getContactId() {
|
||||
return contactId;
|
||||
}
|
||||
}
|
||||
@@ -200,6 +200,14 @@ public class SearchFragment extends BaseConversationListFragment
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInvitationClicked(@NonNull String rawQrString) {
|
||||
ConversationListActivity conversationList = (ConversationListActivity) getActivity();
|
||||
if (conversationList != null) {
|
||||
conversationList.handleQrFromSearch(rawQrString);
|
||||
}
|
||||
}
|
||||
|
||||
public void updateSearchQuery(@NonNull String query) {
|
||||
if (viewModel != null) {
|
||||
viewModel.updateQuery(query);
|
||||
|
||||
@@ -25,6 +25,7 @@ import org.thoughtcrime.securesms.util.StickyHeaderDecoration;
|
||||
class SearchListAdapter
|
||||
extends BaseConversationListAdapter<SearchListAdapter.SearchResultViewHolder>
|
||||
implements StickyHeaderDecoration.StickyHeaderAdapter<SearchListAdapter.HeaderViewHolder> {
|
||||
private static final int TYPE_QR_INVITE = 0;
|
||||
private static final int TYPE_CHATS = 1;
|
||||
private static final int TYPE_CONTACTS = 2;
|
||||
private static final int TYPE_MESSAGES = 3;
|
||||
@@ -57,6 +58,14 @@ class SearchListAdapter
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull SearchResultViewHolder holder, int position) {
|
||||
if (isQrInvitePosition(position)) {
|
||||
QrInviteData inviteData = searchResult.getQrInviteData();
|
||||
if (inviteData != null) {
|
||||
holder.bind(inviteData, glideRequests, eventListener);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
DcChatlist.Item conversationResult = getConversationResult(position);
|
||||
|
||||
if (conversationResult != null) {
|
||||
@@ -97,7 +106,9 @@ class SearchListAdapter
|
||||
|
||||
@Override
|
||||
public long getHeaderId(int position) {
|
||||
if (getConversationResult(position) != null) {
|
||||
if (isQrInvitePosition(position)) {
|
||||
return TYPE_QR_INVITE;
|
||||
} else if (getConversationResult(position) != null) {
|
||||
return TYPE_CHATS;
|
||||
} else if (getContactResult(position) != null) {
|
||||
return TYPE_CONTACTS;
|
||||
@@ -116,33 +127,39 @@ class SearchListAdapter
|
||||
@Override
|
||||
public void onBindHeaderViewHolder(HeaderViewHolder viewHolder, int position) {
|
||||
int headerType = (int) getHeaderId(position);
|
||||
int textId = R.plurals.n_messages;
|
||||
int count = 1;
|
||||
boolean maybeLimitedTo1000 = false;
|
||||
String title;
|
||||
|
||||
switch (headerType) {
|
||||
case TYPE_CHATS:
|
||||
textId = R.plurals.n_chats;
|
||||
count = searchResult.getChats().getCnt();
|
||||
break;
|
||||
case TYPE_CONTACTS:
|
||||
textId = R.plurals.n_contacts;
|
||||
count = searchResult.getContacts().length;
|
||||
break;
|
||||
case TYPE_MESSAGES:
|
||||
textId = R.plurals.n_messages;
|
||||
count = searchResult.getMessages().length;
|
||||
maybeLimitedTo1000 =
|
||||
count == 1000; // a count of 1000 results may be limited, see documentation of
|
||||
// dc_search_msgs()
|
||||
break;
|
||||
}
|
||||
if (headerType == TYPE_QR_INVITE) {
|
||||
title = context.getString(R.string.link);
|
||||
} else {
|
||||
int textId = R.plurals.n_messages;
|
||||
int count = 1;
|
||||
boolean maybeLimitedTo1000 = false;
|
||||
|
||||
String title = context.getResources().getQuantityString(textId, count, count);
|
||||
if (maybeLimitedTo1000) {
|
||||
title =
|
||||
title.replace(
|
||||
"000", "000+"); // skipping the first digit allows formattings as "1.000" or "1,000"
|
||||
switch (headerType) {
|
||||
case TYPE_CHATS:
|
||||
textId = R.plurals.n_chats;
|
||||
count = searchResult.getChats().getCnt();
|
||||
break;
|
||||
case TYPE_CONTACTS:
|
||||
textId = R.plurals.n_contacts;
|
||||
count = searchResult.getContacts().length;
|
||||
break;
|
||||
case TYPE_MESSAGES:
|
||||
textId = R.plurals.n_messages;
|
||||
count = searchResult.getMessages().length;
|
||||
maybeLimitedTo1000 =
|
||||
count == 1000; // a count of 1000 results may be limited, see documentation of
|
||||
// dc_search_msgs()
|
||||
break;
|
||||
}
|
||||
|
||||
title = context.getResources().getQuantityString(textId, count, count);
|
||||
if (maybeLimitedTo1000) {
|
||||
title =
|
||||
title.replace(
|
||||
"000", "000+"); // skipping the first digit allows formattings as "1.000" or "1,000"
|
||||
}
|
||||
}
|
||||
viewHolder.bind(title);
|
||||
}
|
||||
@@ -160,10 +177,19 @@ class SearchListAdapter
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
private int getQrInviteCount() {
|
||||
return searchResult.getQrInviteData() != null ? 1 : 0;
|
||||
}
|
||||
|
||||
private boolean isQrInvitePosition(int position) {
|
||||
return position == 0 && searchResult.getQrInviteData() != null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private DcChatlist.Item getConversationResult(int position) {
|
||||
if (position < searchResult.getChats().getCnt()) {
|
||||
return searchResult.getChats().getItem(position);
|
||||
int offset = position - getQrInviteCount();
|
||||
if (offset >= 0 && offset < searchResult.getChats().getCnt()) {
|
||||
return searchResult.getChats().getItem(offset);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -185,7 +211,7 @@ class SearchListAdapter
|
||||
}
|
||||
|
||||
private int getFirstContactIndex() {
|
||||
return searchResult.getChats().getCnt();
|
||||
return getQrInviteCount() + searchResult.getChats().getCnt();
|
||||
}
|
||||
|
||||
private int getFirstMessageIndex() {
|
||||
@@ -200,6 +226,8 @@ class SearchListAdapter
|
||||
void onContactClicked(@NonNull DcContact contact);
|
||||
|
||||
void onMessageClicked(@NonNull DcMsg message);
|
||||
|
||||
void onInvitationClicked(@NonNull String rawQrString);
|
||||
}
|
||||
|
||||
static class SearchResultViewHolder extends RecyclerView.ViewHolder {
|
||||
@@ -257,9 +285,20 @@ class SearchListAdapter
|
||||
root.setOnClickListener(view -> eventListener.onMessageClicked(messageResult));
|
||||
}
|
||||
|
||||
void bind(
|
||||
@NonNull QrInviteData inviteData,
|
||||
@NonNull GlideRequests glideRequests,
|
||||
@NonNull EventListener eventListener) {
|
||||
root.bind(inviteData, glideRequests);
|
||||
root.setOnClickListener(
|
||||
view -> eventListener.onInvitationClicked(inviteData.getRawQrString()));
|
||||
root.setOnLongClickListener(null);
|
||||
}
|
||||
|
||||
void recycle() {
|
||||
root.unbind();
|
||||
root.setOnClickListener(null);
|
||||
root.setOnLongClickListener(null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import androidx.lifecycle.ViewModel;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
import com.b44t.messenger.DcChatlist;
|
||||
import com.b44t.messenger.DcContext;
|
||||
import com.b44t.messenger.DcLot;
|
||||
import org.thoughtcrime.securesms.connect.DcHelper;
|
||||
import org.thoughtcrime.securesms.search.model.SearchResult;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
@@ -18,13 +19,15 @@ class SearchViewModel extends ViewModel {
|
||||
private static final String TAG = "SearchViewModel";
|
||||
private final ObservingLiveData searchResult;
|
||||
private String lastQuery;
|
||||
private final Context appContext;
|
||||
private final DcContext dcContext;
|
||||
private boolean forwarding = false;
|
||||
private boolean inBgSearch;
|
||||
private boolean needsAnotherBgSearch;
|
||||
|
||||
SearchViewModel(@NonNull Context context) {
|
||||
this.dcContext = DcHelper.getContext(context.getApplicationContext());
|
||||
this.appContext = context.getApplicationContext();
|
||||
this.dcContext = DcHelper.getContext(appContext);
|
||||
this.searchResult = new ObservingLiveData();
|
||||
}
|
||||
|
||||
@@ -73,6 +76,12 @@ class SearchViewModel extends ViewModel {
|
||||
return;
|
||||
}
|
||||
|
||||
QrInviteData qrInviteData = null;
|
||||
if (!forwarding && query.contains(":")) {
|
||||
DcLot qrParsed = dcContext.checkQr(query);
|
||||
qrInviteData = QrInviteData.from(appContext, dcContext, qrParsed, query);
|
||||
}
|
||||
|
||||
// #1 search for chats
|
||||
long startMs = System.currentTimeMillis();
|
||||
DcChatlist conversations =
|
||||
@@ -83,7 +92,8 @@ class SearchViewModel extends ViewModel {
|
||||
// #2 search for contacts
|
||||
if (!query.equals(lastQuery) && overallCnt > 0) {
|
||||
Log.i(TAG, "... skipping getContacts() and searchMsgs(), more recent search pending");
|
||||
callback.onResult(new SearchResult(query, new int[0], conversations, new int[0]));
|
||||
callback.onResult(
|
||||
new SearchResult(query, new int[0], conversations, new int[0], qrInviteData));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -95,19 +105,19 @@ class SearchViewModel extends ViewModel {
|
||||
// #3 search for messages
|
||||
if (forwarding) {
|
||||
Log.i(TAG, "... searchMsgs() disabled by caller");
|
||||
callback.onResult(new SearchResult(query, contacts, conversations, new int[0]));
|
||||
callback.onResult(new SearchResult(query, contacts, conversations, new int[0], qrInviteData));
|
||||
return;
|
||||
}
|
||||
|
||||
if (query.length() <= 1) {
|
||||
Log.i(TAG, "... skipping searchMsgs(), string too short");
|
||||
callback.onResult(new SearchResult(query, contacts, conversations, new int[0]));
|
||||
callback.onResult(new SearchResult(query, contacts, conversations, new int[0], qrInviteData));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!query.equals(lastQuery) && overallCnt > 0) {
|
||||
Log.i(TAG, "... skipping searchMsgs(), more recent search pending");
|
||||
callback.onResult(new SearchResult(query, contacts, conversations, new int[0]));
|
||||
callback.onResult(new SearchResult(query, contacts, conversations, new int[0], qrInviteData));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -115,7 +125,7 @@ class SearchViewModel extends ViewModel {
|
||||
int[] messages = dcContext.searchMsgs(0, query);
|
||||
Log.i(TAG, "⏰ searchMsgs(" + query + "): " + (System.currentTimeMillis() - startMs) + "ms");
|
||||
|
||||
callback.onResult(new SearchResult(query, contacts, conversations, messages));
|
||||
callback.onResult(new SearchResult(query, contacts, conversations, messages, qrInviteData));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package org.thoughtcrime.securesms.search.model;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.b44t.messenger.DcChatlist;
|
||||
import org.thoughtcrime.securesms.search.QrInviteData;
|
||||
|
||||
/**
|
||||
* Represents an all-encompassing search result that can contain various result for different
|
||||
@@ -16,16 +18,27 @@ public class SearchResult {
|
||||
private final int[] contacts;
|
||||
private final DcChatlist conversations;
|
||||
private final int[] messages;
|
||||
private final QrInviteData qrInviteData;
|
||||
|
||||
public SearchResult(
|
||||
@NonNull String query,
|
||||
@NonNull int[] contacts,
|
||||
@NonNull DcChatlist conversations,
|
||||
@NonNull int[] messages) {
|
||||
this(query, contacts, conversations, messages, null);
|
||||
}
|
||||
|
||||
public SearchResult(
|
||||
@NonNull String query,
|
||||
@NonNull int[] contacts,
|
||||
@NonNull DcChatlist conversations,
|
||||
@NonNull int[] messages,
|
||||
@Nullable QrInviteData qrInviteData) {
|
||||
this.query = query;
|
||||
this.contacts = contacts;
|
||||
this.conversations = conversations;
|
||||
this.messages = messages;
|
||||
this.qrInviteData = qrInviteData;
|
||||
}
|
||||
|
||||
public int[] getContacts() {
|
||||
@@ -44,8 +57,16 @@ public class SearchResult {
|
||||
return query;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public QrInviteData getQrInviteData() {
|
||||
return qrInviteData;
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return contacts.length + conversations.getCnt() + messages.length;
|
||||
return (qrInviteData != null ? 1 : 0)
|
||||
+ contacts.length
|
||||
+ conversations.getCnt()
|
||||
+ messages.length;
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
|
||||
Reference in New Issue
Block a user