mirror of
https://github.com/ArcaneChat/android.git
synced 2026-07-03 14:05:24 +02:00
Merge remote-tracking branch 'upstream/main'
This commit is contained in:
@@ -1315,8 +1315,8 @@ public class Rpc {
|
||||
}
|
||||
|
||||
/** Starts an outgoing call. */
|
||||
public Integer placeOutgoingCall(Integer accountId, Integer chatId, String placeCallInfo) throws RpcException {
|
||||
return transport.callForResult(new TypeReference<Integer>(){}, "place_outgoing_call", mapper.valueToTree(accountId), mapper.valueToTree(chatId), mapper.valueToTree(placeCallInfo));
|
||||
public Integer placeOutgoingCall(Integer accountId, Integer chatId, String placeCallInfo, Boolean hasVideo) throws RpcException {
|
||||
return transport.callForResult(new TypeReference<Integer>(){}, "place_outgoing_call", mapper.valueToTree(accountId), mapper.valueToTree(chatId), mapper.valueToTree(placeCallInfo), mapper.valueToTree(hasVideo));
|
||||
}
|
||||
|
||||
/** Accepts an incoming call. */
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
package chat.delta.rpc.types;
|
||||
|
||||
public class CallInfo {
|
||||
/** True if SDP offer has a video. */
|
||||
/** True if the call is started as a video call. */
|
||||
public Boolean hasVideo;
|
||||
/**
|
||||
* SDP offer.
|
||||
|
||||
@@ -2,8 +2,8 @@ package com.b44t.messenger;
|
||||
|
||||
public class DcAccounts {
|
||||
|
||||
public DcAccounts(String dir) {
|
||||
accountsCPtr = createAccountsCPtr(dir);
|
||||
public DcAccounts(String dir, DcEventChannel channel) {
|
||||
accountsCPtr = createAccountsCPtr(dir, channel);
|
||||
if (accountsCPtr == 0) throw new RuntimeException("createAccountsCPtr() returned null pointer");
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ public class DcAccounts {
|
||||
|
||||
// working with raw c-data
|
||||
private long accountsCPtr; // CAVE: the name is referenced in the JNI
|
||||
private native long createAccountsCPtr (String dir);
|
||||
private native long createAccountsCPtr (String dir, DcEventChannel channel);
|
||||
private native void unrefAccountsCPtr ();
|
||||
private native long getEventEmitterCPtr ();
|
||||
private native long getJsonrpcInstanceCPtr ();
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.b44t.messenger;
|
||||
|
||||
public class DcEventChannel {
|
||||
|
||||
public DcEventChannel() {
|
||||
eventChannelCPtr = createEventChannelCPtr();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
super.finalize();
|
||||
if (eventChannelCPtr != 0) {
|
||||
unrefEventChannelCPtr();
|
||||
eventChannelCPtr = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public DcEventEmitter getEventEmitter() {
|
||||
return new DcEventEmitter(getEventEmitterCPtr());
|
||||
}
|
||||
|
||||
// working with raw c-data
|
||||
private long eventChannelCPtr; // CAVE: the name is referenced in the JNI
|
||||
private native long createEventChannelCPtr ();
|
||||
private native void unrefEventChannelCPtr ();
|
||||
private native long getEventEmitterCPtr ();
|
||||
}
|
||||
@@ -23,6 +23,7 @@ import androidx.work.WorkManager;
|
||||
import com.b44t.messenger.DcAccounts;
|
||||
import com.b44t.messenger.DcContext;
|
||||
import com.b44t.messenger.DcEvent;
|
||||
import com.b44t.messenger.DcEventChannel;
|
||||
import com.b44t.messenger.DcEventEmitter;
|
||||
import com.b44t.messenger.FFITransport;
|
||||
|
||||
@@ -196,7 +197,29 @@ public class ApplicationContext extends MultiDexApplication {
|
||||
Util.runOnBackground(() -> {
|
||||
synchronized (initLock) {
|
||||
try {
|
||||
dcAccounts = new DcAccounts(new File(getFilesDir(), "accounts").getAbsolutePath());
|
||||
DcEventChannel eventChannel = new DcEventChannel();
|
||||
DcEventEmitter emitter = eventChannel.getEventEmitter();
|
||||
eventCenter = new DcEventCenter(this);
|
||||
|
||||
new Thread(() -> {
|
||||
Log.i(TAG, "Starting event loop");
|
||||
while (true) {
|
||||
DcEvent event = emitter.getNextEvent();
|
||||
if (event == null) {
|
||||
break;
|
||||
}
|
||||
if (isInitialized) {
|
||||
eventCenter.handleEvent(event);
|
||||
} else {
|
||||
// not fully initialized, only handle logging events,
|
||||
// ex. account migrations during DcAccounts initialization
|
||||
eventCenter.handleLogging(event);
|
||||
}
|
||||
}
|
||||
Log.i("DeltaChat", "shutting down event handler");
|
||||
}, "eventThread").start();
|
||||
|
||||
dcAccounts = new DcAccounts(new File(getFilesDir(), "accounts").getAbsolutePath(), eventChannel);
|
||||
Log.i(TAG, "DcAccounts created");
|
||||
rpc = new Rpc(new FFITransport(dcAccounts.getJsonrpcInstance()));
|
||||
Log.i(TAG, "Rpc created");
|
||||
@@ -236,28 +259,12 @@ public class ApplicationContext extends MultiDexApplication {
|
||||
}
|
||||
dcContext = dcAccounts.getSelectedAccount();
|
||||
notificationCenter = new NotificationCenter(this);
|
||||
eventCenter = new DcEventCenter(this);
|
||||
dcLocationManager = new DcLocationManager(this, dcContext);
|
||||
|
||||
// Mark as initialized before starting threads that depend on it
|
||||
isInitialized = true;
|
||||
initLock.notifyAll();
|
||||
Log.i(TAG, "DcAccounts initialization complete");
|
||||
|
||||
new Thread(() -> {
|
||||
Log.i(TAG, "Starting event loop");
|
||||
DcEventEmitter emitter = dcAccounts.getEventEmitter();
|
||||
Log.i(TAG, "DcEventEmitter obtained");
|
||||
while (true) {
|
||||
DcEvent event = emitter.getNextEvent();
|
||||
if (event==null) {
|
||||
break;
|
||||
}
|
||||
eventCenter.handleEvent(event);
|
||||
}
|
||||
Log.i("DeltaChat", "shutting down event handler");
|
||||
}, "eventThread").start();
|
||||
|
||||
// set translations before starting I/O to avoid sending untranslated MDNs (issue #2288)
|
||||
DcHelper.setStockTranslations(this);
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ import java.util.List;
|
||||
public class ContactMultiSelectionActivity extends ContactSelectionActivity {
|
||||
|
||||
public static final String CONTACTS_EXTRA = "contacts_extra";
|
||||
public static final String DESELECTED_CONTACTS_EXTRA = "deselected_contacts_extra";
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle icicle, boolean ready) {
|
||||
@@ -71,7 +72,9 @@ public class ContactMultiSelectionActivity extends ContactSelectionActivity {
|
||||
private void saveSelection() {
|
||||
Intent resultIntent = getIntent();
|
||||
List<Integer> selectedContacts = contactsFragment.getSelectedContacts();
|
||||
List<Integer> deselectedContacts = contactsFragment.getDeselectedContacts();
|
||||
resultIntent.putIntegerArrayListExtra(CONTACTS_EXTRA, new ArrayList<>(selectedContacts));
|
||||
resultIntent.putIntegerArrayListExtra(DESELECTED_CONTACTS_EXTRA, new ArrayList<>(deselectedContacts));
|
||||
setResult(RESULT_OK, resultIntent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,6 +58,7 @@ import org.thoughtcrime.securesms.util.Util;
|
||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
@@ -83,6 +84,7 @@ public class ContactSelectionListFragment extends Fragment
|
||||
private DcContext dcContext;
|
||||
|
||||
private Set<Integer> selectedContacts;
|
||||
private Set<Integer> deselectedContacts;
|
||||
private OnContactSelectedListener onContactSelectedListener;
|
||||
private String cursorFilter;
|
||||
private RecyclerView recyclerView;
|
||||
@@ -127,6 +129,7 @@ public class ContactSelectionListFragment extends Fragment
|
||||
public boolean onCreateActionMode(ActionMode actionMode, Menu menu) {
|
||||
MenuInflater inflater = getActivity().getMenuInflater();
|
||||
inflater.inflate(R.menu.contact_list, menu);
|
||||
menu.findItem(R.id.menu_delete_selected).setVisible(!isMulti());
|
||||
updateActionModeState(actionMode);
|
||||
return true;
|
||||
}
|
||||
@@ -226,6 +229,15 @@ public class ContactSelectionListFragment extends Fragment
|
||||
return selected;
|
||||
}
|
||||
|
||||
public @NonNull List<Integer> getDeselectedContacts() {
|
||||
List<Integer> deselected = new LinkedList<>();
|
||||
if (deselectedContacts != null) {
|
||||
deselected.addAll(deselectedContacts);
|
||||
}
|
||||
|
||||
return deselected;
|
||||
}
|
||||
|
||||
private boolean isMulti() {
|
||||
return getActivity().getIntent().getBooleanExtra(MULTI_SELECT, false);
|
||||
}
|
||||
@@ -241,6 +253,7 @@ public class ContactSelectionListFragment extends Fragment
|
||||
isMulti(),
|
||||
true);
|
||||
selectedContacts = adapter.getSelectedContacts();
|
||||
deselectedContacts = new HashSet<>();
|
||||
ArrayList<Integer> preselectedContacts = getActivity().getIntent().getIntegerArrayListExtra(PRESELECTED_CONTACTS);
|
||||
if(preselectedContacts!=null) {
|
||||
selectedContacts.addAll(preselectedContacts);
|
||||
@@ -309,12 +322,14 @@ public class ContactSelectionListFragment extends Fragment
|
||||
}
|
||||
|
||||
selectedContacts.add(contactId);
|
||||
deselectedContacts.remove(contactId);
|
||||
contact.setChecked(true);
|
||||
if (onContactSelectedListener != null) {
|
||||
onContactSelectedListener.onContactSelected(contactId);
|
||||
}
|
||||
} else {
|
||||
selectedContacts.remove(contactId);
|
||||
deselectedContacts.add(contactId);
|
||||
contact.setChecked(false);
|
||||
if (onContactSelectedListener != null) {
|
||||
onContactSelectedListener.onContactDeselected(contactId);
|
||||
@@ -355,6 +370,7 @@ public class ContactSelectionListFragment extends Fragment
|
||||
int contactId = data.getIntExtra(NewContactActivity.CONTACT_ID_EXTRA, 0);
|
||||
if (contactId != 0) {
|
||||
selectedContacts.add(contactId);
|
||||
deselectedContacts.remove(contactId);
|
||||
}
|
||||
getLoaderManager().restartLoader(0, null, ContactSelectionListFragment.this);
|
||||
}
|
||||
|
||||
@@ -569,8 +569,11 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
} else if (itemId == R.id.menu_show_map) {
|
||||
WebxdcActivity.openMaps(this, chatId);
|
||||
return true;
|
||||
} else if (itemId == R.id.menu_start_call) {
|
||||
CallUtil.startCall(this, chatId);
|
||||
} else if (itemId == R.id.menu_start_audio_call) {
|
||||
CallUtil.startCall(this, chatId, false);
|
||||
return true;
|
||||
} else if (itemId == R.id.menu_start_video_call) {
|
||||
CallUtil.startCall(this, chatId, true);
|
||||
return true;
|
||||
} else if (itemId == R.id.menu_all_media) {
|
||||
handleAllMedia();
|
||||
|
||||
@@ -891,7 +891,7 @@ public class ConversationFragment extends MessageSelectorFragment
|
||||
|
||||
@Override
|
||||
public void onReactionClicked(DcMsg messageRecord) {
|
||||
ReactionsDetailsFragment dialog = new ReactionsDetailsFragment(messageRecord.getId());
|
||||
ReactionsDetailsFragment dialog = ReactionsDetailsFragment.newInstance(messageRecord.getId());
|
||||
dialog.show(getActivity().getSupportFragmentManager(), null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1000,9 +1000,9 @@ public class ConversationItem extends BaseConversationItem
|
||||
int chatId = messageRecord.getChatId();
|
||||
if (!messageRecord.isOutgoing() && callInfo.state instanceof CallState.Alerting) {
|
||||
int callId = messageRecord.getId();
|
||||
CallUtil.openCall(getContext(), accId, chatId, callId, callInfo.sdpOffer);
|
||||
CallUtil.openCall(getContext(), accId, chatId, callId, callInfo.sdpOffer, callInfo.hasVideo);
|
||||
} else {
|
||||
CallUtil.startCall(getContext(), accId, chatId);
|
||||
CallUtil.startCall(getContext(), accId, chatId, callInfo.hasVideo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,10 +22,7 @@ import static org.thoughtcrime.securesms.util.ShareUtil.acquireRelayMessageConte
|
||||
import static org.thoughtcrime.securesms.util.ShareUtil.isRelayingMessageContent;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
|
||||
@@ -37,7 +34,6 @@ import com.google.zxing.integration.android.IntentResult;
|
||||
import org.thoughtcrime.securesms.connect.DcHelper;
|
||||
import org.thoughtcrime.securesms.qr.QrActivity;
|
||||
import org.thoughtcrime.securesms.qr.QrCodeHandler;
|
||||
import org.thoughtcrime.securesms.util.MailtoUtil;
|
||||
|
||||
import chat.delta.rpc.types.SecurejoinSource;
|
||||
import chat.delta.rpc.types.SecurejoinUiPath;
|
||||
@@ -57,53 +53,6 @@ public class NewConversationActivity extends ContactSelectionActivity {
|
||||
super.onCreate(bundle, ready);
|
||||
assert getSupportActionBar() != null;
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
handleIntent();
|
||||
}
|
||||
|
||||
private void handleIntent() {
|
||||
Intent intent = getIntent();
|
||||
String action = intent.getAction();
|
||||
if(Intent.ACTION_VIEW.equals(action) || Intent.ACTION_SENDTO.equals(action)) {
|
||||
try {
|
||||
Uri uri = intent.getData();
|
||||
if(uri != null) {
|
||||
String scheme = uri.getScheme();
|
||||
if(MailtoUtil.isMailto(uri)) {
|
||||
String textToShare = MailtoUtil.getText(uri);
|
||||
String[] recipientsArray = MailtoUtil.getRecipients(uri);
|
||||
if (recipientsArray.length >= 1) {
|
||||
if (!textToShare.isEmpty()) {
|
||||
getIntent().putExtra(TEXT_EXTRA, textToShare);
|
||||
}
|
||||
final String addr = recipientsArray[0];
|
||||
final DcContext dcContext = DcHelper.getContext(this);
|
||||
int contactId = dcContext.lookupContactIdByAddr(addr);
|
||||
if (contactId == 0 && dcContext.mayBeValidAddr(addr)) {
|
||||
contactId = dcContext.createContact(null, recipientsArray[0]);
|
||||
}
|
||||
if (contactId == 0) {
|
||||
Toast.makeText(this, R.string.bad_email_address, Toast.LENGTH_LONG).show();
|
||||
} else {
|
||||
onContactSelected(contactId);
|
||||
}
|
||||
} else {
|
||||
Intent shareIntent = new Intent(this, ShareActivity.class);
|
||||
shareIntent.putExtra(Intent.EXTRA_TEXT, textToShare);
|
||||
startActivity(shareIntent);
|
||||
finish();
|
||||
}
|
||||
} else if(scheme != null && scheme.startsWith("http")) {
|
||||
Intent shareIntent = new Intent(this, ShareActivity.class);
|
||||
shareIntent.putExtra(Intent.EXTRA_TEXT, uri.toString());
|
||||
startActivity(shareIntent);
|
||||
finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(Exception e) {
|
||||
Log.e(TAG, "start activity from external 'mailto:' link failed", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -310,11 +310,27 @@ public class ProfileFragment extends Fragment
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
if (requestCode==REQUEST_CODE_PICK_CONTACT && resultCode==Activity.RESULT_OK && data!=null) {
|
||||
List<Integer> selected = data.getIntegerArrayListExtra(ContactMultiSelectionActivity.CONTACTS_EXTRA);
|
||||
if(selected == null) return;
|
||||
List<Integer> deselected = data.getIntegerArrayListExtra(ContactMultiSelectionActivity.DESELECTED_CONTACTS_EXTRA);
|
||||
Util.runOnAnyBackgroundThread(() -> {
|
||||
for (Integer contactId : selected) {
|
||||
if (contactId!=null) {
|
||||
dcContext.addContactToChat(chatId, contactId);
|
||||
if (deselected != null) {
|
||||
// Remove members that were deselected
|
||||
int[] members = dcContext.getChatContacts(chatId);
|
||||
for (int contactId : deselected) {
|
||||
for (int memberId : members) {
|
||||
if (memberId == contactId) {
|
||||
dcContext.removeContactFromChat(chatId, memberId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (selected != null) {
|
||||
// Add new members
|
||||
for (Integer contactId : selected) {
|
||||
if (contactId != null) {
|
||||
dcContext.addContactToChat(chatId, contactId);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -104,26 +104,25 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity implement
|
||||
resolvedExtras = new ArrayList<>();
|
||||
|
||||
List<Uri> streamExtras = new ArrayList<>();
|
||||
if (MailtoUtil.isMailto(getIntent().getData())) {
|
||||
String[] extraEmail = getIntent().getStringArrayExtra(Intent.EXTRA_EMAIL);
|
||||
if (extraEmail == null || extraEmail.length == 0) {
|
||||
getIntent().putExtra(Intent.EXTRA_EMAIL, MailtoUtil.getRecipients(getIntent().getData()));
|
||||
}
|
||||
String text = getIntent().getStringExtra(Intent.EXTRA_TEXT);
|
||||
if (text == null || text.isEmpty()) {
|
||||
getIntent().putExtra(Intent.EXTRA_TEXT, MailtoUtil.getText(getIntent().getData()));
|
||||
}
|
||||
}
|
||||
|
||||
if (Intent.ACTION_SEND.equals(getIntent().getAction()) &&
|
||||
getIntent().getParcelableExtra(Intent.EXTRA_STREAM) != null) {
|
||||
Uri uri = getIntent().getParcelableExtra(Intent.EXTRA_STREAM);
|
||||
streamExtras.add(uri);
|
||||
} else if (getIntent().getParcelableArrayListExtra(Intent.EXTRA_STREAM) != null) {
|
||||
streamExtras = getIntent().getParcelableArrayListExtra(Intent.EXTRA_STREAM);
|
||||
} else {
|
||||
Uri uri = getIntent().getData();
|
||||
if (MailtoUtil.isMailto(uri)) {
|
||||
String[] extraEmail = getIntent().getStringArrayExtra(Intent.EXTRA_EMAIL);
|
||||
if (extraEmail == null || extraEmail.length == 0) {
|
||||
getIntent().putExtra(Intent.EXTRA_EMAIL, MailtoUtil.getRecipients(uri));
|
||||
}
|
||||
String text = getIntent().getStringExtra(Intent.EXTRA_TEXT);
|
||||
if (text == null || text.isEmpty()) {
|
||||
getIntent().putExtra(Intent.EXTRA_TEXT, MailtoUtil.getText(uri));
|
||||
}
|
||||
} else if (uri != null) {
|
||||
streamExtras.add(uri);
|
||||
}
|
||||
} else if (uri != null) {
|
||||
streamExtras.add(uri);
|
||||
}
|
||||
|
||||
if (needsFilePermission(streamExtras)) {
|
||||
@@ -265,6 +264,7 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity implement
|
||||
}
|
||||
|
||||
chatId = dcContext.createChatByContactId(contactId);
|
||||
accId = dcContext.getAccountId();
|
||||
}
|
||||
Intent composeIntent;
|
||||
if (accId != -1 && chatId != -1) {
|
||||
|
||||
@@ -77,7 +77,7 @@ public class WebViewActivity extends PassphraseRequiredActionBarActivity
|
||||
findViewById(R.id.status_bar_background).setBackgroundResource(R.drawable.search_toolbar_shadow);
|
||||
} else {
|
||||
// add padding to avoid content hidden behind system bars
|
||||
ViewUtil.applyWindowInsets(findViewById(R.id.content_container));
|
||||
ViewUtil.applyWindowInsets(findViewById(R.id.content_container), true, true, true, true, true);
|
||||
}
|
||||
|
||||
webView.setWebViewClient(new WebViewClient() {
|
||||
|
||||
+13
-7
@@ -47,21 +47,24 @@ import chat.delta.rpc.RpcException;
|
||||
public class AccountSelectionListFragment extends DialogFragment implements DcEventCenter.DcEventDelegate
|
||||
{
|
||||
private static final String TAG = AccountSelectionListFragment.class.getSimpleName();
|
||||
private final ConversationListActivity activity;
|
||||
private static final String ARG_SELECT_ONLY = "select_only";
|
||||
private RecyclerView recyclerView;
|
||||
private AccountSelectionListAdapter adapter;
|
||||
private final boolean selectOnly;
|
||||
private boolean selectOnly;
|
||||
|
||||
public AccountSelectionListFragment(ConversationListActivity activity, boolean selectOnly) {
|
||||
super();
|
||||
this.activity = activity;
|
||||
this.selectOnly = selectOnly;
|
||||
public static AccountSelectionListFragment newInstance(boolean selectOnly) {
|
||||
AccountSelectionListFragment fragment = new AccountSelectionListFragment();
|
||||
Bundle args = new Bundle();
|
||||
args.putBoolean(ARG_SELECT_ONLY, selectOnly);
|
||||
fragment.setArguments(args);
|
||||
return fragment;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity())
|
||||
selectOnly = getArguments() != null && getArguments().getBoolean(ARG_SELECT_ONLY, false);
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(requireActivity())
|
||||
.setTitle(R.string.switch_account)
|
||||
.setNegativeButton(R.string.cancel, null);
|
||||
if (!selectOnly) {
|
||||
@@ -179,6 +182,7 @@ public class AccountSelectionListFragment extends DialogFragment implements DcEv
|
||||
}
|
||||
|
||||
private void onSetTag(int accountId) {
|
||||
ConversationListActivity activity = (ConversationListActivity)requireActivity();
|
||||
AccountSelectionListFragment.this.dismiss();
|
||||
|
||||
DcContext dcContext = DcHelper.getAccounts(activity).getAccount(accountId);
|
||||
@@ -202,6 +206,7 @@ public class AccountSelectionListFragment extends DialogFragment implements DcEv
|
||||
|
||||
private void onDeleteProfile(int accountId) {
|
||||
AccountSelectionListFragment.this.dismiss();
|
||||
ConversationListActivity activity = (ConversationListActivity)requireActivity();
|
||||
DcAccounts accounts = DcHelper.getAccounts(activity);
|
||||
Rpc rpc = DcHelper.getRpc(activity);
|
||||
|
||||
@@ -254,6 +259,7 @@ public class AccountSelectionListFragment extends DialogFragment implements DcEv
|
||||
@Override
|
||||
public void onItemClick(AccountSelectionListItem contact) {
|
||||
AccountSelectionListFragment.this.dismiss();
|
||||
ConversationListActivity activity = (ConversationListActivity)requireActivity();
|
||||
int accountId = contact.getAccountId();
|
||||
if (accountId == DC_CONTACT_ID_ADD_ACCOUNT) {
|
||||
AccountManager.getInstance().switchAccountAndStartActivity(activity, 0);
|
||||
|
||||
@@ -43,12 +43,14 @@ public class CallActivity extends WebViewActivity implements DcEventCenter.DcEve
|
||||
public static final String EXTRA_CHAT_ID = "chat_id";
|
||||
public static final String EXTRA_CALL_ID = "call_id";
|
||||
public static final String EXTRA_HASH = "hash";
|
||||
public static final String EXTRA_HAS_VIDEO = "has_video";
|
||||
|
||||
private DcContext dcContext;
|
||||
private Rpc rpc;
|
||||
private int accId;
|
||||
private int chatId;
|
||||
private int callId;
|
||||
private boolean hasVideo;
|
||||
private boolean ended = false;
|
||||
|
||||
@SuppressLint("SetJavaScriptEnabled")
|
||||
@@ -58,7 +60,9 @@ public class CallActivity extends WebViewActivity implements DcEventCenter.DcEve
|
||||
|
||||
Bundle bundle = getIntent().getExtras();
|
||||
assert bundle != null;
|
||||
hasVideo = bundle.getBoolean(EXTRA_HAS_VIDEO, true);
|
||||
String hash = bundle.getString(EXTRA_HASH, "");
|
||||
String query = hasVideo? "" : "?noOutgoingVideoInitially";
|
||||
accId = bundle.getInt(EXTRA_ACCOUNT_ID, -1);
|
||||
chatId = bundle.getInt(EXTRA_CHAT_ID, 0);
|
||||
callId = bundle.getInt(EXTRA_CALL_ID, 0);
|
||||
@@ -94,7 +98,7 @@ public class CallActivity extends WebViewActivity implements DcEventCenter.DcEve
|
||||
.withPermanentDenialDialog(getString(R.string.perm_explain_access_to_camera_denied))
|
||||
.onAllGranted(() -> {
|
||||
String url = "file:///android_asset/calls/index.html";
|
||||
webView.loadUrl(url + hash);
|
||||
webView.loadUrl(url + query + hash);
|
||||
}).onAnyDenied(this::finish)
|
||||
.execute();
|
||||
}
|
||||
@@ -168,7 +172,7 @@ public class CallActivity extends WebViewActivity implements DcEventCenter.DcEve
|
||||
@JavascriptInterface
|
||||
public void startCall(String payload) {
|
||||
try {
|
||||
callId = rpc.placeOutgoingCall(accId, chatId, payload);
|
||||
callId = rpc.placeOutgoingCall(accId, chatId, payload, hasVideo);
|
||||
} catch (RpcException e) {
|
||||
Log.e(TAG, "Error", e);
|
||||
}
|
||||
|
||||
@@ -18,28 +18,29 @@ import java.nio.charset.StandardCharsets;
|
||||
public class CallUtil {
|
||||
private static final String TAG = CallUtil.class.getSimpleName();
|
||||
|
||||
public static void startCall(Activity activity, int chatId) {
|
||||
public static void startCall(Activity activity, int chatId, boolean hasVideo) {
|
||||
Permissions.with(activity)
|
||||
.request(Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO)
|
||||
.ifNecessary()
|
||||
.withPermanentDenialDialog(activity.getString(R.string.perm_explain_access_to_camera_denied))
|
||||
.onAllGranted(() -> {
|
||||
int accId = DcHelper.getContext(activity).getAccountId();
|
||||
startCall(activity, accId, chatId);
|
||||
startCall(activity, accId, chatId, hasVideo);
|
||||
})
|
||||
.execute();
|
||||
}
|
||||
|
||||
public static void startCall(Context context, int accId, int chatId) {
|
||||
public static void startCall(Context context, int accId, int chatId, boolean hasVideo) {
|
||||
Intent intent = new Intent(context, CallActivity.class);
|
||||
intent.setAction(Intent.ACTION_VIEW);
|
||||
intent.putExtra(CallActivity.EXTRA_ACCOUNT_ID, accId);
|
||||
intent.putExtra(CallActivity.EXTRA_CHAT_ID, chatId);
|
||||
intent.putExtra(CallActivity.EXTRA_HAS_VIDEO, hasVideo);
|
||||
intent.putExtra(CallActivity.EXTRA_HASH, "#startCall");
|
||||
context.startActivity(intent);
|
||||
}
|
||||
|
||||
public static void openCall(Context context, int accId, int chatId, int callId, String payload) {
|
||||
public static void openCall(Context context, int accId, int chatId, int callId, String payload, boolean hasVideo) {
|
||||
String base64 = Base64.encodeToString(payload.getBytes(StandardCharsets.UTF_8), Base64.NO_WRAP);
|
||||
String hash = "";
|
||||
try {
|
||||
@@ -53,6 +54,7 @@ public class CallUtil {
|
||||
intent.putExtra(CallActivity.EXTRA_ACCOUNT_ID, accId);
|
||||
intent.putExtra(CallActivity.EXTRA_CHAT_ID, chatId);
|
||||
intent.putExtra(CallActivity.EXTRA_CALL_ID, callId);
|
||||
intent.putExtra(CallActivity.EXTRA_HAS_VIDEO, hasVideo);
|
||||
intent.putExtra(CallActivity.EXTRA_HASH, hash);
|
||||
context.startActivity(intent);
|
||||
}
|
||||
|
||||
@@ -71,6 +71,8 @@ public class CallItemView extends FrameLayout {
|
||||
title.setText(isOutgoing? R.string.outgoing_call : R.string.incoming_call);
|
||||
}
|
||||
|
||||
icon.setImageResource(callInfo.hasVideo? R.drawable.ic_videocam_white_24dp : R.drawable.baseline_call_24);
|
||||
|
||||
int[] attrs;
|
||||
if (isOutgoing) {
|
||||
attrs = new int[]{
|
||||
|
||||
@@ -138,7 +138,7 @@ public class AccountManager {
|
||||
// ui
|
||||
|
||||
public void showSwitchAccountMenu(ConversationListActivity activity, boolean selectOnly) {
|
||||
AccountSelectionListFragment dialog = new AccountSelectionListFragment(activity, selectOnly);
|
||||
AccountSelectionListFragment dialog = AccountSelectionListFragment.newInstance(selectOnly);
|
||||
dialog.show(((FragmentActivity) activity).getSupportFragmentManager(), null);
|
||||
}
|
||||
|
||||
|
||||
@@ -164,6 +164,23 @@ public class DcEventCenter {
|
||||
});
|
||||
}
|
||||
|
||||
public void handleLogging(@NonNull DcEvent event) {
|
||||
final String logPrefix = "[accId="+event.getAccountId()+ "] ";
|
||||
switch (event.getId()) {
|
||||
case DcContext.DC_EVENT_INFO:
|
||||
Log.i("DeltaChat", logPrefix + event.getData2Str());
|
||||
break;
|
||||
|
||||
case DcContext.DC_EVENT_WARNING:
|
||||
Log.w("DeltaChat", logPrefix + event.getData2Str());
|
||||
break;
|
||||
|
||||
case DcContext.DC_EVENT_ERROR:
|
||||
Log.e("DeltaChat", logPrefix + event.getData2Str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public long handleEvent(@NonNull DcEvent event) {
|
||||
int accountId = event.getAccountId();
|
||||
int id = event.getId();
|
||||
@@ -205,20 +222,7 @@ public class DcEventCenter {
|
||||
return 0;
|
||||
}
|
||||
|
||||
final String logPrefix = "[accId="+accountId + "] ";
|
||||
switch (id) {
|
||||
case DcContext.DC_EVENT_INFO:
|
||||
Log.i("DeltaChat", logPrefix + event.getData2Str());
|
||||
break;
|
||||
|
||||
case DcContext.DC_EVENT_WARNING:
|
||||
Log.w("DeltaChat", logPrefix + event.getData2Str());
|
||||
break;
|
||||
|
||||
case DcContext.DC_EVENT_ERROR:
|
||||
Log.e("DeltaChat", logPrefix + event.getData2Str());
|
||||
break;
|
||||
}
|
||||
handleLogging(event);
|
||||
|
||||
if (accountId != context.getDcContext().getAccountId()) {
|
||||
return 0;
|
||||
|
||||
@@ -61,6 +61,8 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import chat.delta.rpc.RpcException;
|
||||
|
||||
public class NotificationCenter {
|
||||
private static final String TAG = NotificationCenter.class.getSimpleName();
|
||||
@NonNull private final ApplicationContext context;
|
||||
@@ -164,7 +166,7 @@ public class NotificationCenter {
|
||||
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT | IntentUtils.FLAG_MUTABLE());
|
||||
}
|
||||
|
||||
public PendingIntent getOpenCallIntent(ChatData chatData, int callId, String payload, boolean autoAccept) {
|
||||
public PendingIntent getOpenCallIntent(ChatData chatData, int callId, String payload, boolean autoAccept, boolean hasVideo) {
|
||||
final Intent chatIntent = new Intent(context, ConversationActivity.class)
|
||||
.putExtra(ConversationActivity.ACCOUNT_ID_EXTRA, chatData.accountId)
|
||||
.putExtra(ConversationActivity.CHAT_ID_EXTRA, chatData.chatId)
|
||||
@@ -184,6 +186,7 @@ public class NotificationCenter {
|
||||
intent.putExtra(CallActivity.EXTRA_CHAT_ID, chatData.chatId);
|
||||
intent.putExtra(CallActivity.EXTRA_CALL_ID, callId);
|
||||
intent.putExtra(CallActivity.EXTRA_HASH, hash);
|
||||
intent.putExtra(CallActivity.EXTRA_HAS_VIDEO, hasVideo);
|
||||
intent.setPackage(context.getPackageName());
|
||||
return TaskStackBuilder.create(context)
|
||||
.addNextIntentWithParentStack(chatIntent)
|
||||
@@ -427,6 +430,13 @@ public class NotificationCenter {
|
||||
Util.runOnAnyBackgroundThread(() -> {
|
||||
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
|
||||
DcContext dcContext = context.getDcAccounts().getAccount(accId);
|
||||
boolean hasVideo;
|
||||
try {
|
||||
hasVideo = context.getRpc().callInfo(accId, callId).hasVideo;
|
||||
} catch (RpcException e) {
|
||||
Log.e(TAG, "Rpc.callInfo() failed", e);
|
||||
hasVideo = false;
|
||||
}
|
||||
int chatId = dcContext.getMsg(callId).getChatId();
|
||||
DcChat dcChat = dcContext.getChat(chatId);
|
||||
String name = dcChat.getName();
|
||||
@@ -434,7 +444,7 @@ public class NotificationCenter {
|
||||
String notificationChannel = getCallNotificationChannel(notificationManager, chatData, name);
|
||||
|
||||
PendingIntent declineCallIntent = getDeclineCallIntent(chatData, callId);
|
||||
PendingIntent openCallIntent = getOpenCallIntent(chatData, callId, payload, false);
|
||||
PendingIntent openCallIntent = getOpenCallIntent(chatData, callId, payload, false, hasVideo);
|
||||
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, notificationChannel)
|
||||
.setSmallIcon(R.drawable.icon_notification)
|
||||
@@ -459,7 +469,7 @@ public class NotificationCenter {
|
||||
new NotificationCompat.Action.Builder(
|
||||
R.drawable.ic_videocam_white_24dp,
|
||||
context.getString(R.string.answer_call),
|
||||
getOpenCallIntent(chatData, callId, payload, true)).build());
|
||||
getOpenCallIntent(chatData, callId, payload, true, hasVideo)).build());
|
||||
|
||||
Bitmap bitmap = getAvatar(dcChat);
|
||||
if (bitmap != null) {
|
||||
|
||||
@@ -36,19 +36,24 @@ import chat.delta.rpc.types.Reactions;
|
||||
|
||||
public class ReactionsDetailsFragment extends DialogFragment implements DcEventCenter.DcEventDelegate {
|
||||
private static final String TAG = ReactionsDetailsFragment.class.getSimpleName();
|
||||
private static final String ARG_MSG_ID = "msg_id";
|
||||
|
||||
private RecyclerView recyclerView;
|
||||
private ReactionRecipientsAdapter adapter;
|
||||
private final int msgId;
|
||||
private int msgId;
|
||||
|
||||
public ReactionsDetailsFragment(int msgId) {
|
||||
super();
|
||||
this.msgId = msgId;
|
||||
public static ReactionsDetailsFragment newInstance(int msgId) {
|
||||
ReactionsDetailsFragment fragment = new ReactionsDetailsFragment();
|
||||
Bundle args = new Bundle();
|
||||
args.putInt(ARG_MSG_ID, msgId);
|
||||
fragment.setArguments(args);
|
||||
return fragment;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
msgId = getArguments() != null? getArguments().getInt(ARG_MSG_ID, 0) : 0;
|
||||
adapter = new ReactionRecipientsAdapter(requireActivity(), GlideApp.with(requireActivity()), new ListClickListener());
|
||||
|
||||
LayoutInflater inflater = requireActivity().getLayoutInflater();
|
||||
|
||||
@@ -390,11 +390,27 @@ public class ViewUtil {
|
||||
/**
|
||||
* Apply window insets to a view by adding padding to avoid drawing elements behind system bars.
|
||||
* Convenience method that applies insets to all sides.
|
||||
* IME insets are propagated to child views.
|
||||
*
|
||||
* @param view The view to apply insets to
|
||||
*/
|
||||
public static void applyWindowInsets(@NonNull View view) {
|
||||
applyWindowInsets(view, true, true, true, true);
|
||||
applyWindowInsets(view, true, true, true, true, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply window insets to a view by adding padding to avoid drawing elements behind system bars.
|
||||
*
|
||||
* IME insets are propagated to child views.
|
||||
*
|
||||
* @param view The view to apply insets to
|
||||
* @param left Whether to apply left inset
|
||||
* @param top Whether to apply top inset
|
||||
* @param right Whether to apply right inset
|
||||
* @param bottom Whether to apply bottom inset
|
||||
*/
|
||||
public static void applyWindowInsets(@NonNull View view, boolean left, boolean top, boolean right, boolean bottom) {
|
||||
applyWindowInsets(view, left, top, right, bottom, false);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -408,8 +424,9 @@ public class ViewUtil {
|
||||
* @param top Whether to apply top inset
|
||||
* @param right Whether to apply right inset
|
||||
* @param bottom Whether to apply bottom inset
|
||||
* @param consumeImeInsets Whether to consume IME insets so they don't propagate to child views
|
||||
*/
|
||||
public static void applyWindowInsets(@NonNull View view, boolean left, boolean top, boolean right, boolean bottom) {
|
||||
public static void applyWindowInsets(@NonNull View view, boolean left, boolean top, boolean right, boolean bottom, boolean consumeImeInsets) {
|
||||
// Only enable on API 30+ where WindowInsets APIs work correctly
|
||||
if (!isEdgeToEdgeSupported()) return;
|
||||
|
||||
@@ -442,6 +459,11 @@ public class ViewUtil {
|
||||
bottom ? basePaddingBottom + insets.bottom : basePaddingBottom
|
||||
);
|
||||
|
||||
if (consumeImeInsets) {
|
||||
windowInsets = new WindowInsetsCompat.Builder(windowInsets)
|
||||
.setInsets(WindowInsetsCompat.Type.ime(), Insets.NONE)
|
||||
.build();
|
||||
}
|
||||
return windowInsets;
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user