Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Configuration;
import android.os.Build;

import androidx.core.content.ContextCompat;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.util.ArrayMap;
Expand Down Expand Up @@ -79,7 +80,12 @@ public void init(final Context context) {
IntentFilter intentFilterLoaded = new IntentFilter("LOADED");

try {
context.registerReceiver(mReceiver, intentFilterLoaded);
ContextCompat.registerReceiver(
context,
mReceiver,
intentFilterLoaded,
ContextCompat.RECEIVER_NOT_EXPORTED
);
} catch (Exception e) {

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.database.Cursor;

import androidx.core.content.ContextCompat;
import android.database.sqlite.SqliteWrapper;
import android.net.Uri;
import android.os.Bundle;
Expand Down Expand Up @@ -57,7 +59,12 @@ public void downloadMultimediaMessage(final Context context, final String locati
mMap.put(location, receiver);

// Use unique action in order to avoid cancellation of notifying download result.
context.getApplicationContext().registerReceiver(receiver, new IntentFilter(receiver.mAction));
ContextCompat.registerReceiver(
context.getApplicationContext(),
receiver,
new IntentFilter(receiver.mAction),
ContextCompat.RECEIVER_NOT_EXPORTED
);

Timber.v("receiving with system method");
final String fileName = "download." + String.valueOf(Math.abs(new Random().nextLong())) + ".dat";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;

import androidx.core.content.ContextCompat;
import android.database.Cursor;
import android.database.sqlite.SqliteWrapper;
import android.net.ConnectivityManager;
Expand Down Expand Up @@ -186,7 +188,12 @@ public void onCreate() {
mReceiver = new ConnectivityBroadcastReceiver();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
registerReceiver(mReceiver, intentFilter);
ContextCompat.registerReceiver(
this,
mReceiver,
intentFilter,
ContextCompat.RECEIVER_NOT_EXPORTED
);
mConnMgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;

import androidx.core.content.ContextCompat;
import android.database.Cursor;
import android.database.sqlite.SqliteWrapper;
import android.provider.Telephony.Mms.Rate;
Expand Down Expand Up @@ -124,8 +126,12 @@ synchronized public boolean isAllowedByUser() {
}
sMutexLock = true;

mContext.registerReceiver(mBroadcastReceiver,
new IntentFilter(RATE_LIMIT_CONFIRMED_ACTION));
ContextCompat.registerReceiver(
mContext,
mBroadcastReceiver,
new IntentFilter(RATE_LIMIT_CONFIRMED_ACTION),
ContextCompat.RECEIVER_NOT_EXPORTED
);

mAnswer = NO_ANSWER;
try {
Expand Down
10 changes: 7 additions & 3 deletions data/src/main/java/com/moez/QKSMS/manager/BluetoothMicManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import android.content.IntentFilter
import android.media.AudioDeviceInfo
import android.media.AudioManager
import android.media.AudioManager.GET_DEVICES_INPUTS
import androidx.core.content.ContextCompat


// this class is, by design, as simplistic it can be to support easy and fast connection
Expand All @@ -45,9 +46,12 @@ class BluetoothMicManager(

init {
// register for bluetooth sco broadcast intents
context.registerReceiver(this, IntentFilter().apply {
addAction(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED)
})
ContextCompat.registerReceiver(
context,
this,
IntentFilter(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED),
ContextCompat.RECEIVER_NOT_EXPORTED
)
}

enum class StartBluetoothDevice {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,30 @@ class EmojiReactionRepositoryImpl @Inject constructor(
return requireNotNull(adapter.fromJson(json)) { "Invalid emoji patterns JSON" }
}

override fun buildOutgoingReactionBody(
emoji: String,
targetMessageText: String,
isRemoval: Boolean
): String? {
if (targetMessageText.isBlank()) return null

val text = targetMessageText.trim()
val (added, removed) = when (emoji) {
"❤️" -> "Loved “$text”" to "Removed a heart from “$text”"
"👍" -> "Liked “$text”" to "Removed a like from “$text”"
"👎" -> "Disliked “$text”" to "Removed a dislike from “$text”"
"😂" -> "Laughed at “$text”" to "Removed a laugh from “$text”"
"‼️" -> "Emphasized “$text”" to "Removed an exclamation from “$text”"
"❓" -> "Questioned “$text”" to "Removed a question mark from “$text”"
else -> "Reacted $emoji to “$text”" to "Removed $emoji from “$text”"
}

return if (isRemoval) removed else added
}

override fun parseEmojiReaction(body: String): ParsedEmojiReaction? {
Timber.d("Attempting to parse reaction from body: '$body'")

val removal = parseRemoval(body)
if (removal != null) return removal

Expand All @@ -133,10 +156,11 @@ class EmojiReactionRepositoryImpl @Inject constructor(
val result = parser(match)
if (result == null) continue

Timber.d("Reaction found with ${result.emoji}")
Timber.d("Reaction found with ${result.emoji} for message: '${result.originalMessage}'")
return result
}

Timber.d("No reaction pattern matched for body: '$body'")
return null
}

Expand Down Expand Up @@ -170,17 +194,23 @@ class EmojiReactionRepositoryImpl @Inject constructor(
.sort("date", Sort.DESCENDING)
.findAll()
val endTime = System.currentTimeMillis()
Timber.d("Found ${messages.size} messages as potential emoji targets in ${endTime - startTime}ms")
Timber.d("Searching for target message in thread $threadId: found ${messages.size} messages in ${endTime - startTime}ms")
Timber.d("Looking for message text: '$originalMessageText'")

val match = messages.find { message ->
message.getText(false).trim() == originalMessageText.trim()
val msgText = message.getText(false).trim()
val matches = msgText == originalMessageText.trim()
if (!matches && msgText.isNotEmpty()) {
Timber.v(" Checked message ${message.id}: '$msgText' - no match")
}
matches
}
if (match != null) {
Timber.d("Found match for reaction target: message ID ${match.id}")
return match
}

Timber.w("No target message found for reaction text: '$originalMessageText'")
Timber.w("No target message found for reaction text: '$originalMessageText' in thread $threadId")
return null
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -401,9 +401,11 @@ open class MessageRepositoryImpl @Inject constructor(
addresses: Collection<String>,
body: String,
attachments: Collection<Attachment>,
delay: Int
delay: Int,
applySignature: Boolean
) {
val signedBody = when {
!applySignature -> body
prefs.signature.get().isEmpty() -> body
body.isNotEmpty() -> body + '\n' + prefs.signature.get()
else -> prefs.signature.get()
Expand Down Expand Up @@ -683,6 +685,9 @@ open class MessageRepositoryImpl @Inject constructor(
body: String,
date: Long
): Message {
// Check if this is a reaction message before inserting
val parsedReaction = reactions.parseEmojiReaction(body)

// Insert the message to Realm
val message = Message().apply {
this.threadId = threadId
Expand All @@ -696,6 +701,8 @@ open class MessageRepositoryImpl @Inject constructor(
type = "sms"
read = true
seen = true
// Mark as reaction upfront so it's hidden from the start
isEmojiReaction = parsedReaction != null
}

// Insert the message to the native content provider
Expand Down Expand Up @@ -730,6 +737,25 @@ open class MessageRepositoryImpl @Inject constructor(
}
}
}

// If this is a reaction message, find the target and link them
if (parsedReaction != null) {
managedMessage?.let { savedMessage ->
val targetMessage = reactions.findTargetMessage(
savedMessage.threadId,
parsedReaction.originalMessage,
realm
)
realm.executeTransaction {
reactions.saveEmojiReaction(
savedMessage,
parsedReaction,
targetMessage,
realm,
)
}
}
}
}

// On some devices, we can't obtain a threadId until after the first message is sent in a
Expand Down Expand Up @@ -856,6 +882,9 @@ open class MessageRepositoryImpl @Inject constructor(
body: String,
sentTime: Long
): Message {
// Check if this is a reaction message before inserting
val parsedReaction = reactions.parseEmojiReaction(body)

// Insert the message to Realm
val message = Message().apply {
this.address = address
Expand All @@ -869,6 +898,8 @@ open class MessageRepositoryImpl @Inject constructor(
boxId = Sms.MESSAGE_TYPE_INBOX
type = "sms"
read = activeConversationManager.getActiveConversation() == threadId
// Mark as reaction upfront so it's hidden from the start
isEmojiReaction = parsedReaction != null
}

// Insert the message to the native content provider
Expand All @@ -891,9 +922,9 @@ open class MessageRepositoryImpl @Inject constructor(
realm.executeTransaction { managedMessage?.contentId = id }
}

managedMessage?.let { savedMessage ->
val parsedReaction = reactions.parseEmojiReaction(body)
if (parsedReaction != null) {
// If this is a reaction message, find the target and link them
if (parsedReaction != null) {
managedMessage?.let { savedMessage ->
val targetMessage = reactions.findTargetMessage(
savedMessage.threadId,
parsedReaction.originalMessage,
Expand Down
7 changes: 4 additions & 3 deletions domain/src/main/java/com/moez/QKSMS/interactor/SendMessage.kt
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ class SendMessage @Inject constructor(
val addresses: List<String>,
val body: String,
val attachments: List<Attachment> = listOf(),
val delay: Int = 0
val delay: Int = 0,
val applySignature: Boolean = true,
)

override fun buildObservable(params: Params): Flowable<*> = Flowable.just(Unit)
Expand All @@ -57,7 +58,7 @@ class SendMessage @Inject constructor(
return@doOnNext

params.apply {
messageRepo.sendMessage(subId, threadId, addresses, body, attachments, delay)
messageRepo.sendMessage(subId, threadId, addresses, body, attachments, delay, applySignature)
}

conversationRepo.updateConversations(threadId)
Expand All @@ -71,4 +72,4 @@ class SendMessage @Inject constructor(
}
.flatMap { updateBadge.buildObservable(Unit) } // Update the widget

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ data class ParsedEmojiReaction(val emoji: String, val originalMessage: String, v
interface EmojiReactionRepository {
fun parseEmojiReaction(body: String): ParsedEmojiReaction?

/**
* Builds an outgoing reaction/tapback body that matches iOS-style SMS fallback formatting.
*/
fun buildOutgoingReactionBody(emoji: String, targetMessageText: String, isRemoval: Boolean = false): String?

fun findTargetMessage(threadId: Long, originalMessageText: String, realm: Realm): Message?

fun saveEmojiReaction(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ interface MessageRepository {
addresses: Collection<String>,
body: String,
attachments: Collection<Attachment>,
delay: Int = 0
delay: Int = 0,
applySignature: Boolean = true,
)

/**
Expand Down
1 change: 1 addition & 0 deletions domain/src/main/java/com/moez/QKSMS/util/Preferences.kt
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ class Preferences @Inject constructor(
val disableScreenshots = rxPrefs.getBoolean("disableScreenshots", false)
val logging = rxPrefs.getBoolean("logging", false)
val unreadAtTop = rxPrefs.getBoolean("unreadAtTop", false)
val bubbles = rxPrefs.getBoolean("bubbles", false)

init {
// Migrate from old night mode preference to new one, now that we support android Q night mode
Expand Down
5 changes: 4 additions & 1 deletion presentation/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,10 @@
android:name=".feature.compose.ComposeActivity"
android:exported="true"
android:parentActivityName=".feature.main.MainActivity"
android:windowSoftInputMode="stateHidden">
android:windowSoftInputMode="stateHidden"
android:documentLaunchMode="always"
android:allowEmbedded="true"
android:resizeableActivity="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.MAIN" />
Expand Down
Loading
Loading