Skip to content

Commit 5c36b2a

Browse files
Merge pull request #236 from smswithoutborders/add-slack
Adjust message compose to accommodate slack
2 parents ed8ca28 + 2574b46 commit 5c36b2a

13 files changed

Lines changed: 156 additions & 62 deletions

File tree

app/src/main/java/com/example/sw0b_001/ui/views/compose/MessageComposeView.kt

Lines changed: 108 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import android.util.Log
1313
import android.widget.Toast
1414
import androidx.activity.compose.BackHandler
1515
import androidx.activity.compose.rememberLauncherForActivityResult
16+
import androidx.activity.result.contract.ActivityResultContract
1617
import androidx.activity.result.contract.ActivityResultContracts
1718
import androidx.compose.foundation.clickable
1819
import androidx.compose.foundation.layout.Column
@@ -57,6 +58,7 @@ import androidx.navigation.NavController
5758
import com.example.sw0b_001.Database.Datastore
5859
import com.example.sw0b_001.Models.ComposeHandlers
5960
import com.example.sw0b_001.Models.GatewayClients.GatewayClientsCommunications
61+
import com.example.sw0b_001.Models.Platforms.AvailablePlatforms
6062
import com.example.sw0b_001.Models.Platforms.PlatformsViewModel
6163
import com.example.sw0b_001.Models.Platforms.StoredPlatformsEntity
6264
import com.example.sw0b_001.Models.Publishers
@@ -79,8 +81,42 @@ import java.nio.charset.StandardCharsets
7981
import java.util.Locale
8082

8183

84+
class PickPhoneNumberContract : ActivityResultContract<Unit, Uri?>() {
85+
override fun createIntent(context: Context, input: Unit): Intent =
86+
Intent(Intent.ACTION_PICK, ContactsContract.CommonDataKinds.Phone.CONTENT_URI)
87+
88+
override fun parseResult(resultCode: Int, intent: Intent?): Uri? {
89+
return if (resultCode == Activity.RESULT_OK) intent?.data else null
90+
}
91+
}
92+
8293
data class MessageContent(val from: String, val to: String, val message: String)
8394

