diff --git a/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java b/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java index 2a46e59106..5dc4b49527 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java +++ b/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java @@ -169,7 +169,7 @@ public class ApplicationContext extends Application implements DefaultLifecycleO @Inject LokiAPIDatabase apiDB; @Inject EmojiSearchDatabase emojiSearchDb; - private volatile boolean isAppVisible; + public static volatile boolean isAppVisible; @Override public Object getSystemService(String name) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/model/MessageRecord.java b/app/src/main/java/org/thoughtcrime/securesms/database/model/MessageRecord.java index 944f10431f..802c7d2374 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/model/MessageRecord.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/model/MessageRecord.java @@ -32,6 +32,7 @@ import org.session.libsession.messaging.utilities.UpdateMessageData; import org.session.libsession.utilities.IdentityKeyMismatch; import org.session.libsession.utilities.NetworkFailure; import org.session.libsession.utilities.recipients.Recipient; +import org.session.libsignal.utilities.Log; import org.thoughtcrime.securesms.dependencies.DatabaseComponent; import java.util.List; @@ -145,8 +146,11 @@ public abstract class MessageRecord extends DisplayRecord { } else if (isOutgoingCall()) { callType = CallMessageType.CALL_OUTGOING; } else if (isMissedCall()) { + Log.w("ACL", "We think CALL_MISSSED"); + callType = CallMessageType.CALL_MISSED; } else { + Log.w("ACL", "We think CALL_FIRST_MISSSED"); callType = CallMessageType.CALL_FIRST_MISSED; } return new SpannableString(UpdateMessageBuilder.INSTANCE.buildCallMessage(context, callType, getIndividualRecipient().getAddress().serialize())); diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/model/ThreadRecord.java b/app/src/main/java/org/thoughtcrime/securesms/database/model/ThreadRecord.java index 033eec0cea..cec3b2bf95 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/model/ThreadRecord.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/model/ThreadRecord.java @@ -105,7 +105,7 @@ public class ThreadRecord extends DisplayRecord { @Override public CharSequence getDisplayBody(@NonNull Context context) { // no need to display anything if there are no messages - if(lastMessage == null){ + if (lastMessage == null){ return ""; } else if (isGroupUpdateMessage()) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/DefaultMessageNotifier.kt b/app/src/main/java/org/thoughtcrime/securesms/notifications/DefaultMessageNotifier.kt index acf98ac0d8..9f07f843c7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/DefaultMessageNotifier.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/DefaultMessageNotifier.kt @@ -45,6 +45,7 @@ import org.session.libsession.messaging.utilities.SodiumUtilities.blindedKeyPair import org.session.libsession.utilities.Address.Companion.fromSerialized import org.session.libsession.utilities.ServiceUtil import org.session.libsession.utilities.StringSubstitutionConstants.EMOJI_KEY +import org.session.libsession.utilities.StringSubstitutionConstants.NAME_KEY import org.session.libsession.utilities.TextSecurePreferences.Companion.getLocalNumber import org.session.libsession.utilities.TextSecurePreferences.Companion.getNotificationPrivacy import org.session.libsession.utilities.TextSecurePreferences.Companion.getRepeatAlertsCount @@ -280,12 +281,21 @@ class DefaultMessageNotifier : MessageNotifier { builder.putStringExtra(LATEST_MESSAGE_ID_TAG, messageIdTag) - val text = notifications[0].text + val notificationText = notifications[0].text + + // TODO: We get missed call notifications whenever we get a call - I have no idea why, and it would be better to strip them out + // TODO: at the source - but I'll stop them here if we recognise the notification text and the home screen is visible + + + // For some reason, even when we're starting a call we get a missed call notification - so we'll bail before that happens. + // TODO: Probably better to fix this at the source so that this never gets called rather then here - do this. + val missedCallString = Phrase.from(context, R.string.callsMissedCallFrom).put(NAME_KEY, notifications[0].recipient.name).format() + if (ApplicationContext.isAppVisible && notificationText == missedCallString) { return } builder.setThread(notifications[0].recipient) builder.setMessageCount(notificationState.messageCount) - val builderCS = text ?: "" + val builderCS = notificationText ?: "" val ss = highlightMentions( builderCS, false, @@ -331,7 +341,6 @@ class DefaultMessageNotifier : MessageNotifier { } val iterator: ListIterator = notifications.listIterator(notifications.size) - while (iterator.hasPrevious()) { val item = iterator.previous() builder.addMessageBody(item.recipient, item.individualRecipient, item.text) @@ -363,6 +372,8 @@ class DefaultMessageNotifier : MessageNotifier { // for ActivityCompat#requestPermissions for more details. return } + + NotificationManagerCompat.from(context).notify(notificationId, notification) Log.i(TAG, "Posted notification. $notification") } diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/KeyCachingService.java b/app/src/main/java/org/thoughtcrime/securesms/service/KeyCachingService.java index a3f9d0d97f..9c1d771af6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/KeyCachingService.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/KeyCachingService.java @@ -235,6 +235,7 @@ public class KeyCachingService extends Service { private void foregroundService() { if (TextSecurePreferences.isPasswordDisabled(this) && !TextSecurePreferences.isScreenLockEnabled(this)) { + Log.w("ACL", "Stopping foreground service in KeyCachingService"); stopForeground(true); return; } @@ -248,8 +249,6 @@ public class KeyCachingService extends Service { .put(APP_NAME_KEY, c.getString(R.string.app_name)) .format().toString(); builder.setContentTitle(unlockedTxt); - - builder.setContentText(getString(R.string.lockAppUnlock)); builder.setSmallIcon(R.drawable.icon_cached); builder.setWhen(0); builder.setPriority(Notification.PRIORITY_MIN); diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/WebRtcCallService.kt b/app/src/main/java/org/thoughtcrime/securesms/service/WebRtcCallService.kt index 8567644514..64f13437e9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/WebRtcCallService.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/service/WebRtcCallService.kt @@ -334,7 +334,7 @@ class WebRtcCallService : LifecycleService(), CallManager.WebRtcListener { networkChangedReceiver = NetworkChangeReceiver(::networkChange) networkChangedReceiver!!.register(this) - Log.w("ACL", "Lesssgo!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!") + //Log.w("ACL", "Lesssgo!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!") //if (appIsBackground(this)) { // ServiceCompat.startForeground(this, // CallNotificationBuilder.WEBRTC_NOTIFICATION, @@ -342,18 +342,10 @@ class WebRtcCallService : LifecycleService(), CallManager.WebRtcListener { // if (Build.VERSION.SDK_INT >= 30) ServiceInfo.FOREGROUND_SERVICE_TYPE_PHONE_CALL else 0 // ) - // Get the KeyguardManager and PowerManager - val keyguardManager = getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager - val powerManager = getSystemService(Context.POWER_SERVICE) as PowerManager - // Check if the phone is locked - val isPhoneLocked = keyguardManager.isKeyguardLocked - - // Check if the screen is awake - val isScreenAwake = powerManager.isInteractive // If the screen is off or phone is locked, wake it up - if (!isScreenAwake || isPhoneLocked) { wakeUpDevice() } + //if (!isScreenAwake || isPhoneLocked) { wakeUpDevice() } // ServiceCompat.startForeground( @@ -365,23 +357,7 @@ class WebRtcCallService : LifecycleService(), CallManager.WebRtcListener { // ) } - private fun wakeUpDevice() { - val powerManager = getSystemService(Context.POWER_SERVICE) as PowerManager - val wakeLock = powerManager.newWakeLock( - PowerManager.FULL_WAKE_LOCK or - PowerManager.ACQUIRE_CAUSES_WAKEUP or - PowerManager.ON_AFTER_RELEASE, - "${NonTranslatableStringConstants.APP_NAME}:WakeLock" - ) - - // Acquire the wake lock to wake up the device - wakeLock.acquire(3000) // Wake up for 3 seconds - // Dismiss the keyguard - val keyguardManager = getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager - val keyguardLock = keyguardManager.newKeyguardLock("MyApp:KeyguardLock") - keyguardLock.disableKeyguard() - } private fun registerUncaughtExceptionHandler() { uncaughtExceptionHandlerManager = UncaughtExceptionHandlerManager().apply { @@ -777,9 +753,51 @@ class WebRtcCallService : LifecycleService(), CallManager.WebRtcListener { } } + private fun wakeUpDeviceIfLocked() { + + // Get the KeyguardManager and PowerManager + val keyguardManager = getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager + val powerManager = getSystemService(Context.POWER_SERVICE) as PowerManager + + // Check if the phone is locked + val isPhoneLocked = keyguardManager.isKeyguardLocked + + // Check if the screen is awake + val isScreenAwake = powerManager.isInteractive + + if (!isScreenAwake) { + + Log.w("ACL", "Screen is NOT awake - waking it up!") + + val wakeLock = powerManager.newWakeLock( + PowerManager.FULL_WAKE_LOCK or + PowerManager.ACQUIRE_CAUSES_WAKEUP or + PowerManager.ON_AFTER_RELEASE, + "${NonTranslatableStringConstants.APP_NAME}:WakeLock" + ) + + // Acquire the wake lock to wake up the device + wakeLock.acquire(3000) // Wake up for 3 seconds + + val startTime = System.currentTimeMillis() + while (!powerManager.isInteractive) { /* Busy wait until we're awake */ } + val endTime = System.currentTimeMillis() + val duration = endTime - startTime + Log.w("ACL", "Woken up in $duration ms") + } else { + Log.w("ACL", "Screen is awake - doing nothing") + } + // Dismiss the keyguard + val keyguardLock = keyguardManager.newKeyguardLock("MyApp:KeyguardLock") + keyguardLock.disableKeyguard() + } + // Over the course of setting up a phonecall this method is called multiple times with `types` of PRE_OFFER -> RING_INCOMING -> ICE_MESSAGE private fun setCallInProgressNotification(type: Int, recipient: Recipient?) { + wakeUpDeviceIfLocked() + + val typeString = when (type) { TYPE_INCOMING_RINGING -> "TYPE_INCOMING_RINGING" TYPE_OUTGOING_RINGING -> "TYPE_OUTGOING_RINGING" @@ -789,7 +807,7 @@ class WebRtcCallService : LifecycleService(), CallManager.WebRtcListener { WEBRTC_NOTIFICATION -> "WEBRTC_NOTIFICATION" else -> "We have no idea!" } - Log.w("ACL", "Hit setCallInProgressNotification with type: $typeString") + Log.w("ACL", "NOOOOOOOOOOTIFICATION - Hit setCallInProgressNotification with type: $typeString") // If notifications are enabled we'll try and start a foreground service to show the notification var failedToStartForegroundService = false @@ -815,9 +833,13 @@ class WebRtcCallService : LifecycleService(), CallManager.WebRtcListener { - if (type == TYPE_INCOMING_PRE_OFFER && failedToStartForegroundService) { + if ((type == TYPE_INCOMING_PRE_OFFER || type == TYPE_INCOMING_RINGING) && failedToStartForegroundService) { + //if (failedToStartForegroundService) { + + Log.w("ACL", "DID SOMETHING - foregroundIntent to WebRtcCallActivity for TYPE_INCOMING_PRE_OFFER") + wakeUpDeviceIfLocked() + - Log.w("ACL", "About to create foregroundIntent and try to start the WebRtcCallActivity with type TYPE_INCOMING_PRE_OFFER") // Start an intent for the fullscreen call activity val foregroundIntent = Intent(this, WebRtcCallActivity::class.java) diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/CallNotificationBuilder.kt b/app/src/main/java/org/thoughtcrime/securesms/util/CallNotificationBuilder.kt index 4ffe12d006..b12b2f5c9a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/CallNotificationBuilder.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/util/CallNotificationBuilder.kt @@ -37,30 +37,30 @@ class CallNotificationBuilder { return notificationManager.areNotificationsEnabled() } - @JvmStatic - fun getFirstCallNotification(context: Context, callerName: String): Notification { - val contentIntent = Intent(context, SettingsActivity::class.java) - - val pendingIntent = PendingIntent.getActivity(context, 0, contentIntent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE) - - val titleTxt = context.getSubbedString(R.string.callsMissedCallFrom, NAME_KEY to callerName) - val bodyTxt = context.getSubbedCharSequence( - R.string.callsYouMissedCallPermissions, - NAME_KEY to callerName - ) - - val builder = NotificationCompat.Builder(context, NotificationChannels.CALLS) - .setSound(null) - .setSmallIcon(R.drawable.ic_baseline_call_24) - .setContentIntent(pendingIntent) - .setPriority(NotificationCompat.PRIORITY_HIGH) - .setContentTitle(titleTxt) - .setContentText(bodyTxt) - .setStyle(NotificationCompat.BigTextStyle().bigText(bodyTxt)) - .setAutoCancel(true) - - return builder.build() - } +// @JvmStatic +// fun getFirstCallNotification(context: Context, callerName: String): Notification { +// val contentIntent = Intent(context, SettingsActivity::class.java) +// +// val pendingIntent = PendingIntent.getActivity(context, 0, contentIntent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE) +// +// val titleTxt = context.getSubbedString(R.string.callsMissedCallFrom, NAME_KEY to callerName) +// val bodyTxt = context.getSubbedCharSequence( +// R.string.callsYouMissedCallPermissions, +// NAME_KEY to callerName +// ) +// +// val builder = NotificationCompat.Builder(context, NotificationChannels.CALLS) +// .setSound(null) +// .setSmallIcon(R.drawable.ic_baseline_call_24) +// .setContentIntent(pendingIntent) +// .setPriority(NotificationCompat.PRIORITY_HIGH) +// .setContentTitle(titleTxt) +// .setContentText(bodyTxt) +// .setStyle(NotificationCompat.BigTextStyle().bigText(bodyTxt)) +// .setAutoCancel(true) +// +// return builder.build() +// } @JvmStatic fun getCallInProgressNotification(context: Context, type: Int, recipient: Recipient?): Notification { diff --git a/app/src/main/java/org/thoughtcrime/securesms/webrtc/CallMessageProcessor.kt b/app/src/main/java/org/thoughtcrime/securesms/webrtc/CallMessageProcessor.kt index 88e147a720..280acbcac2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/webrtc/CallMessageProcessor.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/webrtc/CallMessageProcessor.kt @@ -2,15 +2,18 @@ package org.thoughtcrime.securesms.webrtc import android.Manifest import android.annotation.SuppressLint +import android.app.KeyguardManager import android.app.NotificationChannel import android.app.NotificationManager import android.app.PendingIntent import android.content.Context import android.content.Intent import android.os.Build +import android.os.PowerManager import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat import androidx.core.content.ContextCompat +import androidx.core.content.ContextCompat.getSystemService import androidx.lifecycle.Lifecycle import androidx.lifecycle.coroutineScope import kotlinx.coroutines.Dispatchers.IO @@ -36,7 +39,7 @@ import org.thoughtcrime.securesms.permissions.Permissions import org.thoughtcrime.securesms.service.WebRtcCallService import org.webrtc.IceCandidate import network.loki.messenger.R - +import org.session.libsession.utilities.NonTranslatableStringConstants class CallMessageProcessor(private val context: Context, private val textSecurePreferences: TextSecurePreferences, lifecycle: Lifecycle, private val storage: StorageProtocol) { @@ -56,11 +59,7 @@ class CallMessageProcessor(private val context: Context, private val textSecureP fun safeStartForegroundService(context: Context, intent: Intent) { Log.w("ACL", "Hit safeStartForegroundService for intent action: " + intent.action) - // TODO: This is super-ugly - we're forcing a full-screen intent to wake the device up so we can - // TODO: successfully call `startForegroundService` in the second catch block below. This works - // TODO: even if the device is locked and Session has been closed down - but it's UUUUGLY. Need - // TODO: to find a better way. - showIncomingCallNotification(context) + // If the foreground service crashes then it's possible for one of these intents to // be started in the background (in which case 'startService' will throw a @@ -71,6 +70,12 @@ class CallMessageProcessor(private val context: Context, private val textSecureP try { ContextCompat.startForegroundService(context, intent) } catch (e2: Exception) { Log.e("Loki", "Unable to start CallMessage intent: ${e2.message}") + + // TODO: This is super-ugly - we're forcing a full-screen intent to wake the device up so we can + // TODO: successfully call `startForegroundService` in the second catch block below. This works + // TODO: even if the device is locked and Session has been closed down - but it's UUUUGLY. Need + // TODO: to find a better way. + showIncomingCallNotification(context) } } } @@ -87,8 +92,46 @@ class CallMessageProcessor(private val context: Context, private val textSecureP notificationManager?.createNotificationChannel(channel) } + private fun wakeUpDeviceIfLocked(context: Context) { + + // Get the KeyguardManager and PowerManager + val keyguardManager = context.getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager + val powerManager = context.getSystemService(Context.POWER_SERVICE) as PowerManager + + // Check if the phone is locked + val isPhoneLocked = keyguardManager.isKeyguardLocked + + // Check if the screen is awake + val isScreenAwake = powerManager.isInteractive + + if (!isScreenAwake) { + + Log.w("ACL", "CMP: Screen is NOT awake - waking it up!") + + + val wakeLock = powerManager.newWakeLock( + PowerManager.FULL_WAKE_LOCK or + PowerManager.ACQUIRE_CAUSES_WAKEUP or + PowerManager.ON_AFTER_RELEASE, + "${NonTranslatableStringConstants.APP_NAME}:WakeLock" + ) + + // Acquire the wake lock to wake up the device + wakeLock.acquire(3000) // Wake up for 3 seconds + } else { + Log.w("ACL", "CMP: Screen is awake - doing nothing") + } + // Dismiss the keyguard + //val keyguardManager = getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager + //val keyguardLock = keyguardManager.newKeyguardLock("MyApp:KeyguardLock") + //keyguardLock.disableKeyguard() + } + @SuppressLint("MissingPermission") fun showIncomingCallNotification(context: Context) { + + wakeUpDeviceIfLocked(context) + createNotificationChannel(context) val notificationIntent = Intent(context, WebRtcCallActivity::class.java) @@ -108,7 +151,8 @@ class CallMessageProcessor(private val context: Context, private val textSecureP .setFullScreenIntent(pendingIntent, true) NotificationManagerCompat.from(context).notify(999, notificationBuilder.build()) - } } + } + } init { lifecycle.coroutineScope.launch(IO) {