Skip to content

Commit

Permalink
feat: set custom ringtones for contacts (closes #305)
Browse files Browse the repository at this point in the history
  • Loading branch information
SuhasDissa committed May 12, 2024
1 parent bbc969a commit 59aaad5
Show file tree
Hide file tree
Showing 6 changed files with 134 additions and 28 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.bnyro.contacts.domain.model

import android.graphics.Bitmap
import android.net.Uri
import com.bnyro.contacts.domain.enums.SortOrder

data class ContactData(
Expand All @@ -24,7 +25,8 @@ data class ContactData(
var events: List<ValueWithType> = listOf(),
var notes: List<ValueWithType> = listOf(),
var groups: List<ContactsGroup> = listOf(),
var websites: List<ValueWithType> = listOf()
var websites: List<ValueWithType> = listOf(),
var ringTone: Uri? = null
) {
val accountIdentifier get() = "$accountType|$accountName"
fun getNameBySortOrder(sortOrder: SortOrder): String? {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import android.annotation.SuppressLint
import android.content.ContentProviderOperation
import android.content.ContentResolver
import android.content.ContentUris
import android.content.ContentValues
import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
Expand Down Expand Up @@ -39,6 +40,7 @@ import com.bnyro.contacts.util.extension.stringValue
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext


class DeviceContactsRepository(private val context: Context) : ContactsRepository {
override val label: String = context.getString(R.string.device)

Expand Down Expand Up @@ -126,7 +128,11 @@ class DeviceContactsRepository(private val context: Context) : ContactsRepositor

ContactsHelper.contactAttributesTypes.forEach { attribute ->
if (attribute is StringAttribute) {
val dataStr = getEntry(contactId, attribute.androidContentType, attribute.androidValueColumn)
val dataStr = getEntry(
contactId,
attribute.androidContentType,
attribute.androidValueColumn
)
attribute.set(this, dataStr)
} else if (attribute is ListAttribute) {
val dataEntries = getExtras(
Expand Down Expand Up @@ -317,22 +323,28 @@ class DeviceContactsRepository(private val context: Context) : ContactsRepositor
it.rowId.toString()
)
}.toTypedArray(),
*ContactsHelper.contactAttributesTypes.filterIsInstance<StringAttribute>().map { attribute ->
attribute.get(contact)?.let {
getInsertAction(attribute.androidContentType, attribute.androidValueColumn, it)
}
}.toTypedArray(),
*ContactsHelper.contactAttributesTypes.filterIsInstance<ListAttribute>().map { attribute ->
attribute.get(contact).map {
getInsertAction(
attribute.androidContentType,
attribute.androidValueColumn,
it.value,
attribute.androidTypeColumn,
it.type
)
}
}.flatten().toTypedArray()
*ContactsHelper.contactAttributesTypes.filterIsInstance<StringAttribute>()
.map { attribute ->
attribute.get(contact)?.let {
getInsertAction(
attribute.androidContentType,
attribute.androidValueColumn,
it
)
}
}.toTypedArray(),
*ContactsHelper.contactAttributesTypes.filterIsInstance<ListAttribute>()
.map { attribute ->
attribute.get(contact).map {
getInsertAction(
attribute.androidContentType,
attribute.androidValueColumn,
it.value,
attribute.androidTypeColumn,
it.type
)
}
}.flatten().toTypedArray()
).let { ArrayList(it) }

contentResolver.applyBatch(AUTHORITY, ops)
Expand All @@ -357,15 +369,19 @@ class DeviceContactsRepository(private val context: Context) : ContactsRepositor

for (attribute in ContactsHelper.contactAttributesTypes) {
if (attribute is StringAttribute) {
operations.addAll(getUpdateSingleAction(
rawContactId, attribute.androidContentType,
attribute.androidValueColumn, attribute.get(contact)
))
operations.addAll(
getUpdateSingleAction(
rawContactId, attribute.androidContentType,
attribute.androidValueColumn, attribute.get(contact)
)
)
} else if (attribute is ListAttribute) {
operations.addAll(getUpdateMultipleAction(
rawContactId, attribute.androidContentType, attribute.get(contact),
attribute.androidValueColumn, attribute.androidTypeColumn
))
operations.addAll(
getUpdateMultipleAction(
rawContactId, attribute.androidContentType, attribute.get(contact),
attribute.androidValueColumn, attribute.androidTypeColumn
)
)
}
}

Expand Down Expand Up @@ -398,13 +414,19 @@ class DeviceContactsRepository(private val context: Context) : ContactsRepositor

fun getAccountTypes(): List<AccountType> {
val accounts = AccountManager.get(context).accounts.filter {
ContentResolver.getIsSyncable(it, authority) > 0 && ContentResolver.getSyncAutomatically(it, authority)
ContentResolver.getIsSyncable(
it,
authority
) > 0 && ContentResolver.getSyncAutomatically(it, authority)
}

return listOf(AccountType.androidDefault) + accounts.map { AccountType(it.name, it.type) }
}

private fun getCreateAction(accountType: String, accountName: String): ContentProviderOperation {
private fun getCreateAction(
accountType: String,
accountName: String
): ContentProviderOperation {
return ContentProviderOperation.newInsert(RawContacts.CONTENT_URI)
.withValue(RawContacts.ACCOUNT_TYPE, accountType)
.withValue(RawContacts.ACCOUNT_NAME, accountName)
Expand Down Expand Up @@ -530,6 +552,17 @@ class DeviceContactsRepository(private val context: Context) : ContactsRepositor
}.build()
}

suspend fun updateContactRingTone(contactId: String, ringtoneUri: Uri) =
withContext(Dispatchers.IO) {
val contactUri = Uri.withAppendedPath(Contacts.CONTENT_URI, contactId)

val values = ContentValues().apply {
put(Contacts.CUSTOM_RINGTONE, ringtoneUri.toString())
}

contentResolver.update(contactUri, values, null, null)
}

companion object {
const val MAX_PHOTO_SIZE = 700f
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.bnyro.contacts.presentation.screens.contact

import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.launch
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
Expand All @@ -23,6 +25,9 @@ import androidx.compose.material.icons.filled.Edit
import androidx.compose.material.icons.filled.Message
import androidx.compose.material.icons.filled.Share
import androidx.compose.material.icons.filled.Shortcut
import androidx.compose.material.icons.rounded.MoreVert
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
Expand Down Expand Up @@ -65,6 +70,7 @@ import com.bnyro.contacts.presentation.screens.editor.components.ContactEntryGro
import com.bnyro.contacts.presentation.screens.editor.components.ContactEntryTextGroup
import com.bnyro.contacts.util.ContactsHelper
import com.bnyro.contacts.util.IntentHelper
import com.bnyro.contacts.util.RingtonePickContract

@OptIn(ExperimentalMaterial3Api::class)
@Composable
Expand All @@ -86,6 +92,13 @@ fun SingleContactScreen(contact: ContactData, viewModel: ContactsModel, onClose:
mutableStateOf(false)
}

val ringtonePicker =
rememberLauncherForActivityResult(contract = RingtonePickContract()) { uri ->
if (uri != null) {
viewModel.updateContactRingTone(contact, uri)
}
}

FullScreenDialog(onClose = onClose) {
Scaffold(
topBar = {
Expand Down Expand Up @@ -118,6 +131,30 @@ fun SingleContactScreen(contact: ContactData, viewModel: ContactsModel, onClose:
) {
showDelete = true
}
Box {
var showMore by remember { mutableStateOf(false) }
ClickableIcon(
icon = Icons.Rounded.MoreVert,
contentDescription = R.string.more
) {
showMore = !showMore
}
DropdownMenu(
expanded = showMore,
onDismissRequest = {
showMore = false
}
) {
DropdownMenuItem(
text = {
Text(text = stringResource(R.string.change_ringtone))
},
onClick = {
ringtonePicker.launch()
}
)
}
}
}
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,12 @@ class ContactsModel(
}
}

fun updateContactRingTone(contact: ContactData, uri: Uri) {
viewModelScope.launch {
deviceContactsRepository.updateContactRingTone(contact.contactId.toString(), uri)
}
}

companion object {
val Factory = viewModelFactory {
initializer {
Expand Down
26 changes: 26 additions & 0 deletions app/src/main/java/com/bnyro/contacts/util/RingtonePickContract.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.bnyro.contacts.util

import android.app.Activity
import android.content.Context
import android.content.Intent
import android.media.RingtoneManager
import android.net.Uri
import androidx.activity.result.contract.ActivityResultContract
import com.bnyro.contacts.R

class RingtonePickContract : ActivityResultContract<Void?, Uri?>() {
override fun createIntent(context: Context, input: Void?): Intent {
return Intent(RingtoneManager.ACTION_RINGTONE_PICKER).apply {
putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, RingtoneManager.TYPE_RINGTONE)
putExtra(
RingtoneManager.EXTRA_RINGTONE_TITLE,
context.getString(R.string.select_custom_ringtone)
)
}
}

override fun parseResult(resultCode: Int, intent: Intent?): Uri? {
return intent.takeIf { resultCode == Activity.RESULT_OK }
?.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI)
}
}
2 changes: 2 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -149,4 +149,6 @@
<string name="dialing">Dialing...</string>
<string name="call_from">Call From</string>
<string name="key_pad">Keypad</string>
<string name="select_custom_ringtone">Select custom ringtone</string>
<string name="change_ringtone">Change ringtone</string>
</resources>

0 comments on commit 59aaad5

Please sign in to comment.