Skip to content

Commit 91ea5c1

Browse files
authored
Merge pull request NdoleStudio#133 from eriksson/message_sim_card
Select which SIM to use to send messages
2 parents 400962a + e407bed commit 91ea5c1

35 files changed

Lines changed: 290 additions & 47 deletions

android/app/src/main/AndroidManifest.xml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,15 @@
6969
</intent-filter>
7070
</receiver>
7171

72+
<receiver android:enabled="true"
73+
android:name=".receivers.SimChangeReceiver"
74+
android:exported="true"
75+
android:permission="android.permission.READ_PHONE_STATE">
76+
<intent-filter>
77+
<action android:name="android.intent.action.SIM_STATE_CHANGED"/>
78+
</intent-filter>
79+
</receiver>
80+
7281

7382
<meta-data
7483
android:name="com.google.firebase.messaging.default_notification_channel_id"

android/app/src/main/java/com/httpsms/FirebaseMessagingService.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ class MyFirebaseMessagingService : FirebaseMessagingService() {
6767

6868
if (Settings.isLoggedIn(this)) {
6969
Timber.d("updating phone with new fcm token")
70-
HttpSmsApiService.create(this).updatePhone(Settings.getOwnerOrDefault(this), token)
70+
HttpSmsApiService.create(this).updatePhone(Settings.getOwnerOrDefault(this), token, SmsManagerService.isDualSIM(this))
7171
}
7272

7373
}
@@ -131,7 +131,7 @@ class MyFirebaseMessagingService : FirebaseMessagingService() {
131131
sentIntents.add(createPendingIntent(id, SmsManagerService.sentAction(id)))
132132
deliveredIntents.add(createPendingIntent(id, SmsManagerService.deliveredAction(id)))
133133
}
134-
SmsManagerService().sendMultipartMessage(this.applicationContext,message.contact, parts, sentIntents, deliveredIntents)
134+
SmsManagerService().sendMultipartMessage(this.applicationContext,message.contact, parts, message.sim, sentIntents, deliveredIntents)
135135
Timber.d("sent SMS for message with ID [${message.id}] in [${parts.size}] parts")
136136
Result.success()
137137
} catch (e: Exception) {
@@ -185,7 +185,7 @@ class MyFirebaseMessagingService : FirebaseMessagingService() {
185185
private fun sendMessage(message: Message, sentIntent: PendingIntent, deliveredIntent: PendingIntent) {
186186
Timber.d("sending SMS for message with ID [${message.id}]")
187187
try {
188-
SmsManagerService().sendTextMessage(this.applicationContext,message.contact, message.content, sentIntent, deliveredIntent)
188+
SmsManagerService().sendTextMessage(this.applicationContext,message.contact, message.content, message.sim, sentIntent, deliveredIntent)
189189
} catch (e: Exception) {
190190
Timber.e(e)
191191
Timber.d("could not send SMS for message with ID [${message.id}]")

android/app/src/main/java/com/httpsms/HttpSmsApiService.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,11 +156,12 @@ class HttpSmsApiService(private val apiKey: String, private val baseURL: URI) {
156156
}
157157

158158

159-
fun updatePhone(phoneNumber: String, fcmToken: String): Boolean {
159+
fun updatePhone(phoneNumber: String, fcmToken: String, isDualSIM: Boolean): Boolean {
160160
val body = """
161161
{
162162
"fcm_token": "$fcmToken",
163-
"phone_number": "$phoneNumber"
163+
"phone_number": "$phoneNumber",
164+
"is_dual_sim": $isDualSIM
164165
}
165166
""".trimIndent()
166167

android/app/src/main/java/com/httpsms/MainActivity.kt

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
package com.httpsms
22

33
import android.Manifest
4-
import android.Manifest.permission.READ_PHONE_NUMBERS
54
import android.app.NotificationChannel
65
import android.app.NotificationManager
76
import android.content.Context
87
import android.content.Intent
8+
import android.content.IntentFilter
99
import android.content.pm.PackageManager
1010
import android.os.Build
1111
import android.os.Bundle
@@ -24,6 +24,7 @@ import com.google.android.material.button.MaterialButton
2424
import com.google.android.material.dialog.MaterialAlertDialogBuilder
2525
import com.google.android.material.progressindicator.LinearProgressIndicator
2626
import com.google.android.material.switchmaterial.SwitchMaterial
27+
import com.httpsms.receivers.SimChangeReceiver
2728
import com.httpsms.services.StickyNotificationService
2829
import com.httpsms.worker.HeartbeatWorker
2930
import okhttp3.internal.format
@@ -61,6 +62,7 @@ class MainActivity : AppCompatActivity() {
6162
setLastHeartbeatTimestamp(this)
6263
setVersion()
6364
setHeartbeatListener(this)
65+
registerReceivers()
6466
}
6567

6668
override fun onResume() {
@@ -144,7 +146,7 @@ class MainActivity : AppCompatActivity() {
144146
}
145147

146148
Thread {
147-
val updated = HttpSmsApiService.create(context).updatePhone(Settings.getOwnerOrDefault(context), Settings.getFcmToken(context) ?: "")
149+
val updated = HttpSmsApiService.create(context).updatePhone(Settings.getOwnerOrDefault(context), Settings.getFcmToken(context) ?: "", SmsManagerService.isDualSIM(this))
148150
if (updated) {
149151
Settings.setFcmTokenLastUpdateTimestampAsync(context, currentTimeStamp)
150152
Timber.i("fcm token uploaded successfully")
@@ -170,6 +172,9 @@ class MainActivity : AppCompatActivity() {
170172
findViewById<MaterialButton>(R.id.mainLogoutButton).setOnClickListener { onLogoutClick() }
171173
}
172174

175+
private fun registerReceivers() {
176+
registerReceiver(SimChangeReceiver(), IntentFilter("android.intent.action.SIM_STATE_CHANGED"))
177+
}
173178
private fun onLogoutClick() {
174179
Timber.d("logout button clicked")
175180
MaterialAlertDialogBuilder(this)
@@ -216,7 +221,7 @@ class MainActivity : AppCompatActivity() {
216221
Manifest.permission.SEND_SMS
217222
) == PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
218223
context,
219-
READ_PHONE_NUMBERS
224+
Manifest.permission.READ_PHONE_NUMBERS
220225
) == PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
221226
context,
222227
Manifest.permission.RECEIVE_SMS
@@ -271,7 +276,7 @@ class MainActivity : AppCompatActivity() {
271276
var permissions = arrayOf(
272277
Manifest.permission.SEND_SMS,
273278
Manifest.permission.RECEIVE_SMS,
274-
READ_PHONE_NUMBERS,
279+
Manifest.permission.READ_PHONE_NUMBERS,
275280
Manifest.permission.READ_SMS,
276281
Manifest.permission.READ_PHONE_STATE
277282
)

android/app/src/main/java/com/httpsms/Models.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ data class ResponseMessage (
2222
data class Message (
2323
val contact: String,
2424
val content: String,
25+
val sim: String,
2526

2627
@Json(name = "created_at")
2728
val createdAt: String,
Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
package com.httpsms
22

3+
import android.annotation.SuppressLint
34
import android.app.PendingIntent
45
import android.content.Context
56
import android.os.Build
67
import android.telephony.SmsManager
8+
import android.telephony.SubscriptionManager
79

810

911
class SmsManagerService {
@@ -18,26 +20,51 @@ class SmsManagerService {
1820
fun deliveredAction(messageID: String): String {
1921
return "$ACTION_SMS_DELIVERED.$messageID"
2022
}
23+
24+
@SuppressLint("MissingPermission")
25+
fun isDualSIM(context: Context) : Boolean {
26+
val localSubscriptionManager: SubscriptionManager = if (Build.VERSION.SDK_INT < 31) {
27+
SubscriptionManager.from(context)
28+
} else {
29+
context.getSystemService(SubscriptionManager::class.java)
30+
}
31+
return localSubscriptionManager.activeSubscriptionInfoList.size > 1
32+
}
2133
}
2234

2335
fun messageParts(context: Context, content: String): ArrayList<String> {
2436
return getSmsManager(context).divideMessage(content)
2537
}
2638

27-
fun sendMultipartMessage(context: Context, contact: String, parts: ArrayList<String>, sendIntents: ArrayList<PendingIntent>, deliveryIntents: ArrayList<PendingIntent>) {
28-
getSmsManager(context).sendMultipartTextMessage(contact, null, parts, sendIntents, deliveryIntents)
39+
fun sendMultipartMessage(context: Context, contact: String, parts: ArrayList<String>, sim: String, sendIntents: ArrayList<PendingIntent>, deliveryIntents: ArrayList<PendingIntent>) {
40+
getSmsManager(context, sim).sendMultipartTextMessage(contact, null, parts, sendIntents, deliveryIntents)
2941
}
3042

31-
fun sendTextMessage(context: Context, contact: String, content: String, sentIntent:PendingIntent, deliveryIntent: PendingIntent) {
32-
getSmsManager(context).sendTextMessage(contact, null, content, sentIntent, deliveryIntent)
43+
fun sendTextMessage(context: Context, contact: String, content: String, sim: String, sentIntent:PendingIntent, deliveryIntent: PendingIntent) {
44+
getSmsManager(context, sim).sendTextMessage(contact, null, content, sentIntent, deliveryIntent)
3345
}
3446

3547
@Suppress("DEPRECATION")
36-
private fun getSmsManager(context: Context): SmsManager {
48+
@SuppressLint("MissingPermission")
49+
private fun getSmsManager(context: Context, sim: String = "DEFAULT"): SmsManager {
50+
val localSubscriptionManager: SubscriptionManager = if (Build.VERSION.SDK_INT < 31) {
51+
SubscriptionManager.from(context)
52+
} else {
53+
context.getSystemService(SubscriptionManager::class.java)
54+
}
55+
56+
val subscriptionId = if (sim == "SIM1" && localSubscriptionManager.activeSubscriptionInfoList.size > 0) {
57+
localSubscriptionManager.activeSubscriptionInfoList[0].subscriptionId
58+
} else if (sim == "SIM2" && localSubscriptionManager.activeSubscriptionInfoList.size > 1) {
59+
localSubscriptionManager.activeSubscriptionInfoList[1].subscriptionId
60+
} else{
61+
SubscriptionManager.getDefaultSmsSubscriptionId()
62+
}
63+
3764
return if (Build.VERSION.SDK_INT < 31) {
38-
SmsManager.getDefault()
65+
SmsManager.getSmsManagerForSubscriptionId(subscriptionId)
3966
} else {
40-
context.getSystemService(SmsManager::class.java)
67+
context.getSystemService(SmsManager::class.java).createForSubscriptionId(subscriptionId)
4168
}
4269
}
4370
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package com.httpsms.receivers
2+
3+
import android.content.BroadcastReceiver
4+
import android.content.Context
5+
import android.content.Intent
6+
import com.httpsms.HttpSmsApiService
7+
import com.httpsms.Settings
8+
import com.httpsms.SmsManagerService
9+
import timber.log.Timber
10+
11+
12+
class SimChangeReceiver : BroadcastReceiver() {
13+
private var lastDualSIMState: Boolean = false
14+
override fun onReceive(context: Context, intent: Intent) {
15+
Timber.d("SIM state changed")
16+
17+
Thread {
18+
val currentTimeStamp = System.currentTimeMillis()
19+
val isDualSIM = SmsManagerService.isDualSIM(context)
20+
if (isDualSIM == lastDualSIMState) {
21+
return@Thread
22+
}
23+
val updated = HttpSmsApiService.create(context).updatePhone(
24+
Settings.getOwnerOrDefault(context),
25+
Settings.getFcmToken(context) ?: "",
26+
isDualSIM
27+
)
28+
29+
if (updated) {
30+
lastDualSIMState = isDualSIM
31+
Settings.setFcmTokenLastUpdateTimestampAsync(context, currentTimeStamp)
32+
Timber.i("fcm token uploaded successfully")
33+
return@Thread
34+
}
35+
36+
Timber.e("could not update fcm token")
37+
}.start()
38+
}
39+
}

api/pkg/entities/message.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,17 @@ const (
6060
MessageEventNameFailed = MessageEventName("FAILED")
6161
)
6262

63+
type SIM string
64+
65+
const (
66+
// SIM_1 use the SIM card in slot 1 to send the message
67+
SIM_1 = SIM("SIM1")
68+
// SIM_2 use the SIM card in slot 2 to send the message
69+
SIM_2 = SIM("SIM2")
70+
// SIM_DEFAULT use the SIM card configured as default communication card to send the message
71+
SIM_DEFAULT = SIM("DEFAULT")
72+
)
73+
6374
// Message represents a message sent between 2 phone numbers
6475
type Message struct {
6576
ID uuid.UUID `json:"id" gorm:"primaryKey;type:uuid;" example:"32343a19-da5e-4b1b-a767-3298a73703cb"`
@@ -69,6 +80,11 @@ type Message struct {
6980
Content string `json:"content" example:"This is a sample text message"`
7081
Type MessageType `json:"type" example:"mobile-terminated"`
7182
Status MessageStatus `json:"status" gorm:"index:idx_messages_status" example:"pending"`
83+
// SIM is the type of event
84+
// * ISMS: use the SIM card in slot 1
85+
// * ISMS2: use the SIM card in slot 2
86+
// * DEFAULT: used the default communication SIM card
87+
SIM SIM `json:"sim" example:"DEFAULT"`
7288

7389
// SendDuration is the number of nanoseconds from when the request was received until when the mobile phone send the message
7490
SendDuration *int64 `json:"send_time" example:"133414"`

api/pkg/entities/phone.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ type Phone struct {
1313
FcmToken *string `json:"fcm_token" example:"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzd....."`
1414
PhoneNumber string `json:"phone_number" example:"+18005550199"`
1515
MessagesPerMinute uint `json:"messages_per_minute" example:"1"`
16+
IsDualSIM bool `json:"is_dual_sim" example:"false"`
1617

1718
// MaxSendAttempts determines how many times to retry sending an SMS message
1819
MaxSendAttempts uint `json:"max_send_attempts" example:"1"`

api/pkg/events/message_api_sent_event.go

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,5 @@ type MessageAPISentPayload struct {
2020
Contact string `json:"contact"`
2121
RequestReceivedAt time.Time `json:"request_received_at"`
2222
Content string `json:"content"`
23-
}
24-
25-
// MessageAPISentPayloadV1 is the old event
26-
type MessageAPISentPayloadV1 struct {
27-
MessageID uuid.UUID `json:"id"`
28-
UserID entities.UserID `json:"userID"`
29-
Owner string `json:"owner"`
30-
MaxSendAttempts uint `json:"max_send_attempts"`
31-
Contact string `json:"contact"`
32-
RequestReceivedAt time.Time `json:"request_received_at"`
33-
Content string `json:"content"`
23+
SIM entities.SIM `json:"sim"`
3424
}

0 commit comments

Comments
 (0)