mirror of
https://github.com/ArcaneChat/android.git
synced 2026-07-03 14:05:24 +02:00
Merge branch 'main' into adb/allow-to-edit-and-delete-for-all
This commit is contained in:
@@ -17,6 +17,7 @@ public class DcContext {
|
||||
public final static int DC_EVENT_MSG_READ = 2015;
|
||||
public final static int DC_EVENT_CHAT_MODIFIED = 2020;
|
||||
public final static int DC_EVENT_CHAT_EPHEMERAL_TIMER_MODIFIED = 2021;
|
||||
public final static int DC_EVENT_CHAT_DELETED = 2023;
|
||||
public final static int DC_EVENT_CONTACTS_CHANGED = 2030;
|
||||
public final static int DC_EVENT_LOCATION_CHANGED = 2035;
|
||||
public final static int DC_EVENT_CONFIGURE_PROGRESS = 2041;
|
||||
|
||||
@@ -17,6 +17,7 @@ public class Rpc {
|
||||
private final Map<Integer, SettableFuture<JsonElement>> requestFutures = new ConcurrentHashMap<>();
|
||||
private final DcJsonrpcInstance dcJsonrpcInstance;
|
||||
private int requestId = 0;
|
||||
private boolean started = false;
|
||||
private final Gson gson = new GsonBuilder().serializeNulls().create();
|
||||
|
||||
public Rpc(DcJsonrpcInstance dcJsonrpcInstance) {
|
||||
@@ -46,6 +47,7 @@ public class Rpc {
|
||||
}
|
||||
|
||||
public void start() {
|
||||
started = true;
|
||||
new Thread(() -> {
|
||||
while (true) {
|
||||
try {
|
||||
@@ -57,7 +59,9 @@ public class Rpc {
|
||||
}, "jsonrpcThread").start();
|
||||
}
|
||||
|
||||
public SettableFuture<JsonElement> call(String method, Object... params) {
|
||||
public SettableFuture<JsonElement> call(String method, Object... params) throws RpcException {
|
||||
if (!started) throw new RpcException("RPC not started yet.");
|
||||
|
||||
int id;
|
||||
synchronized (this) {
|
||||
id = ++requestId;
|
||||
|
||||
@@ -88,6 +88,7 @@ public class ApplicationContext extends MultiDexApplication {
|
||||
|
||||
dcAccounts = new DcAccounts(new File(getFilesDir(), "accounts").getAbsolutePath());
|
||||
rpc = new Rpc(dcAccounts.getJsonrpcInstance());
|
||||
rpc.start();
|
||||
AccountManager.getInstance().migrateToDcAccounts(this);
|
||||
int[] allAccounts = dcAccounts.getAll();
|
||||
for (int accountId : allAccounts) {
|
||||
@@ -126,8 +127,6 @@ public class ApplicationContext extends MultiDexApplication {
|
||||
Log.i("DeltaChat", "shutting down event handler");
|
||||
}, "eventThread").start();
|
||||
|
||||
rpc.start();
|
||||
|
||||
// migrating global notifications pref. to per-account config, added 10/July/24
|
||||
final String NOTIFICATION_PREF = "pref_key_enable_notifications";
|
||||
boolean isMuted = !Prefs.getBooleanPreference(this, NOTIFICATION_PREF, true);
|
||||
|
||||
@@ -333,18 +333,22 @@ public abstract class BaseConversationListFragment extends Fragment implements A
|
||||
intent.putExtra(ShareActivity.EXTRA_CHAT_ID, chat.getId());
|
||||
|
||||
Recipient recipient = new Recipient(activity, chat);
|
||||
Bitmap avatar = DirectShareUtil.getIconForShortcut(activity, recipient);
|
||||
ShortcutInfoCompat shortcutInfoCompat = new ShortcutInfoCompat.Builder(activity, "chat-" + dcContext.getAccountId() + "-" + chat.getId())
|
||||
Util.runOnAnyBackgroundThread(() -> {
|
||||
Bitmap avatar = DirectShareUtil.getIconForShortcut(activity, recipient);
|
||||
ShortcutInfoCompat shortcutInfoCompat = new ShortcutInfoCompat.Builder(activity, "chat-" + dcContext.getAccountId() + "-" + chat.getId())
|
||||
.setShortLabel(chat.getName())
|
||||
.setIcon(IconCompat.createWithAdaptiveBitmap(avatar))
|
||||
.setIntent(intent)
|
||||
.build();
|
||||
if (!ShortcutManagerCompat.requestPinShortcut(activity, shortcutInfoCompat, null)) {
|
||||
Toast.makeText(activity, "ErrAddToHomescreen: requestPinShortcut() failed", Toast.LENGTH_LONG).show();
|
||||
} else if (actionMode != null) {
|
||||
actionMode.finish();
|
||||
actionMode = null;
|
||||
}
|
||||
Util.runOnMain(() -> {
|
||||
if (!ShortcutManagerCompat.requestPinShortcut(activity, shortcutInfoCompat, null)) {
|
||||
Toast.makeText(activity, "ErrAddToHomescreen: requestPinShortcut() failed", Toast.LENGTH_LONG).show();
|
||||
} else if (actionMode != null) {
|
||||
actionMode.finish();
|
||||
actionMode = null;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private void updateActionModeItems(Menu menu) {
|
||||
|
||||
@@ -490,6 +490,13 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
Log.e(TAG, "cannot set up in-chat-search: ", e);
|
||||
}
|
||||
|
||||
if (!dcChat.canSend() || isEditing) {
|
||||
MenuItem attachItem = menu.findItem(R.id.menu_add_attachment);
|
||||
if (attachItem!=null) {
|
||||
attachItem.setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
super.onPrepareOptionsMenu(menu);
|
||||
return true;
|
||||
}
|
||||
@@ -498,7 +505,10 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
super.onOptionsItemSelected(item);
|
||||
int itemId = item.getItemId();
|
||||
if (itemId == R.id.menu_leave) {
|
||||
if (itemId == R.id.menu_add_attachment) {
|
||||
handleAddAttachment();
|
||||
return true;
|
||||
} else if (itemId == R.id.menu_leave) {
|
||||
handleLeaveGroup();
|
||||
return true;
|
||||
} else if (itemId == R.id.menu_archive_chat) {
|
||||
@@ -1005,9 +1015,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
|
||||
final SettableFuture<Integer> future = new SettableFuture<>();
|
||||
|
||||
DcMsg msg = null;
|
||||
Optional<QuoteModel> quote = inputPanel.getQuote();
|
||||
Integer recompress = 0;
|
||||
boolean editing = isEditing;
|
||||
|
||||
// for a quick ui feedback, we clear the related controls immediately on sending messages.
|
||||
@@ -1017,122 +1025,114 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
inputPanel.clearQuote();
|
||||
}
|
||||
|
||||
if (editing) {
|
||||
int msgId = quote.get().getQuotedMsg().getId();
|
||||
Util.runOnAnyBackgroundThread(() -> {
|
||||
Util.runOnAnyBackgroundThread(() -> {
|
||||
DcMsg msg = null;
|
||||
int recompress = 0;
|
||||
|
||||
if (editing) {
|
||||
int msgId = quote.get().getQuotedMsg().getId();
|
||||
if (action == ACTION_SEND_OUT) {
|
||||
dcContext.sendEditRequest(msgId, body);
|
||||
} else {
|
||||
dcContext.setDraft(chatId, null);
|
||||
}
|
||||
});
|
||||
return future;
|
||||
}
|
||||
|
||||
if(slideDeck!=null) {
|
||||
|
||||
if (action==ACTION_SEND_OUT) {
|
||||
attachmentManager.clear(glideRequests, false);
|
||||
future.set(chatId);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (slideDeck.getWebxdctDraftId() != 0) {
|
||||
msg = dcContext.getDraft(chatId);
|
||||
} else {
|
||||
List<Attachment> attachments = slideDeck.asAttachments();
|
||||
for (Attachment attachment : attachments) {
|
||||
String contentType = attachment.getContentType();
|
||||
if (MediaUtil.isImageType(contentType) && slideDeck.getDocumentSlide() == null) {
|
||||
msg = new DcMsg(dcContext,
|
||||
MediaUtil.isGif(contentType) ? DcMsg.DC_MSG_GIF : DcMsg.DC_MSG_IMAGE);
|
||||
msg.setDimension(attachment.getWidth(), attachment.getHeight());
|
||||
} else if (MediaUtil.isAudioType(contentType)) {
|
||||
msg = new DcMsg(dcContext,
|
||||
attachment.isVoiceNote() ? DcMsg.DC_MSG_VOICE : DcMsg.DC_MSG_AUDIO);
|
||||
} else if (MediaUtil.isVideoType(contentType) && slideDeck.getDocumentSlide() == null) {
|
||||
msg = new DcMsg(dcContext, DcMsg.DC_MSG_VIDEO);
|
||||
recompress = DcMsg.DC_MSG_VIDEO;
|
||||
} else {
|
||||
msg = new DcMsg(dcContext, DcMsg.DC_MSG_FILE);
|
||||
if(slideDeck!=null) {
|
||||
if (action==ACTION_SEND_OUT) {
|
||||
Util.runOnMain(() -> attachmentManager.clear(glideRequests, false));
|
||||
}
|
||||
|
||||
try {
|
||||
if (slideDeck.getWebxdctDraftId() != 0) {
|
||||
msg = dcContext.getDraft(chatId);
|
||||
} else {
|
||||
List<Attachment> attachments = slideDeck.asAttachments();
|
||||
for (Attachment attachment : attachments) {
|
||||
String contentType = attachment.getContentType();
|
||||
if (MediaUtil.isImageType(contentType) && slideDeck.getDocumentSlide() == null) {
|
||||
msg = new DcMsg(dcContext,
|
||||
MediaUtil.isGif(contentType) ? DcMsg.DC_MSG_GIF : DcMsg.DC_MSG_IMAGE);
|
||||
msg.setDimension(attachment.getWidth(), attachment.getHeight());
|
||||
} else if (MediaUtil.isAudioType(contentType)) {
|
||||
msg = new DcMsg(dcContext,
|
||||
attachment.isVoiceNote() ? DcMsg.DC_MSG_VOICE : DcMsg.DC_MSG_AUDIO);
|
||||
} else if (MediaUtil.isVideoType(contentType) && slideDeck.getDocumentSlide() == null) {
|
||||
msg = new DcMsg(dcContext, DcMsg.DC_MSG_VIDEO);
|
||||
recompress = DcMsg.DC_MSG_VIDEO;
|
||||
} else {
|
||||
msg = new DcMsg(dcContext, DcMsg.DC_MSG_FILE);
|
||||
}
|
||||
String path = attachment.getRealPath(this);
|
||||
msg.setFileAndDeduplicate(path, attachment.getFileName(), null);
|
||||
}
|
||||
String path = attachment.getRealPath(this);
|
||||
msg.setFileAndDeduplicate(path, attachment.getFileName(), null);
|
||||
}
|
||||
if (msg != null) {
|
||||
msg.setText(body);
|
||||
}
|
||||
}
|
||||
catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
else if (!body.isEmpty()){
|
||||
msg = new DcMsg(dcContext, DcMsg.DC_MSG_TEXT);
|
||||
msg.setText(body);
|
||||
}
|
||||
catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
|
||||
if (quote.isPresent()) {
|
||||
if (msg == null) msg = new DcMsg(dcContext, DcMsg.DC_MSG_TEXT);
|
||||
msg.setQuote(quote.get().getQuotedMsg());
|
||||
}
|
||||
}
|
||||
else if (!body.isEmpty()){
|
||||
msg = new DcMsg(dcContext, DcMsg.DC_MSG_TEXT);
|
||||
msg.setText(body);
|
||||
}
|
||||
|
||||
if (quote.isPresent()) {
|
||||
if (msg == null) msg = new DcMsg(dcContext, DcMsg.DC_MSG_TEXT);
|
||||
msg.setQuote(quote.get().getQuotedMsg());
|
||||
}
|
||||
if (action==ACTION_SEND_OUT) {
|
||||
|
||||
// msg may still be null to clear drafts
|
||||
new AsyncTask<Object, Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Object... param) {
|
||||
DcMsg msg = (DcMsg)param[0];
|
||||
Integer recompress = (Integer)param[1];
|
||||
if (action==ACTION_SEND_OUT) {
|
||||
// for WEBXDC, drafts are just sent out as is.
|
||||
// for preparations and other cases, cleanup draft soon.
|
||||
if (msg == null || msg.getType() != DcMsg.DC_MSG_WEBXDC) {
|
||||
dcContext.setDraft(dcChat.getId(), null);
|
||||
}
|
||||
|
||||
// for WEBXDC, drafts are just sent out as is.
|
||||
// for preparations and other cases, cleanup draft soon.
|
||||
if (msg == null || msg.getType() != DcMsg.DC_MSG_WEBXDC) {
|
||||
dcContext.setDraft(dcChat.getId(), null);
|
||||
}
|
||||
|
||||
if(msg!=null)
|
||||
{
|
||||
boolean doSend = true;
|
||||
if (recompress==DcMsg.DC_MSG_VIDEO) {
|
||||
Util.runOnMain(() -> {
|
||||
progressDialog = ProgressDialog.show(
|
||||
ConversationActivity.this,
|
||||
"",
|
||||
getString(R.string.one_moment),
|
||||
true,
|
||||
false
|
||||
);
|
||||
});
|
||||
doSend = VideoRecoder.prepareVideo(ConversationActivity.this, dcChat.getId(), msg);
|
||||
Util.runOnMain(() -> {
|
||||
try {
|
||||
progressDialog.dismiss();
|
||||
} catch (final IllegalArgumentException e) {
|
||||
// The activity is finishing/destroyed, do nothing.
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (doSend) {
|
||||
if (dcContext.sendMsg(dcChat.getId(), msg) == 0) {
|
||||
Util.runOnMain(()-> Toast.makeText(ConversationActivity.this, dcContext.getLastError(), Toast.LENGTH_LONG).show());
|
||||
return null;
|
||||
if(msg!=null) {
|
||||
boolean doSend = true;
|
||||
if (recompress==DcMsg.DC_MSG_VIDEO) {
|
||||
Util.runOnMain(() -> {
|
||||
if (isFinishing()) return;
|
||||
progressDialog = ProgressDialog.show(
|
||||
ConversationActivity.this,
|
||||
"",
|
||||
getString(R.string.one_moment),
|
||||
true,
|
||||
false
|
||||
);
|
||||
});
|
||||
doSend = VideoRecoder.prepareVideo(ConversationActivity.this, dcChat.getId(), msg);
|
||||
Util.runOnMain(() -> {
|
||||
try {
|
||||
if (progressDialog != null) progressDialog.dismiss();
|
||||
} catch (final IllegalArgumentException e) {
|
||||
// The activity is finishing/destroyed, do nothing.
|
||||
}
|
||||
}
|
||||
|
||||
Util.runOnMain(()-> sendComplete(dcChat.getId()));
|
||||
});
|
||||
}
|
||||
}
|
||||
else {
|
||||
dcContext.setDraft(dcChat.getId(), msg);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void result) {
|
||||
future.set(chatId);
|
||||
if (doSend) {
|
||||
if (dcContext.sendMsg(dcChat.getId(), msg) == 0) {
|
||||
Util.runOnMain(()-> Toast.makeText(ConversationActivity.this, dcContext.getLastError(), Toast.LENGTH_LONG).show());
|
||||
future.set(chatId);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Util.runOnMain(() -> sendComplete(dcChat.getId()));
|
||||
}
|
||||
} else {
|
||||
dcContext.setDraft(dcChat.getId(), msg);
|
||||
}
|
||||
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, msg, recompress);
|
||||
future.set(chatId);
|
||||
});
|
||||
|
||||
return future;
|
||||
}
|
||||
@@ -1351,8 +1351,12 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
else {
|
||||
processComposeControls(ACTION_SEND_OUT);
|
||||
DcHelper.getNotificationCenter(ConversationActivity.this).maybePlaySendSound(dcChat);
|
||||
processComposeControls(ACTION_SEND_OUT).addListener(new AssertedSuccessListener<Integer>() {
|
||||
@Override
|
||||
public void onSuccess(Integer chatId) {
|
||||
DcHelper.getNotificationCenter(ConversationActivity.this).maybePlaySendSound(dcChat);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -327,6 +327,7 @@ public class ConversationFragment extends MessageSelectorFragment
|
||||
menu.findItem(R.id.menu_context_edit).setVisible(false);
|
||||
menu.findItem(R.id.menu_context_reply_privately).setVisible(false);
|
||||
menu.findItem(R.id.menu_add_to_home_screen).setVisible(false);
|
||||
menu.findItem(R.id.menu_toggle_save).setVisible(false);
|
||||
} else {
|
||||
DcMsg messageRecord = messageRecords.iterator().next();
|
||||
DcChat chat = getListAdapter().getChat();
|
||||
@@ -339,6 +340,12 @@ public class ConversationFragment extends MessageSelectorFragment
|
||||
boolean showReplyPrivately = chat.isMultiUser() && !messageRecord.isOutgoing() && canReply;
|
||||
menu.findItem(R.id.menu_context_reply_privately).setVisible(showReplyPrivately);
|
||||
menu.findItem(R.id.menu_add_to_home_screen).setVisible(messageRecord.getType() == DcMsg.DC_MSG_WEBXDC);
|
||||
|
||||
boolean saved = messageRecord.getSavedMsgId() != 0;
|
||||
MenuItem toggleSave = menu.findItem(R.id.menu_toggle_save);
|
||||
toggleSave.setVisible(messageRecord.canSave() && !chat.isSelfTalk());
|
||||
toggleSave.setIcon(saved? R.drawable.baseline_bookmark_remove_24 : R.drawable.baseline_bookmark_border_24);
|
||||
toggleSave.setTitle(saved? R.string.unsave : R.string.save);
|
||||
}
|
||||
|
||||
// if one of the selected items cannot be saved, disable saving.
|
||||
@@ -495,6 +502,15 @@ public class ConversationFragment extends MessageSelectorFragment
|
||||
}
|
||||
}
|
||||
|
||||
private void handleToggleSave(final Set<DcMsg> messageRecords) {
|
||||
DcMsg msg = getSelectedMessageRecord(messageRecords);
|
||||
if (msg.getSavedMsgId() != 0) {
|
||||
dcContext.deleteMsgs(new int[]{msg.getSavedMsgId()});
|
||||
} else {
|
||||
dcContext.saveMsgs(new int[]{msg.getId()});
|
||||
}
|
||||
}
|
||||
|
||||
private void reloadList() {
|
||||
reloadList(false);
|
||||
}
|
||||
@@ -987,9 +1003,12 @@ public class ConversationFragment extends MessageSelectorFragment
|
||||
} else if (itemId == R.id.menu_resend) {
|
||||
handleResendMessage(getListAdapter().getSelectedItems());
|
||||
return true;
|
||||
} else if (itemId == R.id.menu_toggle_save) {
|
||||
handleToggleSave(getListAdapter().getSelectedItems());
|
||||
actionMode.finish();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -80,6 +80,7 @@ public class ConversationListFragment extends BaseConversationListFragment
|
||||
DcEventCenter eventCenter = DcHelper.getEventCenter(requireActivity());
|
||||
eventCenter.addMultiAccountObserver(DcContext.DC_EVENT_INCOMING_MSG, this);
|
||||
eventCenter.addMultiAccountObserver(DcContext.DC_EVENT_MSGS_NOTICED, this);
|
||||
eventCenter.addMultiAccountObserver(DcContext.DC_EVENT_CHAT_DELETED, this);
|
||||
eventCenter.addObserver(DcContext.DC_EVENT_CHAT_MODIFIED, this);
|
||||
eventCenter.addObserver(DcContext.DC_EVENT_CONTACTS_CHANGED, this);
|
||||
eventCenter.addObserver(DcContext.DC_EVENT_MSGS_CHANGED, this);
|
||||
@@ -321,7 +322,10 @@ public class ConversationListFragment extends BaseConversationListFragment
|
||||
|
||||
@Override
|
||||
public void handleEvent(@NonNull DcEvent event) {
|
||||
if (event.getAccountId() != DcHelper.getContext(requireActivity()).getAccountId()) {
|
||||
final int accId = event.getAccountId();
|
||||
if (event.getId() == DcContext.DC_EVENT_CHAT_DELETED) {
|
||||
DcHelper.getNotificationCenter(requireActivity()).removeNotifications(accId, event.getData1Int());
|
||||
} else if (accId != DcHelper.getContext(requireActivity()).getAccountId()) {
|
||||
Activity activity = getActivity();
|
||||
if (activity instanceof ConversationListActivity) {
|
||||
((ConversationListActivity) activity).refreshUnreadIndicator();
|
||||
|
||||
@@ -319,12 +319,15 @@ public class ProfileSettingsFragment extends Fragment
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
if (requestCode==REQUEST_CODE_PICK_CONTACT && resultCode==Activity.RESULT_OK && data!=null) {
|
||||
List<String> selected = data.getStringArrayListExtra("contacts");
|
||||
for (String addr : selected) {
|
||||
if (addr!=null) {
|
||||
int toAddId = dcContext.createContact(null, addr);
|
||||
dcContext.addContactToChat(chatId, toAddId);
|
||||
if(selected == null) return;
|
||||
Util.runOnAnyBackgroundThread(() -> {
|
||||
for (String addr : selected) {
|
||||
if (addr!=null) {
|
||||
int toAddId = dcContext.createContact(null, addr);
|
||||
dcContext.addContactToChat(chatId, toAddId);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-1
@@ -27,6 +27,7 @@ public class ConversationAdaptiveActionsToolbar extends Toolbar {
|
||||
private static final int ID_ACTION_2 = R.id.menu_context_copy;
|
||||
private static final int ID_ACTION_3 = R.id.menu_context_share;
|
||||
private static final int ID_ACTION_4 = R.id.menu_context_forward;
|
||||
private static final int ID_ACTION_5 = R.id.menu_toggle_save;
|
||||
|
||||
private final int maxShown;
|
||||
|
||||
@@ -79,7 +80,8 @@ public class ConversationAdaptiveActionsToolbar extends Toolbar {
|
||||
boolean showAsAction = item.getItemId() == ID_ACTION_1
|
||||
|| item.getItemId() == ID_ACTION_2
|
||||
|| item.getItemId() == ID_ACTION_3
|
||||
|| item.getItemId() == ID_ACTION_4;
|
||||
|| item.getItemId() == ID_ACTION_4
|
||||
|| item.getItemId() == ID_ACTION_5;
|
||||
|
||||
if (showAsAction && item.isVisible() && nItemsToShow > 0) {
|
||||
item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
|
||||
|
||||
Reference in New Issue
Block a user