mirror of
https://github.com/ArcaneChat/android.git
synced 2026-07-03 14:05:24 +02:00
Merge branch 'main' into r10s/markfresh
This commit is contained in:
@@ -396,7 +396,7 @@ public class Rpc {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets messages to be processed by the bot and returns their IDs.
|
||||
* (deprecated) Gets messages to be processed by the bot and returns their IDs.
|
||||
* <p>
|
||||
* Only messages with database ID higher than `last_msg_id` config value
|
||||
* are returned. After processing the messages, the bot should
|
||||
@@ -404,6 +404,13 @@ public class Rpc {
|
||||
* or manually updating the value to avoid getting already
|
||||
* processed messages.
|
||||
* <p>
|
||||
* Deprecated 2026-04: This returns the message's id as soon as the first part arrives,
|
||||
* even if it is not fully downloaded yet.
|
||||
* The bot needs to wait for the message to be fully downloaded.
|
||||
* Since this is usually not the desired behavior,
|
||||
* bots should instead use the #DC_EVENT_INCOMING_MSG / [`types::events::EventType::IncomingMsg`]
|
||||
* event for getting notified about new messages.
|
||||
* <p>
|
||||
* [`markseen_msgs`]: Self::markseen_msgs
|
||||
*/
|
||||
public java.util.List<Integer> getNextMsgs(Integer accountId) throws RpcException {
|
||||
@@ -411,7 +418,7 @@ public class Rpc {
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for messages to be processed by the bot and returns their IDs.
|
||||
* (deprecated) Waits for messages to be processed by the bot and returns their IDs.
|
||||
* <p>
|
||||
* This function is similar to [`get_next_msgs`],
|
||||
* but waits for internal new message notification before returning.
|
||||
@@ -422,6 +429,13 @@ public class Rpc {
|
||||
* To shutdown the bot, stopping I/O can be used to interrupt
|
||||
* pending or next `wait_next_msgs` call.
|
||||
* <p>
|
||||
* Deprecated 2026-04: This returns the message's id as soon as the first part arrives,
|
||||
* even if it is not fully downloaded yet.
|
||||
* The bot needs to wait for the message to be fully downloaded.
|
||||
* Since this is usually not the desired behavior,
|
||||
* bots should instead use the #DC_EVENT_INCOMING_MSG / [`types::events::EventType::IncomingMsg`]
|
||||
* event for getting notified about new messages.
|
||||
* <p>
|
||||
* [`get_next_msgs`]: Self::get_next_msgs
|
||||
*/
|
||||
public java.util.List<Integer> waitNextMsgs(Integer accountId) throws RpcException {
|
||||
|
||||
@@ -534,8 +534,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
|
||||
menu.findItem(R.id.menu_start_call)
|
||||
.setVisible(
|
||||
Prefs.isCallsEnabled(this)
|
||||
&& Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
|
||||
&& dcChat.canSend()
|
||||
&& dcChat.isEncrypted()
|
||||
&& !dcChat.isSelfTalk()
|
||||
|
||||
@@ -227,6 +227,7 @@ public class WebxdcActivity extends WebViewActivity implements DcEventCenter.DcE
|
||||
|
||||
WebSettings webSettings = webView.getSettings();
|
||||
webSettings.setJavaScriptEnabled(true);
|
||||
webSettings.setMediaPlaybackRequiresUserGesture(false);
|
||||
webSettings.setAllowFileAccess(false);
|
||||
webSettings.setBlockNetworkLoads(!internetAccess);
|
||||
webSettings.setAllowContentAccess(false);
|
||||
|
||||
@@ -52,7 +52,8 @@ import org.webrtc.VideoTrack;
|
||||
public class CallActivity extends AppCompatActivity {
|
||||
|
||||
private static final String TAG = CallActivity.class.getSimpleName();
|
||||
private static final int PERMISSION_REQUEST_CODE = 1001;
|
||||
private static final int MIC_PERMISSION_REQUEST_CODE = 1001;
|
||||
private static final int CAMERA_PERMISSION_REQUEST_CODE = 1002;
|
||||
|
||||
public static final String ACTION_ANSWER_CALL = BuildConfig.APPLICATION_ID + ".ANSWER_CALL";
|
||||
public static final String ACTION_DECLINE_CALL = BuildConfig.APPLICATION_ID + ".DECLINE_CALL";
|
||||
@@ -94,10 +95,30 @@ public class CallActivity extends AppCompatActivity {
|
||||
|
||||
private PowerManager.WakeLock proximityWakeLock;
|
||||
|
||||
// States
|
||||
private boolean awaitingPermissionResult = false;
|
||||
private boolean pausedWhileAwaitingPermission = false;
|
||||
private boolean intentHandled = false;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// Destructive actions need nothing
|
||||
String action = getIntent() != null ? getIntent().getAction() : null;
|
||||
if (ACTION_DECLINE_CALL.equals(action)) {
|
||||
Log.d(TAG, "Handling DECLINE_CALL action from notification");
|
||||
CallCoordinator.getInstance(getApplication()).declineCall();
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
if (ACTION_HANGUP_CALL.equals(action)) {
|
||||
Log.d(TAG, "Handling HANGUP_CALL action from notification");
|
||||
CallCoordinator.getInstance(getApplication()).hangUp();
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
setContentView(R.layout.activity_call);
|
||||
|
||||
setupWindowFlags();
|
||||
@@ -108,11 +129,6 @@ public class CallActivity extends AppCompatActivity {
|
||||
|
||||
initializeProximityWakeLock();
|
||||
|
||||
if (!hasRequiredPermissions()) {
|
||||
requestRequiredPermissions();
|
||||
return;
|
||||
}
|
||||
|
||||
// PiP listener
|
||||
addOnPictureInPictureModeChangedListener(
|
||||
pipModeInfo -> {
|
||||
@@ -139,47 +155,74 @@ public class CallActivity extends AppCompatActivity {
|
||||
|
||||
initializeViewModel();
|
||||
|
||||
// Intent handling needs permissions
|
||||
if (!hasMicrophonePermission()) {
|
||||
awaitingPermissionResult = true;
|
||||
ActivityCompat.requestPermissions(
|
||||
this, new String[] {Manifest.permission.RECORD_AUDIO}, MIC_PERMISSION_REQUEST_CODE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (shouldRequestCameraPermission()) {
|
||||
awaitingPermissionResult = true;
|
||||
ActivityCompat.requestPermissions(
|
||||
this, new String[] {Manifest.permission.CAMERA}, CAMERA_PERMISSION_REQUEST_CODE);
|
||||
return;
|
||||
}
|
||||
|
||||
handleIntents(getIntent());
|
||||
intentHandled = true;
|
||||
}
|
||||
|
||||
private void handleIntents(Intent intent) {
|
||||
if (intent == null || viewModel == null) {
|
||||
if (intent == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
CallCoordinator coordinator = CallCoordinator.getInstance(getApplication());
|
||||
|
||||
if (!coordinator.hasActiveCall()) {
|
||||
Log.e(TAG, "No active call exists, cannot proceed");
|
||||
Toast.makeText(this, "No active call", Toast.LENGTH_SHORT).show();
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
String action = intent.getAction();
|
||||
Log.d(TAG, "handleIntents: action=" + action);
|
||||
|
||||
// Handle notification actions
|
||||
if (ACTION_ANSWER_CALL.equals(action)) {
|
||||
Log.d(TAG, "Handling ANSWER_CALL action from notification");
|
||||
viewModel.handleNotificationAnswer();
|
||||
return;
|
||||
}
|
||||
|
||||
// Destructive actions without ViewModel
|
||||
if (ACTION_DECLINE_CALL.equals(action)) {
|
||||
Log.d(TAG, "Handling DECLINE_CALL action from notification");
|
||||
viewModel.handleNotificationDecline();
|
||||
Log.d(TAG, "Handling DECLINE_CALL action");
|
||||
if (viewModel != null) {
|
||||
viewModel.handleNotificationDecline();
|
||||
} else {
|
||||
coordinator.declineCall();
|
||||
}
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
if (ACTION_HANGUP_CALL.equals(action)) {
|
||||
Log.d(TAG, "Handling HANGUP_CALL action from notification");
|
||||
viewModel.handleNotificationHangup();
|
||||
Log.d(TAG, "Handling HANGUP_CALL action");
|
||||
if (viewModel != null) {
|
||||
viewModel.handleNotificationHangup();
|
||||
} else {
|
||||
coordinator.hangUp();
|
||||
}
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
if (viewModel == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!coordinator.hasActiveCall()) {
|
||||
Log.e(TAG, "No active call exists, cannot proceed");
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
if (ACTION_ANSWER_CALL.equals(action)) {
|
||||
Log.d(TAG, "Handling ANSWER_CALL action from notification");
|
||||
viewModel.handleNotificationAnswer();
|
||||
return;
|
||||
}
|
||||
|
||||
if (coordinator.hasOngoingCall()) {
|
||||
Log.d(TAG, "Resuming existing call");
|
||||
} else if (!coordinator.isIncomingCall()) {
|
||||
@@ -783,18 +826,46 @@ public class CallActivity extends AppCompatActivity {
|
||||
|
||||
// Permissions
|
||||
|
||||
private boolean hasRequiredPermissions() {
|
||||
return ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
|
||||
== PackageManager.PERMISSION_GRANTED
|
||||
&& ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO)
|
||||
== PackageManager.PERMISSION_GRANTED;
|
||||
private boolean hasMicrophonePermission() {
|
||||
return ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO)
|
||||
== PackageManager.PERMISSION_GRANTED;
|
||||
}
|
||||
|
||||
private void requestRequiredPermissions() {
|
||||
ActivityCompat.requestPermissions(
|
||||
this,
|
||||
new String[] {Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO},
|
||||
PERMISSION_REQUEST_CODE);
|
||||
private boolean hasCameraPermission() {
|
||||
return ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
|
||||
== PackageManager.PERMISSION_GRANTED;
|
||||
}
|
||||
|
||||
private boolean shouldRequestCameraPermission() {
|
||||
CallCoordinator coordinator = CallCoordinator.getInstance(getApplication());
|
||||
return coordinator.isStartsWithVideo() && !hasCameraPermission();
|
||||
}
|
||||
|
||||
private void handleMicPermissionDenied() {
|
||||
Toast.makeText(this, R.string.call_requires_mic_permission, Toast.LENGTH_LONG).show();
|
||||
|
||||
CallCoordinator coordinator = CallCoordinator.getInstance(getApplication());
|
||||
if (coordinator.hasActiveCall()
|
||||
&& coordinator.isIncomingCall()
|
||||
&& !coordinator.hasOngoingCall()) {
|
||||
coordinator.declineCall();
|
||||
}
|
||||
|
||||
finish();
|
||||
}
|
||||
|
||||
private void proceedAfterPermissions() {
|
||||
if (intentHandled) return;
|
||||
|
||||
if (shouldRequestCameraPermission()) {
|
||||
awaitingPermissionResult = true;
|
||||
ActivityCompat.requestPermissions(
|
||||
this, new String[] {Manifest.permission.CAMERA}, CAMERA_PERMISSION_REQUEST_CODE);
|
||||
return;
|
||||
}
|
||||
|
||||
handleIntents(getIntent());
|
||||
intentHandled = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -802,38 +873,34 @@ public class CallActivity extends AppCompatActivity {
|
||||
int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
|
||||
if (requestCode == PERMISSION_REQUEST_CODE) {
|
||||
boolean microphoneGranted = false;
|
||||
boolean cameraGranted = false;
|
||||
awaitingPermissionResult = false;
|
||||
pausedWhileAwaitingPermission = false;
|
||||
|
||||
for (int i = 0; i < permissions.length; i++) {
|
||||
if (permissions[i].equals(Manifest.permission.RECORD_AUDIO)) {
|
||||
microphoneGranted = (grantResults[i] == PackageManager.PERMISSION_GRANTED);
|
||||
} else if (permissions[i].equals(Manifest.permission.CAMERA)) {
|
||||
cameraGranted = (grantResults[i] == PackageManager.PERMISSION_GRANTED);
|
||||
}
|
||||
}
|
||||
CallCoordinator coordinator = CallCoordinator.getInstance(getApplication());
|
||||
|
||||
if (!microphoneGranted) {
|
||||
Toast.makeText(this, "Microphone permission is required for calls", Toast.LENGTH_LONG)
|
||||
.show();
|
||||
finish();
|
||||
if (requestCode == MIC_PERMISSION_REQUEST_CODE) {
|
||||
boolean micGranted =
|
||||
grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED;
|
||||
|
||||
if (!micGranted) {
|
||||
handleMicPermissionDenied();
|
||||
return;
|
||||
}
|
||||
|
||||
CallCoordinator coordinator = CallCoordinator.getInstance(getApplication());
|
||||
} else if (requestCode == CAMERA_PERMISSION_REQUEST_CODE) {
|
||||
boolean cameraGranted =
|
||||
grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED;
|
||||
|
||||
if (!cameraGranted && coordinator.isStartsWithVideo()) {
|
||||
if (!cameraGranted) {
|
||||
Log.w(TAG, "Camera permission denied, switching to audio-only");
|
||||
Toast.makeText(
|
||||
this, "Starting audio-only call (camera permission denied)", Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
coordinator.setStartsWithVideo(false);
|
||||
}
|
||||
|
||||
initializeViewModel();
|
||||
handleIntents(getIntent());
|
||||
}
|
||||
|
||||
proceedAfterPermissions();
|
||||
}
|
||||
|
||||
// Picture-in-Picture
|
||||
@@ -842,6 +909,11 @@ public class CallActivity extends AppCompatActivity {
|
||||
public void onUserLeaveHint() {
|
||||
super.onUserLeaveHint();
|
||||
|
||||
// Do not finish activity when a permission request is pending
|
||||
if (awaitingPermissionResult) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Enter PiP mode when user presses home button during active call
|
||||
if (viewModel != null) {
|
||||
CallViewModel.CallState state = viewModel.getCallState().getValue();
|
||||
@@ -899,12 +971,44 @@ public class CallActivity extends AppCompatActivity {
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
|
||||
if (awaitingPermissionResult) {
|
||||
pausedWhileAwaitingPermission = true;
|
||||
}
|
||||
|
||||
if (proximityWakeLock != null && proximityWakeLock.isHeld()) {
|
||||
proximityWakeLock.release();
|
||||
Log.d(TAG, "Proximity wake lock released in onDestroy");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
|
||||
// Fallback for Android 16 bug: onRequestPermissionsResult not called
|
||||
if (awaitingPermissionResult && pausedWhileAwaitingPermission) {
|
||||
Log.w(TAG, "Permission result callback not received, handling in onResume");
|
||||
|
||||
awaitingPermissionResult = false;
|
||||
pausedWhileAwaitingPermission = false;
|
||||
|
||||
if (!hasMicrophonePermission()) {
|
||||
handleMicPermissionDenied();
|
||||
return;
|
||||
}
|
||||
|
||||
// Mic was granted without callback
|
||||
if (shouldRequestCameraPermission()) {
|
||||
awaitingPermissionResult = true;
|
||||
ActivityCompat.requestPermissions(
|
||||
this, new String[] {Manifest.permission.CAMERA}, CAMERA_PERMISSION_REQUEST_CODE);
|
||||
return;
|
||||
}
|
||||
|
||||
proceedAfterPermissions();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
|
||||
@@ -527,22 +527,7 @@ public class CallCoordinator implements DcEventCenter.DcEventDelegate {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check microphone and camera permissions
|
||||
if (!hasMicrophonePermission()) {
|
||||
Log.e(TAG, "Microphone permission not granted");
|
||||
Intent intent = new Intent(appContext, CallActivity.class);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||
appContext.startActivity(intent);
|
||||
notificationManager.cancel(NOTIFICATION_ID_CALL);
|
||||
return;
|
||||
}
|
||||
|
||||
if (startsWithVideo && !hasCameraPermission()) {
|
||||
Log.w(TAG, "Camera permission not granted");
|
||||
startsWithVideo = false;
|
||||
}
|
||||
|
||||
// Launch CallActivity with answer action
|
||||
// Launch CallActivity
|
||||
Intent intent = new Intent(appContext, CallActivity.class);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||
appContext.startActivity(intent);
|
||||
@@ -1218,7 +1203,7 @@ public class CallCoordinator implements DcEventCenter.DcEventDelegate {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check camera and microphone permissions
|
||||
// Check microphone permission
|
||||
if (!hasMicrophonePermission()) {
|
||||
Log.e(TAG, "Microphone permission not granted");
|
||||
|
||||
@@ -1228,11 +1213,6 @@ public class CallCoordinator implements DcEventCenter.DcEventDelegate {
|
||||
return;
|
||||
}
|
||||
|
||||
if (startsWithVideo && !hasCameraPermission()) {
|
||||
Log.w(TAG, "Camera permission not granted, will start audio-only");
|
||||
startsWithVideo = false;
|
||||
}
|
||||
|
||||
resetLiveDataForNewCall();
|
||||
|
||||
this.activeCallId = -1; // Placeholder call ID for Intent
|
||||
@@ -1378,10 +1358,8 @@ public class CallCoordinator implements DcEventCenter.DcEventDelegate {
|
||||
}
|
||||
|
||||
// Check full screen intent permission on Android 14+
|
||||
boolean canUseFullScreen = false;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
||||
canUseFullScreen = canUseFullScreenIntent();
|
||||
if (!canUseFullScreen) {
|
||||
if (!canUseFullScreenIntent()) {
|
||||
Log.w(TAG, "Full screen intent permission not granted, notification will appear normally");
|
||||
}
|
||||
}
|
||||
@@ -1422,7 +1400,7 @@ public class CallCoordinator implements DcEventCenter.DcEventDelegate {
|
||||
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
|
||||
|
||||
Notification.Builder builder;
|
||||
if (canUseFullScreen && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
// Android 12+, CallStyle
|
||||
Person caller =
|
||||
new Person.Builder().setName(callerName).setIcon(callerIcon).setImportant(true).build();
|
||||
|
||||
@@ -142,17 +142,13 @@ public class CallService extends Service implements WebRTCClient.Callbacks {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check permissions
|
||||
boolean hasMicrophone = callCoordinator.hasMicrophonePermission();
|
||||
boolean hasCamera = callCoordinator.hasCameraPermission();
|
||||
|
||||
if (!hasMicrophone) {
|
||||
if (!callCoordinator.hasMicrophonePermission()) {
|
||||
Log.e(TAG, "Microphone permission not granted, cannot start call");
|
||||
callCoordinator.reportError("Microphone permission is required for calls");
|
||||
return;
|
||||
}
|
||||
|
||||
boolean startsWithVideo = callCoordinator.isStartsWithVideo() && hasCamera;
|
||||
boolean startsWithVideo = callCoordinator.isStartsWithVideo();
|
||||
|
||||
Log.d(TAG, "Creating media stream with video: " + startsWithVideo);
|
||||
|
||||
@@ -172,8 +168,10 @@ public class CallService extends Service implements WebRTCClient.Callbacks {
|
||||
VideoTrack localTrack = stream.videoTracks.get(0);
|
||||
callCoordinator.updateLocalVideoTrack(localTrack);
|
||||
} else {
|
||||
Log.w(TAG, "Camera unavailable, call will be audio-only");
|
||||
callCoordinator.reportError("Camera unavailable, using audio only");
|
||||
Log.w(TAG, "No video track in stream, call will be audio-only");
|
||||
if (startsWithVideo) {
|
||||
callCoordinator.reportError("Camera unavailable, using audio only");
|
||||
}
|
||||
callCoordinator.setVideoEnabled(false);
|
||||
}
|
||||
|
||||
@@ -183,7 +181,9 @@ public class CallService extends Service implements WebRTCClient.Callbacks {
|
||||
@Override
|
||||
public void onError(String error) {
|
||||
Log.e(TAG, "Failed to setup media: " + error);
|
||||
callCoordinator.reportError("Camera/microphone error: " + error);
|
||||
if (startsWithVideo) {
|
||||
callCoordinator.reportError("Camera/microphone error: " + error);
|
||||
}
|
||||
callCoordinator.setVideoEnabled(false);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
package org.thoughtcrime.securesms.calls;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import org.thoughtcrime.securesms.EglUtils;
|
||||
import org.webrtc.AudioSource;
|
||||
import org.webrtc.AudioTrack;
|
||||
@@ -93,6 +96,12 @@ public class MediaStreamManager {
|
||||
|
||||
@Nullable
|
||||
private VideoCapturer createVideoCapturer() {
|
||||
if (ContextCompat.checkSelfPermission(context, Manifest.permission.CAMERA)
|
||||
!= PackageManager.PERMISSION_GRANTED) {
|
||||
Log.w(TAG, "Camera permission not granted");
|
||||
return null;
|
||||
}
|
||||
|
||||
Camera2Enumerator enumerator = new Camera2Enumerator(context);
|
||||
|
||||
// Try front camera first
|
||||
|
||||
@@ -187,24 +187,6 @@ public class AdvancedPreferenceFragment extends ListSummaryPreferenceFragment
|
||||
});
|
||||
}
|
||||
|
||||
Preference callsEnabled = this.findPreference("pref_calls_enabled");
|
||||
if (callsEnabled != null) {
|
||||
callsEnabled.setOnPreferenceChangeListener(
|
||||
(preference, newValue) -> {
|
||||
if ((Boolean) newValue) {
|
||||
new AlertDialog.Builder(requireActivity())
|
||||
.setTitle("Thanks for helping to debug \"Calls\"!")
|
||||
.setMessage(
|
||||
"• You can now debug calls using the \"phone\" icon in one-to-one-chats\n\n"
|
||||
+ "• The experiment is about making decentralised calls work and reliable at all, not about options or UI. We're happy about focused feedback at support.delta.chat\n\n")
|
||||
.setCancelable(false)
|
||||
.setPositiveButton(R.string.ok, null)
|
||||
.show();
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
selfReportingCheckbox = this.findPreference("pref_stats_sending");
|
||||
if (selfReportingCheckbox != null) {
|
||||
selfReportingCheckbox.setOnPreferenceChangeListener(
|
||||
|
||||
@@ -208,7 +208,6 @@ public class PersistentBlobProvider {
|
||||
return getFile(context, ContentUris.parseId(uri)).delete();
|
||||
}
|
||||
|
||||
//noinspection SimplifiableIfStatement
|
||||
if (isExternalBlobUri(context, uri)) {
|
||||
return new File(uri.getPath()).delete();
|
||||
}
|
||||
@@ -287,6 +286,20 @@ public class PersistentBlobProvider {
|
||||
|
||||
private static @NonNull File getExternalDir(Context context) throws IOException {
|
||||
File externalDir = context.getExternalCacheDir();
|
||||
|
||||
if (externalDir != null) {
|
||||
try {
|
||||
FileProviderUtil.getUriFor(context, new File(externalDir, "test"));
|
||||
} catch (IllegalArgumentException e) {
|
||||
Log.w(
|
||||
TAG,
|
||||
"External cache dir not resolvable by FileProvider, "
|
||||
+ "falling back to internal cache",
|
||||
e);
|
||||
externalDir = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (externalDir == null) {
|
||||
externalDir = context.getCacheDir();
|
||||
}
|
||||
|
||||
@@ -202,10 +202,6 @@ public class Prefs {
|
||||
return getBooleanPreference(context, "pref_new_broadcast_list", false);
|
||||
}
|
||||
|
||||
public static boolean isCallsEnabled(Context context) {
|
||||
return getBooleanPreference(context, "pref_calls_enabled", false);
|
||||
}
|
||||
|
||||
// ringtone
|
||||
|
||||
public static @NonNull Uri getNotificationRingtone(Context context) {
|
||||
|
||||
Reference in New Issue
Block a user