95+
data class RecipientFieldInfo(val label: String, val hint: String)
96+
97+
@Composable
98+
private fun getRecipientFieldInfo(platform: AvailablePlatforms?): RecipientFieldInfo {
99+
if (platform?.protocol_type == "pnba") {
100+
return RecipientFieldInfo(
101+
label = stringResource(R.string.recipient_number),
102+
hint = stringResource(R.string.always_add_the_dialing_code_if_absent_e_g_237)
103+
)
104+
}
105+
106+
return when (platform?.name) {
107+
"slack" -> RecipientFieldInfo(
108+
label = stringResource(R.string.slack_recipient),
109+
hint = stringResource(R.string.slack_hint)
110+
)
111+
// Add other platform-specific cases here
112+
113+
else -> RecipientFieldInfo(
114+
label = stringResource(R.string.recipient_account),
115+
hint = stringResource(R.string.recipient_account_format_hint)
116+
)
117+
}
118+
}
119+
84120
object MessageComposeHandler {
85121
fun decomposeMessage(contentBytes: ByteArray): MessageContent {
86122
return try {
@@ -134,18 +170,22 @@ fun MessageComposeView(
134170
}
135171
} else null
136172

137-
var recipientNumber by remember { mutableStateOf(decomposedMessage?.to ?: "") }
173+
var recipientAccount by remember { mutableStateOf(decomposedMessage?.to ?: "") }
138174
var message by remember { mutableStateOf( decomposedMessage?.message ?: "") }
139175
var from by remember { mutableStateOf( decomposedMessage?.from ?: "") }
140176

141177
var showSelectAccountModal by remember { mutableStateOf(!inspectMode) }
142178
var selectedAccount by remember { mutableStateOf<StoredPlatformsEntity?>(null) }
179+
val fieldInfo = getRecipientFieldInfo(platform = platformsViewModel.platform)
180+
val isPnba = platformsViewModel.platform?.protocol_type == "pnba"
181+
182+
143183

144184
val launcher = rememberLauncherForActivityResult(
145-
ActivityResultContracts.PickContact()
185+
contract = PickPhoneNumberContract()
146186
) { uri ->
147187
uri?.let {
148-
recipientNumber = getContactDetails(context, uri)
188+
recipientAccount = getPhoneNumberFromUri(context, it)
149189
}
150190
}
151191

@@ -185,12 +225,16 @@ fun MessageComposeView(
185225
}
186226
},
187227
actions = {
228+
229+
val isSendEnabled = recipientAccount.isNotEmpty() && message.isNotEmpty() &&
230+
(if (isPnba) verifyPhoneNumberFormat(recipientAccount) else true)
231+
188232
IconButton(onClick = {
189233
processSend(
190234
context = context,
191235
messageContent = MessageContent(
192236
from = from,
193-
to = recipientNumber,
237+
to = recipientAccount,
194238
message = message
195239
),
196240
account = selectedAccount!!,
@@ -212,8 +256,7 @@ fun MessageComposeView(
212256
}
213257
)
214258
},
215-
enabled = recipientNumber.isNotEmpty() && message.isNotEmpty() &&
216-
verifyPhoneNumberFormat(recipientNumber)) {
259+
enabled = isSendEnabled) {
217260
Icon(Icons.AutoMirrored.Filled.Send, contentDescription = stringResource(R.string.send))
218261
}
219262
},
@@ -249,42 +292,45 @@ fun MessageComposeView(
249292
verticalAlignment = Alignment.CenterVertically
250293
) {
251294
OutlinedTextField(
252-
value = recipientNumber,
253-
onValueChange = { recipientNumber = it },
254-
label = { Text(stringResource(R.string.recipient_number), style = MaterialTheme.typography.bodyMedium) },
295+
value = recipientAccount,
296+
onValueChange = { recipientAccount = it },
297+
label = { Text(fieldInfo.label, style = MaterialTheme.typography.bodyMedium) },
255298
modifier = Modifier.weight(1f),
256-
isError = recipientNumber.isNotEmpty() && !verifyPhoneNumberFormat(recipientNumber),
299+
isError = if (isPnba) recipientAccount.isNotEmpty() && !verifyPhoneNumberFormat(recipientAccount) else false,
257300
keyboardOptions = KeyboardOptions(
258-
keyboardType = KeyboardType.Phone,
301+
keyboardType = if (isPnba) KeyboardType.Phone else KeyboardType.Text,
259302
imeAction = ImeAction.Next
260303
)
261304
)
305+
262306
Spacer(modifier = Modifier.width(8.dp))
263-
IconButton(onClick = {
264-
if(readContactPermissions.status.isGranted) {
265-
launcher.launch(null)
266-
} else {
267-
readContactPermissions.launchPermissionRequest()
268-
}
307+
if (isPnba) {
308+
IconButton(onClick = {
309+
if(readContactPermissions.status.isGranted) {
310+
launcher.launch(Unit)
311+
} else {
312+
readContactPermissions.launchPermissionRequest()
313+
}
269314

270-
}) {
271-
Icon(
272-
imageVector = Icons.Filled.Contacts,
273-
contentDescription = stringResource(R.string.select_contact),
274-
tint = MaterialTheme.colorScheme.primary,
275-
)
315+
}) {
316+
Icon(
317+
imageVector = Icons.Filled.Contacts,
318+
contentDescription = stringResource(R.string.select_contact),
319+
tint = MaterialTheme.colorScheme.primary,
320+
)
321+
}
276322
}
277323
}
278324

279325
Spacer(modifier = Modifier.height(4.dp))
280326

281-
// Dialing Code Hint
282327
Text(
283-
text = stringResource(R.string.always_add_the_dialing_code_if_absent_e_g_237),
328+
text = fieldInfo.hint,
284329
style = MaterialTheme.typography.bodySmall,
285330
color = MaterialTheme.colorScheme.onSurfaceVariant
286331
)
287332

333+
288334
Spacer(modifier = Modifier.height(16.dp))
289335

290336
// Message Body
@@ -312,29 +358,38 @@ fun verifyPhoneNumberFormat(phoneNumber: String): Boolean {
312358

313359

314360
private fun createMessageByteBuffer(
315-
from: String, to: String, message: String
361+
from: String, to: String, message: String,
362+
accessToken: String? = null, refreshToken: String? = null
316363
): ByteBuffer {
317-
// Define size constants
318364
val BYTE_SIZE_LIMIT = 255
319365
val SHORT_SIZE_LIMIT = 65535
320366

321367
// Convert strings to byte arrays
322368
val fromBytes = from.toByteArray(StandardCharsets.UTF_8)
323369
val toBytes = to.toByteArray(StandardCharsets.UTF_8)
324370
val bodyBytes = message.toByteArray(StandardCharsets.UTF_8)
371+
val accessTokenBytes = accessToken?.toByteArray(StandardCharsets.UTF_8)
372+
val refreshTokenBytes = refreshToken?.toByteArray(StandardCharsets.UTF_8)
373+
325374

326375
// Get sizes for validation
327376
val fromSize = fromBytes.size
328377
val toSize = toBytes.size
329378
val bodySize = bodyBytes.size
379+
val accessTokenSize = accessTokenBytes?.size ?: 0
380+
val refreshTokenSize = refreshTokenBytes?.size ?: 0
330381

331382
// Validate field sizes
332383
if (fromSize > BYTE_SIZE_LIMIT) throw IllegalArgumentException("From field exceeds maximum size of $BYTE_SIZE_LIMIT bytes")
333384
if (toSize > SHORT_SIZE_LIMIT) throw IllegalArgumentException("To field exceeds maximum size of $SHORT_SIZE_LIMIT bytes")
334385
if (bodySize > SHORT_SIZE_LIMIT) throw IllegalArgumentException("Body field exceeds maximum size of $SHORT_SIZE_LIMIT bytes")
386+
if (accessTokenSize > SHORT_SIZE_LIMIT) throw IllegalArgumentException("Access Token exceeds maximum size of $SHORT_SIZE_LIMIT bytes")
387+
if (refreshTokenSize > SHORT_SIZE_LIMIT) throw IllegalArgumentException("Refresh Token exceeds maximum size of $SHORT_SIZE_LIMIT bytes")
335388

389+
390+
// Update total size to include tokens
336391
val totalSize = 1 + 2 + 2 + 2 + 1 + 2 + 2 + 2 +
337-
fromSize + toSize + bodySize
392+
fromSize + toSize + bodySize + accessTokenSize + refreshTokenSize
338393

339394
// Allocate buffer and set byte order
340395
val buffer = ByteBuffer.allocate(totalSize).order(ByteOrder.LITTLE_ENDIAN)
@@ -346,13 +401,16 @@ private fun createMessageByteBuffer(
346401
buffer.putShort(0)
347402
buffer.put(0.toByte())
348403
buffer.putShort(bodySize.toShort())
349-
buffer.putShort(0)
350-
buffer.putShort(0)
404+
buffer.putShort(accessTokenSize.toShort())
405+
buffer.putShort(refreshTokenSize.toShort())
351406

352407
// Write field values
353408
buffer.put(fromBytes)
354409
buffer.put(toBytes)
355410
buffer.put(bodyBytes)
411+
accessTokenBytes?.let { buffer.put(it) }
412+
refreshTokenBytes?.let { buffer.put(it) }
413+
356414

357415
buffer.flip()
358416
return buffer
@@ -371,15 +429,22 @@ private fun processSend(
371429
val AD = Publishers.fetchPublisherPublicKey(context)
372430
?: return@launch onFailure("Could not fetch publisher key.")
373431

432+
val platform = Datastore.getDatastore(context).availablePlatformsDao().fetch(account.name!!)
433+
?: return@launch onFailure("Could not find platform details for '${account.name}'.")
434+
435+
436+
val accessToken = if (platform.protocol_type == "oauth2") account.accessToken else null
437+
val refreshToken = if (platform.protocol_type == "oauth2") account.refreshToken else null
438+
Log.d("MessageComposeView", "Access Token: $accessToken, Refresh Token: $refreshToken")
439+
374440
val contentFormatV2Bytes = createMessageByteBuffer(
375441
from = messageContent.from,
376442
to = messageContent.to,
377-
message = messageContent.message
443+
message = messageContent.message,
444+
accessToken = accessToken,
445+
refreshToken = refreshToken
378446
).array()
379447

380-
val platform = Datastore.getDatastore(context).availablePlatformsDao().fetch(account.name!!)
381-
?: return@launch onFailure("Could not find platform details for '${account.name}'.")
382-
383448
val languageCode = Locale.getDefault().language.take(2).lowercase()
384449
val validLanguageCode = if (languageCode.length == 2) languageCode else "en"
385450

@@ -407,45 +472,26 @@ private fun processSend(
407472
}
408473
}
409474

410-
fun getContactDetails(context: Context, contactUri: Uri): String {
411-
val contentResolver: ContentResolver = context.contentResolver
412-
val contactDetails = mutableMapOf<String, String?>()
475+
private fun getPhoneNumberFromUri(context: Context, uri: Uri): String {
476+
var phoneNumber: String? = null
477+
val projection: Array<String> = arrayOf(ContactsContract.CommonDataKinds.Phone.NUMBER)
413478

414479
try {
415-
val cursor: Cursor? = contentResolver.query(contactUri, null, null, null, null)
480+
val cursor: Cursor? = context.contentResolver.query(uri, projection, null, null, null)
416481
cursor?.use {
417482
if (it.moveToFirst()) {
418-
val id = it.getString(it.getColumnIndexOrThrow(ContactsContract.Contacts._ID))
419-
val name = it.getString(it.getColumnIndexOrThrow(ContactsContract.Contacts.DISPLAY_NAME))
420-
421-
contactDetails["id"] = id
422-
contactDetails["name"] = name
423-
424-
// Retrieve phone numbers
425-
val hasPhone = it.getInt(it.getColumnIndexOrThrow(ContactsContract.Contacts.HAS_PHONE_NUMBER))
426-
if (hasPhone > 0) {
427-
val phoneCursor = contentResolver.query(
428-
ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
429-
null,
430-
ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ?",
431-
arrayOf(id),
432-
null
433-
)
434-
phoneCursor?.use { phone ->
435-
if (phone.moveToFirst()) {
436-
return phone.getString(phone.getColumnIndexOrThrow(
437-
ContactsContract.CommonDataKinds.Phone.NUMBER))
438-
}
439-
}
483+
val numberIndex = it.getColumnIndex(ContactsContract.Contacts.CONTENT_URI.toString())
484+
if (numberIndex >= 0) {
485+
phoneNumber = it.getString(numberIndex)
440486
}
441-
442487
}
443488
}
444489
} catch (e: Exception) {
445490
e.printStackTrace()
491+
Log.e("getPhoneNumberFromUri", "Failed to get phone number from URI", e)
446492
}
447493

448-
return ""
494+
return phoneNumber ?: ""
449495
}
450496

451497

app/src/main/res/values-ar/strings.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,5 +317,9 @@
317317
<string name="message_">رسالة</string>
318318
<string name="message_content_could_not_be_displayed">تعذر عرض محتوى الرسالة</string>
319319
<string name="text_message">رسالة نصية</string>
320+
<string name="recipient_account">حساب المستلم</string>
321+
<string name="recipient_account_format_hint">تأكد من أن حساب المستلم بالتنسيق الصحيح.</string>
322+
<string name="slack_recipient">قناة أو مستخدم سلاك</string>
323+
<string name="slack_hint">للقنوات: استخدم #الاسم أو مُعرّف (مثال: #عام أو C123ABC). للرسائل الخاصة: استخدم @اسم_المستخدم، أو بريد إلكتروني، أو مُعرّف مستخدم (مثال: @jane أو @user@gmail.com أو U456DEF).</string>
320324

321325
</resources>

app/src/main/res/values-de/strings.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,5 +315,9 @@
315315
<string name="message_">Nachricht</string>
316316
<string name="message_content_could_not_be_displayed">Nachrichteninhalt konnte nicht angezeigt werden</string>
317317
<string name="text_message">Textnachricht</string>
318+
<string name="recipient_account">Empfängerkonto</string>
319+
<string name="recipient_account_format_hint">Stellen Sie sicher, dass das Empfängerkonto im richtigen Format ist.</string>
320+
<string name="slack_recipient">Slack-Kanal oder -Benutzer</string>
321+
<string name="slack_hint">Für Kanäle: Verwenden Sie #Name oder eine ID (z.B. #general oder C123ABC). Für DMs: Verwenden Sie @Benutzername, eine E-Mail-Adresse oder eine Benutzer-ID (z.B. @jane oder @user@gmail.com oder U456DEF).</string>
318322

319323
</resources>

app/src/main/res/values-es/strings.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,5 +316,9 @@
316316
<string name="message_">Mensaje</string>
317317
<string name="message_content_could_not_be_displayed">No se pudo mostrar el contenido del mensaje</string>
318318
<string name="text_message">Mensaje de texto</string>
319+
<string name="recipient_account">Cuenta del destinatario</string>
320+
<string name="recipient_account_format_hint">Asegúrese de que la cuenta del destinatario esté en el formato correcto.</string>
321+
<string name="slack_recipient">Canal o usuario de Slack</string>
322+
<string name="slack_hint">Para canales: use #nombre o una ID (ej., #general o C123ABC). Para MD: use @nombredeusuario, un correo electrónico o una ID de usuario (ej., @jane o @user@gmail.com o U456DEF).</string>
319323

320324
</resources>

app/src/main/res/values-fa/strings.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,5 +316,9 @@
316316
<string name="message_">پیام</string>
317317
<string name="message_content_could_not_be_displayed">محتوای پیام قابل نمایش نبود</string>
318318
<string name="text_message">پیام متنی</string>
319+
<string name="recipient_account">حساب گیرنده</string>
320+
<string name="recipient_account_format_hint">اطمینان حاصل کنید که فرمت حساب گیرنده صحیح باشد.</string>
321+
<string name="slack_recipient">کانال یا کاربر Slack</string>
322+
<string name="slack_hint">برای کانال‌ها: از #نام یا یک شناسه (مثلا #general یا C123ABC) استفاده کنید. برای پیام‌های مستقیم: از @نام‌کاربری، یک ایمیل یا شناسه کاربری (مثلا @jane یا @user@gmail.com یا U456DEF) استفاده کنید.</string>
319323

320324
</resources>

app/src/main/res/values-fr/strings.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,4 +316,8 @@
316316
<string name="message_">Message</string>
317317
<string name="message_content_could_not_be_displayed">Le contenu du message n\'a pas pu être affiché</string>
318318
<string name="text_message">Message texte</string>
319+
<string name="recipient_account">Compte du destinataire</string>
320+
<string name="recipient_account_format_hint">Assurez-vous que le compte du destinataire est au bon format.</string>
321+
<string name="slack_recipient">Canal ou utilisateur Slack</string>
322+
<string name="slack_hint">Pour les canaux : utilisez #nom ou un ID (par ex., #general ou C123ABC). Pour les MP : utilisez @nomutilisateur, une adresse e-mail ou un ID utilisateur (par ex., @jane ou @user@gmail.com ou U456DEF).</string>
319323
</resources>

app/src/main/res/values-hi/strings.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,5 +321,9 @@
321321
<string name="message_">संदेश</string>
322322
<string name="message_content_could_not_be_displayed">संदेश की सामग्री प्रदर्शित नहीं की जा सकी</string>
323323
<string name="text_message">पाठ संदेश</string>
324+
<string name="recipient_account">प्राप्तकर्ता का खाता</string>
325+
<string name="recipient_account_format_hint">सुनिश्चित करें कि प्राप्तकर्ता का खाता सही प्रारूप में है।</string>
326+
<string name="slack_recipient">स्लैक चैनल या उपयोगकर्ता</string>
327+
<string name="slack_hint">चैनलों के लिए: #नाम या एक आईडी का उपयोग करें (जैसे, #general या C123ABC)। डीएम के लिए: @उपयोगकर्ता नाम, एक ईमेल, या एक उपयोगकर्ता आईडी का उपयोग करें (जैसे, @jane या @user@gmail.com या U456DEF)।</string>
324328

325329
</resources>

app/src/main/res/values-ko/strings.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,5 +320,9 @@
320320
<string name="message_">메시지</string>
321321
<string name="message_content_could_not_be_displayed">메시지 내용을 표시할 수 없습니다</string>
322322
<string name="text_message">문자 메시지</string>
323+
<string name="recipient_account">수신자 계정</string>
324+
<string name="recipient_account_format_hint">수신자 계정이 올바른 형식인지 확인하십시오.</string>
325+
<string name="slack_recipient">Slack 채널 또는 사용자</string>
326+
<string name="slack_hint">채널: #이름 또는 ID(예: #general 또는 C123ABC)를 사용하세요. DM: @사용자이름, 이메일 또는 사용자 ID(예: @jane 또는 @user@gmail.com 또는 U456DEF)를 사용하세요.</string>
323327

324328
</resources>

0 commit comments

Comments
 (0)