From 1de41afdf8e47f0fb652479e1e609c9d392b2184 Mon Sep 17 00:00:00 2001 From: ThomasSession Date: Tue, 14 Oct 2025 14:50:05 +1100 Subject: [PATCH 1/6] SES-4676 - Fix up vibration for incoming call ring --- .../securesms/webrtc/AudioManagerCommand.kt | 2 +- .../securesms/webrtc/CallManager.kt | 2 +- .../securesms/webrtc/audio/IncomingRinger.kt | 78 ++++++++++++++----- .../webrtc/audio/SignalAudioManager.kt | 8 +- 4 files changed, 64 insertions(+), 26 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/webrtc/AudioManagerCommand.kt b/app/src/main/java/org/thoughtcrime/securesms/webrtc/AudioManagerCommand.kt index c6dd6525e9..1171fc190b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/webrtc/AudioManagerCommand.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/webrtc/AudioManagerCommand.kt @@ -26,7 +26,7 @@ open class AudioManagerCommand: Parcelable { data class Stop(val playDisconnect: Boolean): AudioManagerCommand() @Parcelize - data class StartIncomingRinger(val vibrate: Boolean): AudioManagerCommand() + data object StartIncomingRinger: AudioManagerCommand() @Parcelize data class SetUserDevice(val device: SignalAudioManager.AudioDevice): AudioManagerCommand() diff --git a/app/src/main/java/org/thoughtcrime/securesms/webrtc/CallManager.kt b/app/src/main/java/org/thoughtcrime/securesms/webrtc/CallManager.kt index 62be057c74..0fb4d6755b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/webrtc/CallManager.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/webrtc/CallManager.kt @@ -823,7 +823,7 @@ class CallManager @Inject constructor( } fun startIncomingRinger() { - signalAudioManager.handleCommand(AudioManagerCommand.StartIncomingRinger(true)) + signalAudioManager.handleCommand(AudioManagerCommand.StartIncomingRinger) } fun startCommunication() { diff --git a/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/IncomingRinger.kt b/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/IncomingRinger.kt index d150c653e3..36a4658ed5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/IncomingRinger.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/IncomingRinger.kt @@ -1,11 +1,16 @@ package org.thoughtcrime.securesms.webrtc.audio +import android.app.NotificationManager import android.content.Context import android.media.AudioAttributes import android.media.AudioManager import android.media.MediaPlayer import android.media.RingtoneManager +import android.os.Build +import android.os.VibrationAttributes +import android.os.VibrationEffect import android.os.Vibrator +import android.os.VibratorManager import org.session.libsession.utilities.ServiceUtil import org.session.libsignal.utilities.Log @@ -15,56 +20,89 @@ class IncomingRinger(private val context: Context) { val PATTERN = longArrayOf(0L, 1000L, 1000L) } - private val vibrator: Vibrator? = ServiceUtil.getVibrator(context) var mediaPlayer: MediaPlayer? = null val isRinging: Boolean get() = mediaPlayer?.isPlaying ?: false - fun start(vibrate: Boolean) { + fun start() { val audioManager = ServiceUtil.getAudioManager(context) mediaPlayer?.release() mediaPlayer = createMediaPlayer() - val ringerMode = audioManager.ringerMode - if (shouldVibrate(mediaPlayer, ringerMode, vibrate)) { - Log.i(TAG,"Starting vibration") - vibrator?.vibrate(PATTERN, 1) - } else { - Log.i(TAG,"Skipping vibration") - } + // Vibrate if policy/system allows + if (shouldVibrate(audioManager)) vibrate() + // Play ringtone only in NORMAL mediaPlayer?.let { player -> - if (ringerMode == AudioManager.RINGER_MODE_NORMAL) { + if (audioManager.ringerMode == AudioManager.RINGER_MODE_NORMAL) { try { if (!player.isPlaying) { player.prepare() player.start() - Log.i(TAG,"Playing ringtone") + Log.i(TAG, "Playing ringtone") } } catch (e: Exception) { - Log.e(TAG,"Failed to start mediaPlayer", e) + Log.e(TAG, "Failed to start mediaPlayer", e) } } } ?: run { - Log.w(TAG,"Not ringing, mediaPlayer: ${mediaPlayer?.let{"available"}}, mode: $ringerMode") + Log.w(TAG,"Not ringing, mediaPlayer: ${mediaPlayer?.let{"available"}}") } - } fun stop() { mediaPlayer?.release() mediaPlayer = null - vibrator?.cancel() + if (Build.VERSION.SDK_INT >= 31) { + context.getSystemService(VibratorManager::class.java) + ?.defaultVibrator?.cancel() + } else { + context.getSystemService(Vibrator::class.java)?.cancel() + } } - private fun shouldVibrate(player: MediaPlayer?, ringerMode: Int, vibrate: Boolean): Boolean { - player ?: return true + private fun shouldVibrate(audioManager: AudioManager): Boolean { + val v = ServiceUtil.getVibrator(context) ?: return false + if (!v.hasVibrator()) return false - if (vibrator == null || !vibrator.hasVibrator()) return false + // Respect 'Do Not Disturb' + val nm = context.getSystemService(NotificationManager::class.java) + when (nm?.currentInterruptionFilter) { + NotificationManager.INTERRUPTION_FILTER_NONE, + NotificationManager.INTERRUPTION_FILTER_ALARMS -> return false + } - return if (vibrate) ringerMode != AudioManager.RINGER_MODE_SILENT - else ringerMode == AudioManager.RINGER_MODE_VIBRATE + return when (audioManager.ringerMode) { + AudioManager.RINGER_MODE_SILENT -> false + AudioManager.RINGER_MODE_VIBRATE -> true + AudioManager.RINGER_MODE_NORMAL -> true + else -> false + } + } + + private fun vibrate() { + if (Build.VERSION.SDK_INT >= 31) { + val vm = context.getSystemService(VibratorManager::class.java) ?: return + val v = vm.defaultVibrator + if (!v.hasVibrator()) return + + val effect = VibrationEffect.createWaveform(PATTERN, 1) + if (Build.VERSION.SDK_INT >= 33) { + val attrs = VibrationAttributes.Builder() + .setUsage(VibrationAttributes.USAGE_RINGTONE) + .build() + v.vibrate(effect, attrs) + } else { + v.vibrate(effect) + } + } else { + val v = context.getSystemService(Vibrator::class.java) ?: return + if (!v.hasVibrator()) return + + val effect = VibrationEffect.createWaveform(PATTERN, 1) + v.vibrate(effect) + } } private fun createMediaPlayer(): MediaPlayer? { diff --git a/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/SignalAudioManager.kt b/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/SignalAudioManager.kt index de3636f502..11f26f9738 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/SignalAudioManager.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/SignalAudioManager.kt @@ -72,7 +72,7 @@ class SignalAudioManager(private val context: Context, is AudioManagerCommand.Stop -> stop(command.playDisconnect) is AudioManagerCommand.SetDefaultDevice -> setDefaultAudioDevice(command.device, command.clearUserEarpieceSelection) is AudioManagerCommand.SetUserDevice -> selectAudioDevice(command.device) - is AudioManagerCommand.StartIncomingRinger -> startIncomingRinger(command.vibrate) + is AudioManagerCommand.StartIncomingRinger -> startIncomingRinger() is AudioManagerCommand.SilenceIncomingRinger -> silenceIncomingRinger() is AudioManagerCommand.StartOutgoingRinger -> startOutgoingRinger(command.type) } @@ -331,11 +331,11 @@ class SignalAudioManager(private val context: Context, } } - private fun startIncomingRinger(vibrate: Boolean) { - Log.i(TAG, "startIncomingRinger(): vibrate: $vibrate") + private fun startIncomingRinger() { + Log.i(TAG, "startIncomingRinger()") androidAudioManager.mode = AudioManager.MODE_RINGTONE - incomingRinger.start(vibrate) + incomingRinger.start() } private fun silenceIncomingRinger() { From 1e05b7a05d874276549489fdf7aa62501a2cd20c Mon Sep 17 00:00:00 2001 From: ThomasSession Date: Tue, 14 Oct 2025 15:15:09 +1100 Subject: [PATCH 2/6] Do not crash in conversation without an address, instead take the user back home --- .../conversation/v2/ConversationActivityV2.kt | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt index 2ef2cf13ce..71633b62c2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt @@ -173,6 +173,7 @@ import org.thoughtcrime.securesms.dependencies.ConfigFactory import org.thoughtcrime.securesms.giph.ui.GiphyActivity import org.thoughtcrime.securesms.groups.GroupMembersActivity import org.thoughtcrime.securesms.groups.OpenGroupManager +import org.thoughtcrime.securesms.home.HomeActivity import org.thoughtcrime.securesms.home.search.searchName import org.thoughtcrime.securesms.linkpreview.LinkPreviewRepository import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil @@ -282,9 +283,7 @@ class ConversationActivityV2 : ScreenLockActionBarActivity(), InputBarDelegate, } private val address: Address.Conversable by lazy { - requireNotNull(IntentCompat.getParcelableExtra(intent, ADDRESS, Address.Conversable::class.java)) { - "Address must be provided in the intent extras" - } + IntentCompat.getParcelableExtra(intent, ADDRESS, Address.Conversable::class.java)!! // safe to do !! since we check for this in onCreate } private val viewModel: ConversationViewModel by viewModels(extrasProducer = { @@ -521,6 +520,18 @@ class ConversationActivityV2 : ScreenLockActionBarActivity(), InputBarDelegate, override fun onCreate(savedInstanceState: Bundle?, isReady: Boolean) { super.onCreate(savedInstanceState, isReady) + + // Check if address is null before proceeding with initialization + if (IntentCompat.getParcelableExtra(intent, ADDRESS, Address.Conversable::class.java) == null) { + Log.w(TAG, "ConversationActivityV2 launched without ADDRESS extra - Returning home") + val intent = Intent(this, HomeActivity::class.java).apply { + flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK + } + startActivity(intent) + finish() + return + } + binding = ActivityConversationV2Binding.inflate(layoutInflater) setContentView(binding.root) From 3ef4235a1b81b28c6324c77afe7c73a2f9af8e3b Mon Sep 17 00:00:00 2001 From: ThomasSession Date: Tue, 14 Oct 2025 15:21:43 +1100 Subject: [PATCH 3/6] Make sure we reset our observable prefs --- .../utilities/TextSecurePreferences.kt | 16 +++++++++------- .../securesms/util/ClearDataUtils.kt | 4 +++- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/org/session/libsession/utilities/TextSecurePreferences.kt b/app/src/main/java/org/session/libsession/utilities/TextSecurePreferences.kt index 6db04761b8..811771e60d 100644 --- a/app/src/main/java/org/session/libsession/utilities/TextSecurePreferences.kt +++ b/app/src/main/java/org/session/libsession/utilities/TextSecurePreferences.kt @@ -930,12 +930,6 @@ interface TextSecurePreferences { setBooleanPreference(context, FINGERPRINT_KEY_GENERATED, true) } - @JvmStatic - fun clearAll(context: Context) { - getDefaultSharedPreferences(context).edit().clear().commit() - } - - // ----- Get / set methods for if we have already warned the user that saving attachments will allow other apps to access them ----- // Note: We only ever show the warning dialog about this ONCE - when the user accepts this fact we write true to the flag & never show again. @JvmStatic @@ -1656,8 +1650,16 @@ class AppTextSecurePreferences @Inject constructor( return getBooleanPreference(AUTOPLAY_AUDIO_MESSAGES, false) } + /** + * Clear all prefs and reset or observables + */ override fun clearAll() { - getDefaultSharedPreferences(context).edit().clear().commit() + pushEnabled.update { false } + localNumberState.update { null } + postProLaunchState.update { false } + hiddenPasswordState.update { false } + + getDefaultSharedPreferences(context).edit(commit = true) { clear() } } override fun getHidePassword() = getBooleanPreference(HIDE_PASSWORD, false) diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/ClearDataUtils.kt b/app/src/main/java/org/thoughtcrime/securesms/util/ClearDataUtils.kt index 841e8930b6..4baecaf6e2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/ClearDataUtils.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/util/ClearDataUtils.kt @@ -50,7 +50,9 @@ class ClearDataUtils @Inject constructor( application.deleteDatabase(DatabaseMigrationManager.CIPHER4_DB_NAME) application.deleteDatabase(DatabaseMigrationManager.CIPHER3_DB_NAME) - TextSecurePreferences.clearAll(application) + // clear all prefs + prefs.clearAll() + application.getSharedPreferences(ApplicationContext.PREFERENCES_NAME, 0).edit(commit = true) { clear() } application.cacheDir.deleteRecursively() application.filesDir.deleteRecursively() From 56e1c4c1d750adce6d4614dee23fcd152f5f30d2 Mon Sep 17 00:00:00 2001 From: ThomasSession Date: Tue, 14 Oct 2025 15:31:44 +1100 Subject: [PATCH 4/6] typo and error refining --- .../main/java/org/session/libsession/snode/OnionRequestAPI.kt | 2 +- .../org/session/libsession/utilities/TextSecurePreferences.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/org/session/libsession/snode/OnionRequestAPI.kt b/app/src/main/java/org/session/libsession/snode/OnionRequestAPI.kt index fdcb94193b..fe47f23487 100644 --- a/app/src/main/java/org/session/libsession/snode/OnionRequestAPI.kt +++ b/app/src/main/java/org/session/libsession/snode/OnionRequestAPI.kt @@ -60,7 +60,7 @@ object OnionRequestAPI { .map { it.isNotEmpty() } .stateIn(GlobalScope, SharingStarted.Eagerly, paths.value.isNotEmpty()) - private val NON_PENALIZING_STATUSES = setOf(400, 403, 404, 406, 425) + private val NON_PENALIZING_STATUSES = setOf(403, 404, 406, 425) init { // Listen for the changes in paths and persist it to the db diff --git a/app/src/main/java/org/session/libsession/utilities/TextSecurePreferences.kt b/app/src/main/java/org/session/libsession/utilities/TextSecurePreferences.kt index 811771e60d..bd83b3cfc4 100644 --- a/app/src/main/java/org/session/libsession/utilities/TextSecurePreferences.kt +++ b/app/src/main/java/org/session/libsession/utilities/TextSecurePreferences.kt @@ -1651,7 +1651,7 @@ class AppTextSecurePreferences @Inject constructor( } /** - * Clear all prefs and reset or observables + * Clear all prefs and reset our observables */ override fun clearAll() { pushEnabled.update { false } From 1153675c4d3911d7ba2bb1b7f0440e14a84e7b48 Mon Sep 17 00:00:00 2001 From: ThomasSession Date: Tue, 14 Oct 2025 15:41:22 +1100 Subject: [PATCH 5/6] We shouldn't penalise a 400 from a SOGS --- .../java/org/session/libsession/snode/OnionRequestAPI.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/org/session/libsession/snode/OnionRequestAPI.kt b/app/src/main/java/org/session/libsession/snode/OnionRequestAPI.kt index fe47f23487..116d5fbc2c 100644 --- a/app/src/main/java/org/session/libsession/snode/OnionRequestAPI.kt +++ b/app/src/main/java/org/session/libsession/snode/OnionRequestAPI.kt @@ -418,10 +418,10 @@ object OnionRequestAPI { Log.d("Loki","Request returned a non penalizing code ${exception.statusCode} with message: $message") } // we do not want to penalize the path/nodes when: - // - the exit node reached the server but the destination returned 5xx - // - the exit node couldn't reach its destination with a 5xx, but the destination was a community (which we can know from the server's name being in the error message) + // - the exit node reached the server but the destination returned 5xx or 400 + // - the exit node couldn't reach its destination with a 5xx or 400, but the destination was a community (which we can know from the server's name being in the error message) else if (destination is Destination.Server && - (exception.statusCode in 500..504) && + (exception.statusCode in 500..504 || exception.statusCode == 400) && (exception is HTTPRequestFailedAtDestinationException || exception.body?.contains(destination.host) == true)) { Log.d("Loki","Destination server error - Non path penalizing. Request returned code ${exception.statusCode} with message: $message") } else if (message == "Loki Server error") { From 480523d6f7931a9f425efd3df1adb430cdce0283 Mon Sep 17 00:00:00 2001 From: ThomasSession Date: Wed, 15 Oct 2025 12:13:02 +1100 Subject: [PATCH 6/6] PR feedback --- .../securesms/conversation/v2/ConversationActivityV2.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt index 71633b62c2..c68d14c1de 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt @@ -283,7 +283,9 @@ class ConversationActivityV2 : ScreenLockActionBarActivity(), InputBarDelegate, } private val address: Address.Conversable by lazy { - IntentCompat.getParcelableExtra(intent, ADDRESS, Address.Conversable::class.java)!! // safe to do !! since we check for this in onCreate + requireNotNull(IntentCompat.getParcelableExtra(intent, ADDRESS, Address.Conversable::class.java)) { + "Address must be provided in the intent extras to open a conversation" + } } private val viewModel: ConversationViewModel by viewModels(extrasProducer = {