mirror of
https://github.com/ArcaneChat/android.git
synced 2026-07-03 14:05:24 +02:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8217d3fc22 | |||
| dfa80abd13 | |||
| 177b68a42c | |||
| e8d3458a7b | |||
| 54332e8bac | |||
| 6b06422fa1 | |||
| a7bd40f851 | |||
| 78659bb9e0 |
@@ -23,6 +23,7 @@ import android.graphics.Color;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.Rect;
|
||||
import android.text.Spannable;
|
||||
import android.text.Spanned;
|
||||
import android.text.TextUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
@@ -36,6 +37,7 @@ import androidx.annotation.DimenRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.core.view.ViewCompat;
|
||||
|
||||
import com.b44t.messenger.DcChat;
|
||||
import com.b44t.messenger.DcContact;
|
||||
@@ -66,6 +68,7 @@ import org.thoughtcrime.securesms.mms.VcardSlide;
|
||||
import org.thoughtcrime.securesms.reactions.ReactionsConversationView;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.util.Linkifier;
|
||||
import org.thoughtcrime.securesms.util.LongClickCopySpan;
|
||||
import org.thoughtcrime.securesms.util.LongClickMovementMethod;
|
||||
import org.thoughtcrime.securesms.util.MarkdownUtil;
|
||||
import org.thoughtcrime.securesms.util.MediaUtil;
|
||||
@@ -73,6 +76,7 @@ import org.thoughtcrime.securesms.util.Util;
|
||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||
import org.thoughtcrime.securesms.util.views.Stub;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -125,6 +129,9 @@ public class ConversationItem extends BaseConversationItem
|
||||
private Stub<VcardView> vcardViewStub;
|
||||
private Stub<CallItemView> callViewStub;
|
||||
private @Nullable EventListener eventListener;
|
||||
// IDs of accessibility actions registered via ViewCompat.addAccessibilityAction, kept so they
|
||||
// can be removed on rebind (RecyclerView reuses views for different messages).
|
||||
private final List<Integer> linkActionIds = new ArrayList<>();
|
||||
|
||||
private int measureCalls;
|
||||
|
||||
@@ -413,6 +420,13 @@ public class ConversationItem extends BaseConversationItem
|
||||
bodyText.setClickable(false);
|
||||
bodyText.setFocusable(false);
|
||||
|
||||
// Remove any link actions registered for the previous message binding.
|
||||
// RecyclerView reuses views, so stale actions from a previous item must be cleared.
|
||||
for (int id : linkActionIds) {
|
||||
ViewCompat.removeAccessibilityAction(this, id);
|
||||
}
|
||||
linkActionIds.clear();
|
||||
|
||||
String subject = messageRecord.getSubject();
|
||||
String text = messageRecord.getText();
|
||||
|
||||
@@ -428,6 +442,31 @@ public class ConversationItem extends BaseConversationItem
|
||||
}
|
||||
bodyText.setText(spannable);
|
||||
bodyText.setVisibility(View.VISIBLE);
|
||||
|
||||
// Register a TalkBack "Actions" entry for each link in the message.
|
||||
// ViewCompat.addAccessibilityAction is used instead of an AccessibilityDelegateCompat because
|
||||
// it is the modern, reliable API (available since androidx.core 1.5.0) that TalkBack always
|
||||
// surfaces under the "Actions" submenu of the local context menu (L-gesture).
|
||||
// The actions are registered unconditionally — no isTouchExplorationEnabled() guard — because
|
||||
// the registration is cheap and the guard was a source of timing bugs at bind time.
|
||||
// Links are only present when not in batch-selection mode (Linkifier is not called in that
|
||||
// branch above), so no actions are added in batch-selection mode.
|
||||
if (spannable instanceof Spanned) {
|
||||
Spanned spanned = (Spanned) spannable;
|
||||
final TextView tv = bodyText;
|
||||
for (LongClickCopySpan span : spanned.getSpans(0, spanned.length(), LongClickCopySpan.class)) {
|
||||
int start = spanned.getSpanStart(span);
|
||||
int end = spanned.getSpanEnd(span);
|
||||
if (start >= 0 && end > start && end <= spanned.length()) {
|
||||
String linkText = spanned.subSequence(start, end).toString();
|
||||
String label = context.getString(R.string.accessibility_link_action, linkText);
|
||||
linkActionIds.add(ViewCompat.addAccessibilityAction(this, label, (v, args) -> {
|
||||
span.onClick(tv);
|
||||
return true;
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int downloadState = messageRecord.getDownloadState();
|
||||
|
||||
@@ -3,9 +3,7 @@ package org.thoughtcrime.securesms.relay;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.ContextMenu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
@@ -45,10 +43,6 @@ public class RelayListActivity extends BaseActionBarActivity
|
||||
private RelayListAdapter adapter;
|
||||
private Rpc rpc;
|
||||
private int accId;
|
||||
|
||||
/** Relay selected for context menu */
|
||||
private EnteredLoginParam contextMenuRelay = null;
|
||||
private boolean contextMenuRelayIsMain = false;
|
||||
|
||||
/** QR provided via Intent extras needs to be saved to pass it to QrCodeHandler when authorization finishes */
|
||||
private String qrData = null;
|
||||
@@ -172,56 +166,6 @@ public class RelayListActivity extends BaseActionBarActivity
|
||||
.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRelayLongClick(View view, EnteredLoginParam relay, boolean isMain) {
|
||||
contextMenuRelay = relay;
|
||||
contextMenuRelayIsMain = isMain;
|
||||
registerForContextMenu(view);
|
||||
openContextMenu(view);
|
||||
unregisterForContextMenu(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
|
||||
super.onCreateContextMenu(menu, v, menuInfo);
|
||||
getMenuInflater().inflate(R.menu.relay_item_context, menu);
|
||||
|
||||
// Hide delete option if this is the main relay
|
||||
if (contextMenuRelayIsMain) {
|
||||
menu.findItem(R.id.menu_delete).setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onContextMenuClosed(android.view.Menu menu) {
|
||||
super.onContextMenuClosed(menu);
|
||||
clearContextMenuState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onContextItemSelected(@NonNull MenuItem item) {
|
||||
int itemId = item.getItemId();
|
||||
if (itemId == R.id.menu_edit) {
|
||||
if (contextMenuRelay != null) {
|
||||
onRelayEdit(contextMenuRelay);
|
||||
clearContextMenuState();
|
||||
}
|
||||
return true;
|
||||
} else if (itemId == R.id.menu_delete) {
|
||||
if (contextMenuRelay != null) {
|
||||
onRelayDelete(contextMenuRelay);
|
||||
clearContextMenuState();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return super.onContextItemSelected(item);
|
||||
}
|
||||
|
||||
private void clearContextMenuState() {
|
||||
contextMenuRelay = null;
|
||||
contextMenuRelayIsMain = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
||||
if (item.getItemId() == android.R.id.home) {
|
||||
|
||||
@@ -27,7 +27,6 @@ public class RelayListAdapter extends RecyclerView.Adapter<RelayListAdapter.Rela
|
||||
void onRelayClick(EnteredLoginParam relay);
|
||||
void onRelayEdit(EnteredLoginParam relay);
|
||||
void onRelayDelete(EnteredLoginParam relay);
|
||||
void onRelayLongClick(View view, EnteredLoginParam relay, boolean isMain);
|
||||
}
|
||||
|
||||
public RelayListAdapter(OnRelayClickListener listener) {
|
||||
@@ -68,12 +67,16 @@ public class RelayListAdapter extends RecyclerView.Adapter<RelayListAdapter.Rela
|
||||
private final TextView titleText;
|
||||
private final TextView subtitleText;
|
||||
private final ImageView mainIndicator;
|
||||
private final ImageView editButton;
|
||||
private final ImageView deleteButton;
|
||||
|
||||
public RelayViewHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
titleText = itemView.findViewById(R.id.title);
|
||||
subtitleText = itemView.findViewById(R.id.subtitle);
|
||||
mainIndicator = itemView.findViewById(R.id.main_indicator);
|
||||
editButton = itemView.findViewById(R.id.edit_button);
|
||||
deleteButton = itemView.findViewById(R.id.delete_button);
|
||||
}
|
||||
|
||||
public void bind(EnteredLoginParam relay, boolean isMain, OnRelayClickListener listener) {
|
||||
@@ -81,6 +84,7 @@ public class RelayListAdapter extends RecyclerView.Adapter<RelayListAdapter.Rela
|
||||
titleText.setText(parts.length == 2? parts[1] : parts[0]);
|
||||
subtitleText.setText(parts.length == 2? parts[0] : "");
|
||||
mainIndicator.setVisibility(isMain ? View.VISIBLE : View.INVISIBLE);
|
||||
deleteButton.setVisibility(isMain ? View.GONE : View.VISIBLE);
|
||||
|
||||
itemView.setOnClickListener(v -> {
|
||||
if (listener != null) {
|
||||
@@ -88,11 +92,16 @@ public class RelayListAdapter extends RecyclerView.Adapter<RelayListAdapter.Rela
|
||||
}
|
||||
});
|
||||
|
||||
itemView.setOnLongClickListener(v -> {
|
||||
editButton.setOnClickListener(v -> {
|
||||
if (listener != null) {
|
||||
listener.onRelayLongClick(v, relay, isMain);
|
||||
listener.onRelayEdit(relay);
|
||||
}
|
||||
});
|
||||
|
||||
deleteButton.setOnClickListener(v -> {
|
||||
if (listener != null) {
|
||||
listener.onRelayDelete(relay);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,4 +59,30 @@
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/delete_button"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="72dp"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:contentDescription="@string/remove_transport"
|
||||
android:background="@drawable/touch_highlight_background"
|
||||
android:src="@drawable/ic_delete_white_24dp"
|
||||
app:tint="?attr/conversation_list_item_date_color" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/edit_button"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="72dp"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:contentDescription="@string/edit_transport"
|
||||
android:background="@drawable/touch_highlight_background"
|
||||
android:src="@drawable/ic_create_white_24dp"
|
||||
app:tint="?attr/conversation_list_item_date_color" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item android:id="@+id/menu_edit"
|
||||
android:title="@string/edit_transport"/>
|
||||
|
||||
<item android:id="@+id/menu_delete"
|
||||
android:title="@string/remove_transport"/>
|
||||
|
||||
</menu>
|
||||
@@ -1179,6 +1179,9 @@
|
||||
<string name="perm_enable_bg_reminder_title">Tap here to receive messages while Delta Chat is in the background.</string>
|
||||
<string name="perm_enable_bg_already_done">You already allowed Delta Chat to receive messages in the background.\n\nIf messages still do not arrive in background, please also check your system settings.</string>
|
||||
|
||||
<!-- Accessibility -->
|
||||
<string name="accessibility_link_action">Open link: %1$s</string>
|
||||
|
||||
<!-- device messages for updates -->
|
||||
<string name="update_2_0">What\'s new?\n\n💯 End-to-end encryption is reliable and forever now. Padlocks 🔒 are gone!\n\n✉️ Classic email without end-to-end encryption is marked with a letter symbol\n\n😻 New enhanced profile screen for all your contacts\n\n🔲 New button for quick access to apps used in a chat\n\n❤️ Please donate to help us remain independent and continue to bring improvements: %1$s</string>
|
||||
</resources>
|
||||
|
||||
Reference in New Issue
Block a user