diff --git a/CHANGELOG-upstream.md b/CHANGELOG-upstream.md index 7dd07ad0f..e98854105 100644 --- a/CHANGELOG-upstream.md +++ b/CHANGELOG-upstream.md @@ -2,6 +2,9 @@ ## Unreleased +* Improve switch speed when changing profiles +* Allow to switch profile when sharing or forwarding +* Display message views count for channel owners * Don't notify notification-to-all from in-chat apps if the chat is muted * Allow to see inbox quota for all relays in connectivity screen * Truncate file names in the middle, not at the end; important information are more often at the end diff --git a/src/main/java/com/b44t/messenger/DcAccounts.java b/src/main/java/com/b44t/messenger/DcAccounts.java index 43e986e45..c7e3c121c 100644 --- a/src/main/java/com/b44t/messenger/DcAccounts.java +++ b/src/main/java/com/b44t/messenger/DcAccounts.java @@ -4,6 +4,7 @@ public class DcAccounts { public DcAccounts(String dir) { accountsCPtr = createAccountsCPtr(dir); + if (accountsCPtr == 0) throw new RuntimeException("createAccountsCPtr() returned null pointer"); } @Override diff --git a/src/main/java/org/thoughtcrime/securesms/AllMediaDocumentsFragment.java b/src/main/java/org/thoughtcrime/securesms/AllMediaDocumentsFragment.java index 62407dcd0..afe3a653e 100644 --- a/src/main/java/org/thoughtcrime/securesms/AllMediaDocumentsFragment.java +++ b/src/main/java/org/thoughtcrime/securesms/AllMediaDocumentsFragment.java @@ -54,7 +54,6 @@ public class AllMediaDocumentsFragment public void onCreate(Bundle bundle) { super.onCreate(bundle); - dcContext = DcHelper.getContext(getContext()); chatId = getArguments().getInt(CHAT_ID_EXTRA, -1); viewtype1 = getArguments().getInt(VIEWTYPE1, 0); viewtype2 = getArguments().getInt(VIEWTYPE2, 0); diff --git a/src/main/java/org/thoughtcrime/securesms/AllMediaGalleryFragment.java b/src/main/java/org/thoughtcrime/securesms/AllMediaGalleryFragment.java index 48f8b4635..57103cf02 100644 --- a/src/main/java/org/thoughtcrime/securesms/AllMediaGalleryFragment.java +++ b/src/main/java/org/thoughtcrime/securesms/AllMediaGalleryFragment.java @@ -52,7 +52,6 @@ public class AllMediaGalleryFragment public void onCreate(Bundle bundle) { super.onCreate(bundle); - dcContext = DcHelper.getContext(getContext()); chatId = getArguments().getInt(CHAT_ID_EXTRA, -1); getLoaderManager().initLoader(0, null, this); diff --git a/src/main/java/org/thoughtcrime/securesms/ConversationActivity.java b/src/main/java/org/thoughtcrime/securesms/ConversationActivity.java index 791b2493d..f1d89690c 100644 --- a/src/main/java/org/thoughtcrime/securesms/ConversationActivity.java +++ b/src/main/java/org/thoughtcrime/securesms/ConversationActivity.java @@ -188,7 +188,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity private ApplicationContext context; private Recipient recipient; - private DcContext dcContext; private Rpc rpc; private DcChat dcChat = new DcChat(0, 0); private int chatId; @@ -202,7 +201,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity @Override protected void onCreate(Bundle state, boolean ready) { this.context = ApplicationContext.getInstance(getApplicationContext()); - this.dcContext = DcHelper.getContext(context); this.rpc = DcHelper.getRpc(context); supportRequestWindowFeature(WindowCompat.FEATURE_ACTION_BAR_OVERLAY); @@ -250,17 +248,23 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity } }); + setDcEventListener(); + handleRelaying(); + } + + private void setDcEventListener() { DcEventCenter eventCenter = DcHelper.getEventCenter(this); - eventCenter.addObserver(DcContext.DC_EVENT_CHAT_MODIFIED, this); - eventCenter.addObserver(DcContext.DC_EVENT_CHAT_EPHEMERAL_TIMER_MODIFIED, this); - eventCenter.addObserver(DcContext.DC_EVENT_CONTACTS_CHANGED, this); + // first cleanup in case it was already registered for other chat + eventCenter.removeObservers(this); + + eventCenter.addMultiAccountObserver(DcContext.DC_EVENT_CHAT_MODIFIED, this); + eventCenter.addMultiAccountObserver(DcContext.DC_EVENT_CHAT_EPHEMERAL_TIMER_MODIFIED, this); + eventCenter.addMultiAccountObserver(DcContext.DC_EVENT_CONTACTS_CHANGED, this); if (!isMultiUser()) { - eventCenter.addObserver(DcContext.DC_EVENT_INCOMING_MSG, this); - eventCenter.addObserver(DcContext.DC_EVENT_MSG_READ, this); + eventCenter.addMultiAccountObserver(DcContext.DC_EVENT_INCOMING_MSG, this); + eventCenter.addMultiAccountObserver(DcContext.DC_EVENT_MSG_READ, this); } - - handleRelaying(); } @Override @@ -286,6 +290,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity } }); + setDcEventListener(); // reset event listener handleRelaying(); if (fragment != null) { @@ -312,7 +317,13 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity titleView.setTitle(glideRequests, dcChat); - DcHelper.getNotificationCenter(this).updateVisibleChat(dcContext.getAccountId(), chatId); + try { + int accId = rpc.getSelectedAccountId(); + DcHelper.getNotificationCenter(this).updateVisibleChat(accId, chatId); + } catch (RpcException e) { + Log.e(TAG, "rpc.getSelectedAccountId() failed", e); + } + attachmentManager.onResume(); } @@ -402,7 +413,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity break; case GROUP_EDIT: - dcChat = dcContext.getChat(chatId); + dcChat = DcHelper.getContext(context).getChat(chatId); titleView.setTitle(glideRequests, dcChat); break; @@ -603,6 +614,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity //////// Event Handlers private void handleEphemeralMessages() { + DcContext dcContext = DcHelper.getContext(context); int preselected = dcContext.getChatEphemeralTimer(chatId); EphemeralMessagesDialog.show(this, preselected, duration -> { dcContext.setChatEphemeralTimer(chatId, (int) duration); @@ -628,6 +640,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity } private void handleMuteNotifications() { + DcContext dcContext = DcHelper.getContext(context); if(!dcChat.isMuted()) { MuteDialog.show(this, duration -> { dcContext.setChatMuteDuration(chatId, duration); @@ -663,7 +676,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity AlertDialog dialog = new AlertDialog.Builder(this) .setMessage(getString(R.string.ask_leave_group)) .setPositiveButton(leaveLabel, (d, which) -> { - dcContext.removeContactFromChat(chatId, DcContact.DC_CONTACT_ID_SELF); + DcHelper.getContext(context).removeContactFromChat(chatId, DcContact.DC_CONTACT_ID_SELF); Toast.makeText(this, getString(R.string.done), Toast.LENGTH_SHORT).show(); }) .setNegativeButton(R.string.cancel, null) @@ -672,6 +685,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity } private void handleArchiveChat() { + DcContext dcContext = DcHelper.getContext(context); int newVisibility = isArchived() ? DcChat.DC_CHAT_VISIBILITY_NORMAL : DcChat.DC_CHAT_VISIBILITY_ARCHIVED; dcContext.setChatVisibility(chatId, newVisibility); @@ -687,7 +701,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity AlertDialog dialog = new AlertDialog.Builder(this) .setMessage(getResources().getString(R.string.ask_delete_named_chat, dcChat.getName())) .setPositiveButton(R.string.delete, (d, which) -> { - dcContext.deleteChat(chatId); + DcHelper.getContext(context).deleteChat(chatId); DirectShareUtil.clearShortcut(this, chatId); finish(); }) @@ -717,6 +731,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity } private void handleForwarding() { + DcContext dcContext = DcHelper.getContext(context); DcChat dcChat = dcContext.getChat(chatId); if (dcChat.isSelfTalk()) { SendRelayedMessageUtil.immediatelyRelay(this, chatId); @@ -765,7 +780,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity SendRelayedMessageUtil.immediatelyRelay(this, chatId); } else { Uri uri = uriList.isEmpty()? null : uriList.get(0); - dcContext.setDraft(chatId, SendRelayedMessageUtil.createMessage(this, uri, ShareUtil.getSharedType(this), null, null, getSharedText(this))); + DcHelper.getContext(context).setDraft(chatId, SendRelayedMessageUtil.createMessage(this, uri, ShareUtil.getSharedType(this), null, null, getSharedText(this))); } initializeDraft(); } @@ -780,7 +795,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity private ListenableFuture initializeDraft() { isEditing = false; final SettableFuture future = new SettableFuture<>(); - DcMsg draft = dcContext.getDraft(chatId); + DcMsg draft = DcHelper.getContext(context).getDraft(chatId); final String sharedText = ShareUtil.getSharedText(this); if (!draft.isOk()) { @@ -941,7 +956,15 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity } private void initializeBackground() { - String backgroundImagePath = Prefs.getBackgroundImagePath(this, dcContext.getAccountId()); + int accId; + try { + accId = rpc.getSelectedAccountId(); + } catch (RpcException e) { + Log.e(TAG, "rpc.getSelectedAccountId() failed", e); + return; + } + + String backgroundImagePath = Prefs.getBackgroundImagePath(this, accId); Drawable background; if(!backgroundImagePath.isEmpty()) { background = Drawable.createFromPath(backgroundImagePath); @@ -967,17 +990,17 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity } private void initializeResources() { - int accountId = getIntent().getIntExtra(ACCOUNT_ID_EXTRA, dcContext.getAccountId()); - if (accountId != dcContext.getAccountId()) { + int selectedAccId = DcHelper.getContext(context).getAccountId(); + int accountId = getIntent().getIntExtra(ACCOUNT_ID_EXTRA, selectedAccId); + if (accountId != selectedAccId) { switchedProfile = true; AccountManager.getInstance().switchAccount(context, accountId); - fragment.dcContext = dcContext = context.getDcContext(); initializeBackground(); } chatId = getIntent().getIntExtra(CHAT_ID_EXTRA, -1); if(chatId == DcChat.DC_CHAT_NO_CHAT) throw new IllegalStateException("can't display a conversation for no chat."); - dcChat = dcContext.getChat(chatId); + dcChat = DcHelper.getContext(context).getChat(chatId); recipient = new Recipient(this, dcChat); glideRequests = GlideApp.with(this); @@ -1044,11 +1067,11 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity } try { - byte[] vcard = rpc.makeVcard(dcContext.getAccountId(), Collections.singletonList(contactId)).getBytes(); + byte[] vcard = rpc.makeVcard(rpc.getSelectedAccountId(), Collections.singletonList(contactId)).getBytes(); String mimeType = "application/octet-stream"; setMedia(PersistentBlobProvider.getInstance().create(this, vcard, mimeType, "vcard.vcf"), MediaType.DOCUMENT); } catch (RpcException e) { - Log.e(TAG, "makeVcard() failed", e); + Log.e(TAG, "RPC failed", e); } } @@ -1087,6 +1110,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity inputPanel.clearSubject(); } + DcContext dcContext = DcHelper.getContext(context); Util.runOnAnyBackgroundThread(() -> { DcMsg msg = null; int recompress = 0; @@ -1219,7 +1243,12 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity if (refreshFragment) { fragment.reload(recipient, chatId); - DcHelper.getNotificationCenter(this).updateVisibleChat(dcContext.getAccountId(), chatId); + try { + int accId = rpc.getSelectedAccountId(); + DcHelper.getNotificationCenter(this).updateVisibleChat(accId, chatId); + } catch (RpcException e) { + Log.e(TAG, "rpc.getSelectedAccountId() failed", e); + } } fragment.scrollToBottom(); @@ -1381,6 +1410,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity Optional quote = inputPanel.getQuote(); inputPanel.clearQuote(); + DcContext dcContext = DcHelper.getContext(context); DcMsg msg = new DcMsg(dcContext, DcMsg.DC_MSG_STICKER); if (quote.isPresent()) { msg.setQuote(quote.get().getQuotedMsg()); @@ -1502,7 +1532,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity if (isEditing) composeText.setText(""); isEditing = false; // If you modify these lines you may also want to modify ConversationItem.setQuote(): - Recipient author = new Recipient(this, dcContext.getContact(msg.getFromId())); + Recipient author = new Recipient(this, DcHelper.getContext(context).getContact(msg.getFromId())); SlideDeck slideDeck = new SlideDeck(); if (msg.hasFile()) { @@ -1525,7 +1555,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity @Override public void handleEditMessage(DcMsg msg) { isEditing = true; - Recipient author = new Recipient(this, dcContext.getContact(msg.getFromId())); + Recipient author = new Recipient(this, DcHelper.getContext(context).getContact(msg.getFromId())); SlideDeck slideDeck = new SlideDeck(); String text = msg.getSummarytext(500); @@ -1550,6 +1580,9 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity @Override public void handleEvent(@NonNull DcEvent event) { + DcContext dcContext = DcHelper.getContext(context); + if (event.getAccountId() != dcContext.getAccountId()) return; + int eventId = event.getId(); if ((eventId == DcContext.DC_EVENT_CHAT_MODIFIED && event.getData1Int() == chatId) || (eventId == DcContext.DC_EVENT_CHAT_EPHEMERAL_TIMER_MODIFIED && event.getData1Int() == chatId) @@ -1636,7 +1669,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity } String normQuery = query.trim(); - searchResult = dcContext.searchMsgs(chatId, normQuery); + searchResult = DcHelper.getContext(context).searchMsgs(chatId, normQuery); if(searchResult.length>0) { searchResultPosition = searchResult.length - 1; @@ -1667,7 +1700,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity messageRequestBottomView.setVisibility(View.VISIBLE); messageRequestBottomView.setAcceptOnClickListener(v -> { - dcContext.acceptChat(chatId); + DcHelper.getContext(context).acceptChat(chatId); messageRequestBottomView.setVisibility(View.GONE); composePanel.setVisibility(View.VISIBLE); }); @@ -1685,7 +1718,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity // avoid showing compose panel on receiving DC_EVENT_CONTACTS_CHANGED for the chat that is no longer a request after blocking DcHelper.getEventCenter(this).removeObserver(DcContext.DC_EVENT_CONTACTS_CHANGED, this); - dcContext.blockChat(chatId); + DcHelper.getContext(context).blockChat(chatId); Bundle extras = new Bundle(); extras.putInt(ConversationListFragment.RELOAD_LIST, 1); handleReturnToConversationList(extras); diff --git a/src/main/java/org/thoughtcrime/securesms/ConversationFragment.java b/src/main/java/org/thoughtcrime/securesms/ConversationFragment.java index ecd0b8d34..1cbce4a76 100644 --- a/src/main/java/org/thoughtcrime/securesms/ConversationFragment.java +++ b/src/main/java/org/thoughtcrime/securesms/ConversationFragment.java @@ -68,6 +68,7 @@ import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.ViewUtil; import org.thoughtcrime.securesms.util.views.ConversationAdaptiveActionsToolbar; +import java.util.ArrayList; import java.util.Collections; import java.util.LinkedList; import java.util.List; @@ -75,6 +76,9 @@ import java.util.Set; import java.util.Timer; import java.util.TimerTask; +import chat.delta.rpc.Rpc; +import chat.delta.rpc.RpcException; + @SuppressLint("StaticFieldLeak") public class ConversationFragment extends MessageSelectorFragment { @@ -103,11 +107,12 @@ public class ConversationFragment extends MessageSelectorFragment public boolean isPaused; private Debouncer markseenDebouncer; + private Rpc rpc; @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); - this.dcContext = DcHelper.getContext(getContext()); + rpc = DcHelper.getRpc(getContext()); DcEventCenter eventCenter = DcHelper.getEventCenter(getContext()); eventCenter.addObserver(DcContext.DC_EVENT_INCOMING_MSG, this); @@ -208,8 +213,13 @@ public class ConversationFragment extends MessageSelectorFragment @Override public void onResume() { super.onResume(); - - Util.runOnBackground(() -> dcContext.marknoticedChat((int) chatId)); + Util.runOnBackground(() -> { + try { + rpc.marknoticedChat(rpc.getSelectedAccountId(), (int) chatId); + } catch (RpcException e) { + Log.e(TAG, "RPC error", e); + } + }); if (list.getAdapter() != null) { list.getAdapter().notifyDataSetChanged(); } @@ -288,7 +298,12 @@ public class ConversationFragment extends MessageSelectorFragment dateDecoration = new StickyHeaderDecoration(adapter, false, false); list.addItemDecoration(dateDecoration); - int freshMsgs = dcContext.getFreshMsgCount((int) chatId); + int freshMsgs = 0; + try { + freshMsgs = rpc.getFreshMsgCnt(rpc.getSelectedAccountId(), (int) chatId); + } catch (RpcException e) { + Log.e(TAG, "RPC error", e); + } SetStartingPositionLinearLayoutManager layoutManager = (SetStartingPositionLinearLayoutManager) list.getLayoutManager(); if (startingPosition > -1) { layoutManager.setStartingPosition(startingPosition); @@ -448,6 +463,7 @@ public class ConversationFragment extends MessageSelectorFragment StringBuilder result = new StringBuilder(); + DcContext dcContext = DcHelper.getContext(getContext()); DcMsg prevMsg = new DcMsg(dcContext, DcMsg.DC_MSG_TEXT); for (DcMsg msg : dcMsgsList) { if (result.length() > 0) { @@ -476,9 +492,13 @@ public class ConversationFragment extends MessageSelectorFragment private void handleForwardMessage(final Set messageRecords) { Intent composeIntent = new Intent(); int[] msgIds = DcMsg.msgSetToIds(messageRecords); - setForwardingMessageIds(composeIntent, msgIds); - ConversationListRelayingActivity.start(this, composeIntent); - getActivity().overridePendingTransition(R.anim.slide_from_right, R.anim.fade_scale_out); + try { + setForwardingMessageIds(composeIntent, msgIds, rpc.getSelectedAccountId()); + ConversationListRelayingActivity.start(this, composeIntent); + getActivity().overridePendingTransition(R.anim.slide_from_right, R.anim.fade_scale_out); + } catch (RpcException e) { + Log.e(TAG, "RPC error", e); + } } @SuppressLint("RestrictedApi") @@ -504,6 +524,7 @@ public class ConversationFragment extends MessageSelectorFragment private void handleReplyMessagePrivately(final DcMsg msg) { if (getActivity() != null) { + DcContext dcContext = DcHelper.getContext(getActivity()); int privateChatId = dcContext.createChatByContactId(msg.getFromId()); DcMsg replyMsg = new DcMsg(dcContext, DcMsg.DC_MSG_TEXT); replyMsg.setQuote(msg); @@ -520,10 +541,14 @@ public class ConversationFragment extends MessageSelectorFragment private void handleToggleSave(final Set messageRecords) { DcMsg msg = getSelectedMessageRecord(messageRecords); - if (msg.getSavedMsgId() != 0) { - dcContext.deleteMsgs(new int[]{msg.getSavedMsgId()}); - } else { - dcContext.saveMsgs(new int[]{msg.getId()}); + try { + if (msg.getSavedMsgId() != 0) { + rpc.deleteMessages(rpc.getSelectedAccountId(), Collections.singletonList(msg.getSavedMsgId())); + } else { + rpc.saveMsgs(rpc.getSelectedAccountId(), Collections.singletonList(msg.getId())); + } + } catch (RpcException e) { + Log.e(TAG, "RPC error", e); } } @@ -593,7 +618,7 @@ public class ConversationFragment extends MessageSelectorFragment } private void updateLocationButton() { - floatingLocationButton.setVisibility(dcContext.isSendingLocationsToChat((int) chatId)? View.VISIBLE : View.GONE); + floatingLocationButton.setVisibility(DcHelper.getContext(getContext()).isSendingLocationsToChat((int) chatId)? View.VISIBLE : View.GONE); } private void scrollAndHighlight(final int pos, boolean smooth) { @@ -729,16 +754,20 @@ public class ConversationFragment extends MessageSelectorFragment return; } - int[] ids = new int[lastPos - firstPos + 1]; - int index = 0; + ArrayList ids = new ArrayList<>(lastPos - firstPos + 1); for(int pos = firstPos; pos <= lastPos; pos++) { DcMsg message = ((ConversationAdapter) list.getAdapter()).getMsg(pos); if (message.getFromId() != DC_CONTACT_ID_SELF) { - ids[index] = message.getId(); - index++; + ids.add(message.getId()); } } - Util.runOnAnyBackgroundThread(() -> dcContext.markseenMsgs(ids)); + Util.runOnAnyBackgroundThread(() -> { + try { + rpc.markseenMsgs(rpc.getSelectedAccountId(), ids); + } catch (RpcException e) { + Log.e(TAG, "RPC error", e); + } + }); } private class ConversationFragmentItemClickListener implements ItemClickListener { @@ -783,7 +812,7 @@ public class ConversationFragment extends MessageSelectorFragment startActivity(intent); } else { - String self_mail = dcContext.getConfig("configured_mail_user"); + String self_mail = DcHelper.getContext(getContext()).getConfig("configured_mail_user"); if (self_mail != null && !self_mail.isEmpty() && messageRecord.getText().contains(self_mail) && getListAdapter().getChat().isDeviceTalk()) { @@ -822,7 +851,7 @@ public class ConversationFragment extends MessageSelectorFragment if (foreignChatId != 0 && foreignChatId != chatId) { Intent intent = new Intent(getActivity(), ConversationActivity.class); intent.putExtra(ConversationActivity.CHAT_ID_EXTRA, foreignChatId); - int start = DcMsg.getMessagePosition(original, dcContext); + int start = DcMsg.getMessagePosition(original, DcHelper.getContext(getContext())); intent.putExtra(ConversationActivity.STARTING_POSITION_EXTRA, start); intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); ((ConversationActivity) getActivity()).hideSoftKeyboard(); @@ -838,7 +867,7 @@ public class ConversationFragment extends MessageSelectorFragment @Override public void onJumpToOriginalClicked(DcMsg messageRecord) { - jumpToOriginal(dcContext.getMsg(messageRecord.getOriginalMsgId())); + jumpToOriginal(DcHelper.getContext(getContext()).getMsg(messageRecord.getOriginalMsgId())); } @Override @@ -857,7 +886,7 @@ public class ConversationFragment extends MessageSelectorFragment @Override public void onDownloadClicked(DcMsg messageRecord) { - dcContext.downloadFullMsg(messageRecord.getId()); + DcHelper.getContext(getContext()).downloadFullMsg(messageRecord.getId()); } @Override diff --git a/src/main/java/org/thoughtcrime/securesms/ConversationListActivity.java b/src/main/java/org/thoughtcrime/securesms/ConversationListActivity.java index befac3894..3c4c8793c 100644 --- a/src/main/java/org/thoughtcrime/securesms/ConversationListActivity.java +++ b/src/main/java/org/thoughtcrime/securesms/ConversationListActivity.java @@ -16,8 +16,6 @@ */ package org.thoughtcrime.securesms; -import static org.thoughtcrime.securesms.ConversationActivity.CHAT_ID_EXTRA; -import static org.thoughtcrime.securesms.ConversationActivity.STARTING_POSITION_EXTRA; import static org.thoughtcrime.securesms.connect.DcHelper.CONFIG_PROXY_ENABLED; import static org.thoughtcrime.securesms.connect.DcHelper.CONFIG_PROXY_URL; import static org.thoughtcrime.securesms.util.ShareUtil.acquireRelayMessageContent; @@ -25,10 +23,12 @@ import static org.thoughtcrime.securesms.util.ShareUtil.getDirectSharingChatId; import static org.thoughtcrime.securesms.util.ShareUtil.getSharedTitle; import static org.thoughtcrime.securesms.util.ShareUtil.isDirectSharing; import static org.thoughtcrime.securesms.util.ShareUtil.isForwarding; +import static org.thoughtcrime.securesms.util.ShareUtil.getForwardedMessageAccountId; import static org.thoughtcrime.securesms.util.ShareUtil.isRelayingMessageContent; import static org.thoughtcrime.securesms.util.ShareUtil.resetRelayingMessageContent; import android.Manifest; +import android.app.Activity; import android.content.Intent; import android.content.res.TypedArray; import android.graphics.Color; @@ -124,36 +124,9 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit @Override protected void onCreate(Bundle icicle, boolean ready) { - // update messages - for new messages, do not reuse or modify strings but create new ones. - // it is not needed to keep all past update messages, however, when deleted, also the strings should be deleted. - try { - DcContext dcContext = DcHelper.getContext(this); - final String deviceMsgLabel = "update_2_33_1_android"; - if (!dcContext.wasDeviceMsgEverAdded(deviceMsgLabel)) { - DcMsg msg = null; - if (!getIntent().getBooleanExtra(FROM_WELCOME, false)) { - msg = new DcMsg(dcContext, DcMsg.DC_MSG_TEXT); - - // InputStream inputStream = getResources().getAssets().open("device-messages/green-checkmark.jpg"); - // String outputFile = DcHelper.getBlobdirFile(dcContext, "green-checkmark", ".jpg"); - // Util.copy(inputStream, new FileOutputStream(outputFile)); - // msg.setFile(outputFile, "image/jpeg"); - - msg.setText(getString(R.string.update_2_33, "https://arcanechat.me/#contribute")); - } - dcContext.addDeviceMsg(deviceMsgLabel, msg); - - if (Prefs.getStringPreference(this, Prefs.LAST_DEVICE_MSG_LABEL, "").equals(deviceMsgLabel)) { - int deviceChatId = dcContext.getChatIdByContactId(DcContact.DC_CONTACT_ID_DEVICE); - if (deviceChatId != 0) { - dcContext.marknoticedChat(deviceChatId); - } - } - Prefs.setStringPreference(this, Prefs.LAST_DEVICE_MSG_LABEL, deviceMsgLabel); - } - - } catch(Exception e) { - e.printStackTrace(); + addDeviceMessages(getIntent().getBooleanExtra(FROM_WELCOME, false)); + if (getIntent().getIntExtra(ACCOUNT_ID_EXTRA, -1) <= 0) { + getIntent().putExtra(ACCOUNT_ID_EXTRA, DcHelper.getContext(this).getAccountId()); } // create view @@ -183,8 +156,20 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit if (searchToolbar.isVisible()) { searchToolbar.collapse(); } else { - if (isRelayingMessageContent(ConversationListActivity.this)) { - handleResetRelaying(); + Activity activity = ConversationListActivity.this; + if (isRelayingMessageContent(activity)) { + int selectedAccId = DcHelper.getContext(activity).getAccountId(); + int initialAccId = getIntent().getIntExtra(ACCOUNT_ID_EXTRA, selectedAccId); + if (initialAccId != selectedAccId) { + // allowing to go back is dangerous, it could be activity on previously selected account, + // instead of figuring out account rollback in onResume in each activity (conversation, gallery, media preview, webxdc, etc.) + // just clear the back stack and stay in newly selected account + finishAffinity(); + startActivity(new Intent(activity, ConversationListActivity.class)); + return; + } else { + handleResetRelaying(); + } } setEnabled(false); @@ -196,10 +181,10 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit TooltipCompat.setTooltipText(searchAction, getText(R.string.search_explain)); TooltipCompat.setTooltipText(selfAvatar, getText(R.string.switch_account)); - selfAvatar.setOnClickListener(v -> AccountManager.getInstance().showSwitchAccountMenu(this)); + selfAvatar.setOnClickListener(v -> AccountManager.getInstance().showSwitchAccountMenu(this, false)); findViewById(R.id.avatar_and_title).setOnClickListener(v -> { if (!isRelayingMessageContent(this)) { - AccountManager.getInstance().showSwitchAccountMenu(this); + AccountManager.getInstance().showSwitchAccountMenu(this, false); } }); @@ -274,24 +259,29 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit } super.onNewIntent(intent); setIntent(intent); + if (getIntent().getIntExtra(ACCOUNT_ID_EXTRA, -1) <= 0) { + getIntent().putExtra(ACCOUNT_ID_EXTRA, DcHelper.getContext(this).getAccountId()); + } refresh(); conversationListFragment.onNewIntent(); invalidateOptionsMenu(); } private void refresh() { - DcContext dcContext = DcHelper.getContext(this); - int accountId = getIntent().getIntExtra(ACCOUNT_ID_EXTRA, dcContext.getAccountId()); + int selectedAccId = DcHelper.getContext(this).getAccountId(); + int accountId = getIntent().getIntExtra(ACCOUNT_ID_EXTRA, selectedAccId); if (getIntent().getBooleanExtra(CLEAR_NOTIFICATIONS, false)) { DcHelper.getNotificationCenter(this).removeAllNotifications(accountId); } - if (accountId != dcContext.getAccountId()) { - AccountManager.getInstance().switchAccountAndStartActivity(this, accountId); + if (accountId != selectedAccId) { + AccountManager.getInstance().switchAccount(this, accountId); + onProfileSwitched(accountId); + } else { + refreshAvatar(); + refreshUnreadIndicator(); + refreshTitle(); } - refreshAvatar(); - refreshUnreadIndicator(); - refreshTitle(); handleOpenpgp4fpr(); if (isDirectSharing(this)) { openConversation(getDirectSharingChatId(this), -1); @@ -476,6 +466,9 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit return true; } else if (itemId == R.id.menu_export_attachment) { handleSaveAttachment(); + } else if (itemId == R.id.menu_switch_account) { + AccountManager.getInstance().showSwitchAccountMenu(this, true); + return true; } return false; @@ -543,15 +536,17 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit searchToolbar.clearFocus(); final DcContext dcContext = DcHelper.getContext(this); - if (isForwarding(this) && dcContext.getChat(chatId).isSelfTalk()) { + int fwdAccId = getForwardedMessageAccountId(this); + if (fwdAccId == dcContext.getAccountId() && dcContext.getChat(chatId).isSelfTalk()) { SendRelayedMessageUtil.immediatelyRelay(this, chatId); Toast.makeText(this, DynamicTheme.getCheckmarkEmoji(this) + " " + getString(R.string.saved), Toast.LENGTH_SHORT).show(); handleResetRelaying(); finish(); } else { Intent intent = new Intent(this, ConversationActivity.class); - intent.putExtra(CHAT_ID_EXTRA, chatId); - intent.putExtra(STARTING_POSITION_EXTRA, startingPosition); + intent.putExtra(ConversationActivity.ACCOUNT_ID_EXTRA, dcContext.getAccountId()); + intent.putExtra(ConversationActivity.CHAT_ID_EXTRA, chatId); + intent.putExtra(ConversationActivity.STARTING_POSITION_EXTRA, startingPosition); if (isRelayingMessageContent(this)) { acquireRelayMessageContent(this, intent); } @@ -587,6 +582,48 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit startActivity(Intent.createChooser(intent, getString(R.string.chat_share_with_title))); } + private void addDeviceMessages(boolean fromWelcome) { + // update messages - for new messages, do not reuse or modify strings but create new ones. + // it is not needed to keep all past update messages, however, when deleted, also the strings should be deleted. + try { + DcContext dcContext = DcHelper.getContext(this); + final String deviceMsgLabel = "update_2_33_1_android"; + if (!dcContext.wasDeviceMsgEverAdded(deviceMsgLabel)) { + DcMsg msg = null; + if (!getIntent().getBooleanExtra(FROM_WELCOME, false)) { + msg = new DcMsg(dcContext, DcMsg.DC_MSG_TEXT); + + // InputStream inputStream = getResources().getAssets().open("device-messages/green-checkmark.jpg"); + // String outputFile = DcHelper.getBlobdirFile(dcContext, "green-checkmark", ".jpg"); + // Util.copy(inputStream, new FileOutputStream(outputFile)); + // msg.setFile(outputFile, "image/jpeg"); + + msg.setText(getString(R.string.update_2_33, "https://arcanechat.me/#contribute")); + } + dcContext.addDeviceMsg(deviceMsgLabel, msg); + + if (Prefs.getStringPreference(this, Prefs.LAST_DEVICE_MSG_LABEL, "").equals(deviceMsgLabel)) { + int deviceChatId = dcContext.getChatIdByContactId(DcContact.DC_CONTACT_ID_DEVICE); + if (deviceChatId != 0) { + dcContext.marknoticedChat(deviceChatId); + } + } + Prefs.setStringPreference(this, Prefs.LAST_DEVICE_MSG_LABEL, deviceMsgLabel); + } + + } catch(Exception e) { + e.printStackTrace(); + } + } + + public void onProfileSwitched(int profileId) { + addDeviceMessages(false); + refreshAvatar(); + refreshUnreadIndicator(); + refreshTitle(); + conversationListFragment.loadChatlist(); + } + public void onDeleteProfile(int profileId) { deleteProfileId = profileId; boolean result = ScreenLockUtil.applyScreenLock(this, getString(R.string.delete_account), getString(R.string.enter_system_secret_to_continue), REQUEST_CODE_CONFIRM_CREDENTIALS_DELETE_PROFILE); @@ -602,9 +639,14 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit accounts.removeAccount(profileId); if (selected) { DcContext selAcc = accounts.getSelectedAccount(); - AccountManager.getInstance().switchAccountAndStartActivity(this, selAcc.isOk()? selAcc.getAccountId() : 0); + if (selAcc.isOk()) { + AccountManager.getInstance().switchAccount(this, selAcc.getAccountId()); + onProfileSwitched(selAcc.getAccountId()); + } else { + AccountManager.getInstance().switchAccountAndStartActivity(this, 0); + } } else { - AccountManager.getInstance().showSwitchAccountMenu(this); + AccountManager.getInstance().showSwitchAccountMenu(this, false); } // title update needed to show "Delta Chat" in case there is only one profile left diff --git a/src/main/java/org/thoughtcrime/securesms/ConversationListFragment.java b/src/main/java/org/thoughtcrime/securesms/ConversationListFragment.java index 7d22831d1..e244cf1c4 100644 --- a/src/main/java/org/thoughtcrime/securesms/ConversationListFragment.java +++ b/src/main/java/org/thoughtcrime/securesms/ConversationListFragment.java @@ -260,7 +260,7 @@ public class ConversationListFragment extends BaseConversationListFragment }); } - private void loadChatlist() { + public void loadChatlist() { int listflags = 0; if (archive) { listflags |= DcContext.DC_GCL_ARCHIVED_ONLY; diff --git a/src/main/java/org/thoughtcrime/securesms/ConversationListItem.java b/src/main/java/org/thoughtcrime/securesms/ConversationListItem.java index a7a932925..4d99776d1 100644 --- a/src/main/java/org/thoughtcrime/securesms/ConversationListItem.java +++ b/src/main/java/org/thoughtcrime/securesms/ConversationListItem.java @@ -124,7 +124,6 @@ public class ConversationListItem extends RelativeLayout this.msgId = msgId; int state = dcSummary.getState(); - int unreadCount = thread.getUnreadCount(); if (highlightSubstring != null) { this.fromView.setText(getHighlightedSpan(recipient.getName(), highlightSubstring)); @@ -151,7 +150,7 @@ public class ConversationListItem extends RelativeLayout thread.getVisibility()==DcChat.DC_CHAT_VISIBILITY_PINNED? R.drawable.ic_pinned_chatlist : 0, 0 ); - setStatusIcons(thread.getVisibility(), state, unreadCount, thread.isContactRequest(), thread.isMuted() || chatId == DcChat.DC_CHAT_ID_ARCHIVED_LINK); + setStatusIcons(thread, state); setBatchState(batchMode); setBgColor(thread); @@ -240,14 +239,14 @@ public class ConversationListItem extends RelativeLayout return msgId; } - private void setStatusIcons(int visibility, int state, int unreadCount, boolean isContactRequest, boolean isMuted) { - if (visibility==DcChat.DC_CHAT_VISIBILITY_ARCHIVED) + private void setStatusIcons(ThreadRecord thread, int state) { + if (thread.getVisibility() == DcChat.DC_CHAT_VISIBILITY_ARCHIVED) { archivedBadgeView.setVisibility(View.VISIBLE); - requestBadgeView.setVisibility(isContactRequest ? View.VISIBLE : View.GONE); + requestBadgeView.setVisibility(thread.isContactRequest() ? View.VISIBLE : View.GONE); deliveryStatusIndicator.setNone(); } - else if (isContactRequest) { + else if (thread.isContactRequest()) { requestBadgeView.setVisibility(View.VISIBLE); archivedBadgeView.setVisibility(View.GONE); deliveryStatusIndicator.setNone(); @@ -259,14 +258,16 @@ public class ConversationListItem extends RelativeLayout if (state == DcMsg.DC_STATE_OUT_FAILED) { deliveryStatusIndicator.setFailed(); - } else if (state == DcMsg.DC_STATE_OUT_MDN_RCVD) { - deliveryStatusIndicator.setRead(); - } else if (state == DcMsg.DC_STATE_OUT_DELIVERED) { - deliveryStatusIndicator.setSent(); } else if (state == DcMsg.DC_STATE_OUT_PREPARING) { deliveryStatusIndicator.setPreparing(); } else if (state == DcMsg.DC_STATE_OUT_PENDING) { deliveryStatusIndicator.setPending(); + } else if (thread.isOutChannel()) { + deliveryStatusIndicator.setNone(); + } else if (state == DcMsg.DC_STATE_OUT_MDN_RCVD) { + deliveryStatusIndicator.setRead(); + } else if (state == DcMsg.DC_STATE_OUT_DELIVERED) { + deliveryStatusIndicator.setSent(); } else { deliveryStatusIndicator.setNone(); } @@ -278,11 +279,12 @@ public class ConversationListItem extends RelativeLayout } } - if(unreadCount==0 || isContactRequest) { + int unreadCount = thread.getUnreadCount(); + if(unreadCount==0 || thread.isContactRequest()) { unreadIndicator.setVisibility(View.GONE); } else { final int color; - if(isMuted){ + if(thread.isMuted() || chatId == DcChat.DC_CHAT_ID_ARCHIVED_LINK){ color = getResources().getColor(ThemeUtil.isDarkTheme(getContext()) ? R.color.unread_count_muted_dark : R.color.unread_count_muted); } else { final TypedArray attrs = getContext().obtainStyledAttributes(new int[] { diff --git a/src/main/java/org/thoughtcrime/securesms/MessageSelectorFragment.java b/src/main/java/org/thoughtcrime/securesms/MessageSelectorFragment.java index 1a61e7fdd..7eb050730 100644 --- a/src/main/java/org/thoughtcrime/securesms/MessageSelectorFragment.java +++ b/src/main/java/org/thoughtcrime/securesms/MessageSelectorFragment.java @@ -32,7 +32,6 @@ public abstract class MessageSelectorFragment implements DcEventCenter.DcEventDelegate { protected ActionMode actionMode; - protected DcContext dcContext; protected abstract void setCorrectMenuVisibility(Menu menu); @@ -48,7 +47,7 @@ public abstract class MessageSelectorFragment protected void handleDisplayDetails(DcMsg dcMsg) { View view = View.inflate(getActivity(), R.layout.message_details_view, null); TextView detailsText = view.findViewById(R.id.details_text); - detailsText.setText(dcContext.getMsgInfo(dcMsg.getId())); + detailsText.setText(DcHelper.getContext(getContext()).getMsgInfo(dcMsg.getId())); AlertDialog d = new AlertDialog.Builder(getActivity()) .setView(view) @@ -62,6 +61,7 @@ public abstract class MessageSelectorFragment } protected void handleDeleteMessages(int chatId, final int[] messageIds) { + DcContext dcContext = DcHelper.getContext(getContext()); DcChat dcChat = dcContext.getChat(chatId); boolean canDeleteForAll = true; if (dcChat.isEncrypted() && dcChat.canSend() && !dcChat.isSelfTalk()) { @@ -134,7 +134,7 @@ public abstract class MessageSelectorFragment protected void handleShowInChat(final DcMsg dcMsg) { Intent intent = new Intent(getContext(), ConversationActivity.class); intent.putExtra(ConversationActivity.CHAT_ID_EXTRA, dcMsg.getChatId()); - intent.putExtra(ConversationActivity.STARTING_POSITION_EXTRA, DcMsg.getMessagePosition(dcMsg, dcContext)); + intent.putExtra(ConversationActivity.STARTING_POSITION_EXTRA, DcMsg.getMessagePosition(dcMsg, DcHelper.getContext(getContext()))); startActivity(intent); } @@ -144,6 +144,7 @@ public abstract class MessageSelectorFragment protected void handleResendMessage(final Set dcMsgsSet) { int[] ids = DcMsg.msgSetToIds(dcMsgsSet); + DcContext dcContext = DcHelper.getContext(getContext()); Util.runOnAnyBackgroundThread(() -> { boolean success = dcContext.resendMsgs(ids); Util.runOnMain(() -> { diff --git a/src/main/java/org/thoughtcrime/securesms/accounts/AccountSelectionListFragment.java b/src/main/java/org/thoughtcrime/securesms/accounts/AccountSelectionListFragment.java index 96e9b76b1..d33a14cf1 100644 --- a/src/main/java/org/thoughtcrime/securesms/accounts/AccountSelectionListFragment.java +++ b/src/main/java/org/thoughtcrime/securesms/accounts/AccountSelectionListFragment.java @@ -50,10 +50,12 @@ public class AccountSelectionListFragment extends DialogFragment implements DcEv private final ConversationListActivity activity; private RecyclerView recyclerView; private AccountSelectionListAdapter adapter; + private final boolean selectOnly; - public AccountSelectionListFragment(ConversationListActivity activity) { + public AccountSelectionListFragment(ConversationListActivity activity, boolean selectOnly) { super(); this.activity = activity; + this.selectOnly = selectOnly; } @NonNull @@ -61,10 +63,12 @@ public class AccountSelectionListFragment extends DialogFragment implements DcEv public Dialog onCreateDialog(Bundle savedInstanceState) { AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()) .setTitle(R.string.switch_account) - .setNeutralButton(R.string.connectivity, ((dialog, which) -> { - startActivity(new Intent(getActivity(), ConnectivityActivity.class)); - })) .setNegativeButton(R.string.cancel, null); + if (!selectOnly) { + builder.setNeutralButton(R.string.connectivity, ((dialog, which) -> { + startActivity(new Intent(getActivity(), ConnectivityActivity.class)); + })); + } LayoutInflater inflater = getActivity().getLayoutInflater(); View view = inflater.inflate(R.layout.account_selection_list_fragment, null); @@ -99,18 +103,20 @@ public class AccountSelectionListFragment extends DialogFragment implements DcEv DcAccounts accounts = DcHelper.getAccounts(getActivity()); int[] accountIds = accounts.getAll(); - int[] ids = new int[accountIds.length + 1]; + int[] ids = new int[(selectOnly? 0 : 1) + accountIds.length]; int j = 0; for (int accountId : accountIds) { ids[j++] = accountId; } - ids[j] = DC_CONTACT_ID_ADD_ACCOUNT; + if (!selectOnly) ids[j] = DC_CONTACT_ID_ADD_ACCOUNT; adapter.changeData(ids, accounts.getSelectedAccount().getAccountId()); } @Override public void onCreateContextMenu(@NonNull ContextMenu menu, @NonNull View v, ContextMenu.ContextMenuInfo menuInfo) { super.onCreateContextMenu(menu, v, menuInfo); + if (selectOnly) return; + requireActivity().getMenuInflater().inflate(R.menu.account_item_context, menu); AccountSelectionListItem listItem = (AccountSelectionListItem) v; @@ -188,9 +194,9 @@ public class AccountSelectionListFragment extends DialogFragment implements DcEv .setPositiveButton(android.R.string.ok, (d, b) -> { String newTag = inputField.getText().toString().trim(); dcContext.setConfig(CONFIG_PRIVATE_TAG, newTag); - AccountManager.getInstance().showSwitchAccountMenu(activity); + AccountManager.getInstance().showSwitchAccountMenu(activity, selectOnly); }) - .setNegativeButton(R.string.cancel, (d, b) -> AccountManager.getInstance().showSwitchAccountMenu(activity)) + .setNegativeButton(R.string.cancel, (d, b) -> AccountManager.getInstance().showSwitchAccountMenu(activity, selectOnly)) .show(); } @@ -230,7 +236,7 @@ public class AccountSelectionListFragment extends DialogFragment implements DcEv AlertDialog dialog = new AlertDialog.Builder(activity) .setTitle(R.string.delete_account) .setView(dialogView) - .setNegativeButton(R.string.cancel, (d, which) -> AccountManager.getInstance().showSwitchAccountMenu(activity)) + .setNegativeButton(R.string.cancel, (d, which) -> AccountManager.getInstance().showSwitchAccountMenu(activity, selectOnly)) .setPositiveButton(R.string.delete, (d2, w2) -> activity.onDeleteProfile(accountId)) .show(); Util.redPositiveButton(dialog); @@ -247,13 +253,13 @@ public class AccountSelectionListFragment extends DialogFragment implements DcEv @Override public void onItemClick(AccountSelectionListItem contact) { - Activity activity = requireActivity(); AccountSelectionListFragment.this.dismiss(); int accountId = contact.getAccountId(); if (accountId == DC_CONTACT_ID_ADD_ACCOUNT) { AccountManager.getInstance().switchAccountAndStartActivity(activity, 0); } else if (accountId != DcHelper.getAccounts(activity).getSelectedAccount().getAccountId()) { - AccountManager.getInstance().switchAccountAndStartActivity(activity, accountId); + AccountManager.getInstance().switchAccount(activity, accountId); + activity.onProfileSwitched(accountId); } } } diff --git a/src/main/java/org/thoughtcrime/securesms/components/ConversationItemFooter.java b/src/main/java/org/thoughtcrime/securesms/components/ConversationItemFooter.java index ffc6906ac..e8ace5872 100644 --- a/src/main/java/org/thoughtcrime/securesms/components/ConversationItemFooter.java +++ b/src/main/java/org/thoughtcrime/securesms/components/ConversationItemFooter.java @@ -15,39 +15,51 @@ import androidx.annotation.Nullable; import com.b44t.messenger.DcMsg; import org.thoughtcrime.securesms.R; +import org.thoughtcrime.securesms.connect.DcHelper; import org.thoughtcrime.securesms.util.DateUtils; +import chat.delta.rpc.Rpc; +import chat.delta.rpc.RpcException; + public class ConversationItemFooter extends LinearLayout { private TextView dateView; private TextView editedView; + private TextView viewsLabel; + private ImageView viewsIcon; private ImageView bookmarkIndicatorView; private ImageView emailIndicatorView; private ImageView locationIndicatorView; private DeliveryStatusView deliveryStatusView; private Integer textColor = null; private int callDuration = 0; + private Context context; + private Rpc rpc; public ConversationItemFooter(Context context) { super(context); - init(null); + init(context, null); } public ConversationItemFooter(Context context, @Nullable AttributeSet attrs) { super(context, attrs); - init(attrs); + init(context, attrs); } public ConversationItemFooter(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); - init(attrs); + init(context, attrs); } - private void init(@Nullable AttributeSet attrs) { + private void init(Context context, @Nullable AttributeSet attrs) { + this.context = context; + this.rpc = DcHelper.getRpc(context); inflate(getContext(), R.layout.conversation_item_footer, this); dateView = findViewById(R.id.footer_date); editedView = findViewById(R.id.footer_edited); + viewsLabel = findViewById(R.id.footer_views); + viewsIcon = findViewById(R.id.footer_views_icon); bookmarkIndicatorView = findViewById(R.id.footer_bookmark_indicator); emailIndicatorView = findViewById(R.id.footer_email_indicator); locationIndicatorView = findViewById(R.id.footer_location_indicator); @@ -79,13 +91,33 @@ public class ConversationItemFooter extends LinearLayout { } locationIndicatorView.setVisibility(messageRecord.hasLocation() ? View.VISIBLE : View.GONE); - presentDeliveryStatus(messageRecord); + + boolean isOutChannel = DcHelper.getContext(context).getChat(messageRecord.getChatId()).isOutBroadcast(); + + if (isOutChannel && messageRecord.isOutgoing()) { + try { + int accId = rpc.getSelectedAccountId(); + int count = rpc.getMessageReadReceiptCount(accId, messageRecord.getId()); + viewsLabel.setText(String.format("%d", count)); + viewsLabel.setVisibility(View.VISIBLE); + viewsIcon.setVisibility(View.VISIBLE); + } catch (RpcException e) { + e.printStackTrace(); + } + } else { + viewsLabel.setVisibility(View.GONE); + viewsIcon.setVisibility(View.GONE); + } + + presentDeliveryStatus(messageRecord, isOutChannel); } public void setTextColor(int color) { textColor = color; dateView.setTextColor(color); editedView.setTextColor(color); + viewsLabel.setTextColor(color); + viewsIcon.setColorFilter(color); bookmarkIndicatorView.setColorFilter(color); emailIndicatorView.setColorFilter(color); locationIndicatorView.setColorFilter(color); @@ -106,7 +138,7 @@ public class ConversationItemFooter extends LinearLayout { } } - private void presentDeliveryStatus(@NonNull DcMsg messageRecord) { + private void presentDeliveryStatus(@NonNull DcMsg messageRecord, boolean isOutChannel) { // isDownloading is temporary and should be checked first. boolean isDownloading = messageRecord.getDownloadState() == DcMsg.DC_DOWNLOAD_IN_PROGRESS; boolean isCall = messageRecord.getType() == DcMsg.DC_MSG_CALL; @@ -114,7 +146,7 @@ public class ConversationItemFooter extends LinearLayout { if (isDownloading) deliveryStatusView.setDownloading(); else if (messageRecord.isPending()) deliveryStatusView.setPending(); else if (messageRecord.isFailed()) deliveryStatusView.setFailed(); - else if (!messageRecord.isOutgoing() || isCall) deliveryStatusView.setNone(); + else if (!messageRecord.isOutgoing() || isCall || isOutChannel) deliveryStatusView.setNone(); else if (messageRecord.isRemoteRead()) deliveryStatusView.setRead(); else if (messageRecord.isDelivered()) deliveryStatusView.setSent(); else if (messageRecord.isPreparing()) deliveryStatusView.setPreparing(); diff --git a/src/main/java/org/thoughtcrime/securesms/connect/AccountManager.java b/src/main/java/org/thoughtcrime/securesms/connect/AccountManager.java index d005c18dc..2bdebe7fe 100644 --- a/src/main/java/org/thoughtcrime/securesms/connect/AccountManager.java +++ b/src/main/java/org/thoughtcrime/securesms/connect/AccountManager.java @@ -137,8 +137,8 @@ public class AccountManager { // ui - public void showSwitchAccountMenu(ConversationListActivity activity) { - AccountSelectionListFragment dialog = new AccountSelectionListFragment(activity); + public void showSwitchAccountMenu(ConversationListActivity activity, boolean selectOnly) { + AccountSelectionListFragment dialog = new AccountSelectionListFragment(activity, selectOnly); dialog.show(((FragmentActivity) activity).getSupportFragmentManager(), null); } diff --git a/src/main/java/org/thoughtcrime/securesms/database/model/ThreadRecord.java b/src/main/java/org/thoughtcrime/securesms/database/model/ThreadRecord.java index c41e31d76..428da4776 100644 --- a/src/main/java/org/thoughtcrime/securesms/database/model/ThreadRecord.java +++ b/src/main/java/org/thoughtcrime/securesms/database/model/ThreadRecord.java @@ -101,6 +101,10 @@ public class ThreadRecord { return spannable; } + public boolean isOutChannel() { + return recipient.getChat() != null && recipient.getChat().isOutBroadcast(); + } + public int getUnreadCount() { return unreadCount; } diff --git a/src/main/java/org/thoughtcrime/securesms/profiles/AvatarHelper.java b/src/main/java/org/thoughtcrime/securesms/profiles/AvatarHelper.java index bf98af3d4..e56c5f0c4 100644 --- a/src/main/java/org/thoughtcrime/securesms/profiles/AvatarHelper.java +++ b/src/main/java/org/thoughtcrime/securesms/profiles/AvatarHelper.java @@ -21,7 +21,7 @@ import java.io.IOException; public class AvatarHelper { /* the maximum width/height an avatar should have */ - public static final int AVATAR_SIZE = 640; + public static final int AVATAR_SIZE = 512; public static void setGroupAvatar(Context context, int chatId, Bitmap bitmap) { DcContext dcContext = DcHelper.getContext(context); diff --git a/src/main/java/org/thoughtcrime/securesms/util/SendRelayedMessageUtil.java b/src/main/java/org/thoughtcrime/securesms/util/SendRelayedMessageUtil.java index 26f964ee3..999d4cff4 100644 --- a/src/main/java/org/thoughtcrime/securesms/util/SendRelayedMessageUtil.java +++ b/src/main/java/org/thoughtcrime/securesms/util/SendRelayedMessageUtil.java @@ -1,5 +1,6 @@ package org.thoughtcrime.securesms.util; +import static org.thoughtcrime.securesms.util.ShareUtil.getForwardedMessageAccountId; import static org.thoughtcrime.securesms.util.ShareUtil.getForwardedMessageIDs; import static org.thoughtcrime.securesms.util.ShareUtil.getSharedText; import static org.thoughtcrime.securesms.util.ShareUtil.getSharedUris; @@ -29,6 +30,11 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import chat.delta.rpc.Rpc; +import chat.delta.rpc.RpcException; public class SendRelayedMessageUtil { private static final String TAG = SendRelayedMessageUtil.class.getSimpleName(); @@ -40,12 +46,27 @@ public class SendRelayedMessageUtil { public static void immediatelyRelay(Activity activity, final Long[] chatIds) { ConversationListRelayingActivity.finishActivity(); if (isForwarding(activity)) { + int forwardedMsgAccId = getForwardedMessageAccountId(activity); int[] forwardedMessageIDs = getForwardedMessageIDs(activity); resetRelayingMessageContent(activity); - if (forwardedMessageIDs == null) return; + if (forwardedMessageIDs == null || forwardedMsgAccId <= 0) return; Util.runOnAnyBackgroundThread(() -> { DcContext dcContext = DcHelper.getContext(activity); + int accId = dcContext.getAccountId(); + if (forwardedMsgAccId != accId) { + Rpc rpc = DcHelper.getRpc(activity); + List list = Util.toList(forwardedMessageIDs); + for (long longChatId : chatIds) { + try { + rpc.forwardMessagesToAccount(forwardedMsgAccId, list, accId, (int)longChatId); + } catch (RpcException e) { + e.printStackTrace(); + } + } + return; + } + for (long longChatId : chatIds) { int chatId = (int) longChatId; if (dcContext.getChat(chatId).isSelfTalk()) { diff --git a/src/main/java/org/thoughtcrime/securesms/util/ShareUtil.java b/src/main/java/org/thoughtcrime/securesms/util/ShareUtil.java index 195793e6c..68737b27e 100644 --- a/src/main/java/org/thoughtcrime/securesms/util/ShareUtil.java +++ b/src/main/java/org/thoughtcrime/securesms/util/ShareUtil.java @@ -14,6 +14,7 @@ import androidx.annotation.NonNull; import java.util.ArrayList; public class ShareUtil { + private static final String FORWARDED_MESSAGE_ACCID = "forwarded_message_accid"; private static final String FORWARDED_MESSAGE_IDS = "forwarded_message_ids"; private static final String SHARED_URIS = "shared_uris"; private static final String SHARED_CONTACT_ID = "shared_contact_id"; @@ -27,11 +28,7 @@ public class ShareUtil { } public static boolean isForwarding(Activity activity) { - try { - return activity.getIntent().getIntArrayExtra(FORWARDED_MESSAGE_IDS) != null; - } catch (NullPointerException npe) { - return false; - } + return getForwardedMessageAccountId(activity) > 0; } public static boolean isSharing(Activity activity) { @@ -66,6 +63,14 @@ public class ShareUtil { } } + public static int getForwardedMessageAccountId(Activity activity) { + try { + return activity.getIntent().getIntExtra(FORWARDED_MESSAGE_ACCID, -1); + } catch (NullPointerException npe) { + return -1; + } + } + static int[] getForwardedMessageIDs(Activity activity) { try { return activity.getIntent().getIntArrayExtra(FORWARDED_MESSAGE_IDS); @@ -137,6 +142,7 @@ public class ShareUtil { public static void resetRelayingMessageContent(Activity activity) { try { + activity.getIntent().removeExtra(FORWARDED_MESSAGE_ACCID); activity.getIntent().removeExtra(FORWARDED_MESSAGE_IDS); activity.getIntent().removeExtra(SHARED_URIS); activity.getIntent().removeExtra(SHARED_CONTACT_ID); @@ -153,7 +159,8 @@ public class ShareUtil { public static void acquireRelayMessageContent(Activity currentActivity, @NonNull Intent newActivityIntent) { if (isForwarding(currentActivity)) { - newActivityIntent.putExtra(FORWARDED_MESSAGE_IDS, getForwardedMessageIDs(currentActivity)); + int accId = getForwardedMessageAccountId(currentActivity); + setForwardingMessageIds(newActivityIntent, getForwardedMessageIDs(currentActivity), accId); } else if (isSharing(currentActivity)) { newActivityIntent.putExtra(IS_SHARING, true); if (isDirectSharing(currentActivity)) { @@ -180,7 +187,8 @@ public class ShareUtil { } } - public static void setForwardingMessageIds(Intent composeIntent, int[] messageIds) { + public static void setForwardingMessageIds(Intent composeIntent, int[] messageIds, int accId) { + composeIntent.putExtra(FORWARDED_MESSAGE_ACCID, accId); composeIntent.putExtra(FORWARDED_MESSAGE_IDS, messageIds); } diff --git a/src/main/java/org/thoughtcrime/securesms/util/Util.java b/src/main/java/org/thoughtcrime/securesms/util/Util.java index 144dac195..825420da7 100644 --- a/src/main/java/org/thoughtcrime/securesms/util/Util.java +++ b/src/main/java/org/thoughtcrime/securesms/util/Util.java @@ -53,7 +53,9 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.text.DecimalFormat; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import java.util.Locale; import java.util.Objects; import java.util.concurrent.CountDownLatch; @@ -115,6 +117,14 @@ public class Util { } } + public static List toList(int[] array) { + ArrayList list = new ArrayList<>(array.length); + for (int i : array) { + list.add(i); + } + return list; + } + public static @NonNull int[] appendInt(@Nullable int[] cur, int val) { if (cur == null) { return new int[] { val }; diff --git a/src/main/res/drawable/ic_baseline_eye.xml b/src/main/res/drawable/ic_baseline_eye.xml new file mode 100644 index 000000000..c74c5a758 --- /dev/null +++ b/src/main/res/drawable/ic_baseline_eye.xml @@ -0,0 +1,9 @@ + + + diff --git a/src/main/res/layout/conversation_item_footer.xml b/src/main/res/layout/conversation_item_footer.xml index 4016775f0..4a4f2b990 100644 --- a/src/main/res/layout/conversation_item_footer.xml +++ b/src/main/res/layout/conversation_item_footer.xml @@ -62,6 +62,29 @@ android:contentDescription="@string/location" tools:visibility="gone" /> + + + + + +