diff --git a/src/main/java/org/thoughtcrime/securesms/calls/CallActivity.java b/src/main/java/org/thoughtcrime/securesms/calls/CallActivity.java index 9be3a683b..e5605499e 100644 --- a/src/main/java/org/thoughtcrime/securesms/calls/CallActivity.java +++ b/src/main/java/org/thoughtcrime/securesms/calls/CallActivity.java @@ -95,6 +95,11 @@ 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); @@ -152,18 +157,21 @@ public class CallActivity extends AppCompatActivity { // 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) { @@ -205,7 +213,6 @@ public class CallActivity extends AppCompatActivity { if (!coordinator.hasActiveCall()) { Log.e(TAG, "No active call exists, cannot proceed"); - Toast.makeText(this, "No active call", Toast.LENGTH_SHORT).show(); finish(); return; } @@ -834,11 +841,41 @@ public class CallActivity extends AppCompatActivity { 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 public void onRequestPermissionsResult( int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); + awaitingPermissionResult = false; + pausedWhileAwaitingPermission = false; + CallCoordinator coordinator = CallCoordinator.getInstance(getApplication()); if (requestCode == MIC_PERMISSION_REQUEST_CODE) { @@ -846,22 +883,7 @@ public class CallActivity extends AppCompatActivity { grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED; if (!micGranted) { - Toast.makeText(this, "Microphone permission is required for calls", Toast.LENGTH_LONG) - .show(); - - if (coordinator.hasActiveCall() - && coordinator.isIncomingCall() - && !coordinator.hasOngoingCall()) { - coordinator.declineCall(); - } - - finish(); - return; - } - - if (shouldRequestCameraPermission()) { - ActivityCompat.requestPermissions( - this, new String[] {Manifest.permission.CAMERA}, CAMERA_PERMISSION_REQUEST_CODE); + handleMicPermissionDenied(); return; } @@ -878,7 +900,7 @@ public class CallActivity extends AppCompatActivity { } } - handleIntents(getIntent()); + proceedAfterPermissions(); } // Picture-in-Picture @@ -887,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(); @@ -944,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(); diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 302727c64..cf87b881c 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -417,6 +417,7 @@ Missed call Already in a call Call answered on another device + Microphone permission is required for calls