From 09e40ee6b91845a46d744eeed398a1c4308b9627 Mon Sep 17 00:00:00 2001 From: Bnyro Date: Sun, 19 Nov 2023 14:48:08 +0100 Subject: [PATCH 001/159] feat: inbuilt dialer including call history --- app/build.gradle.kts | 2 +- app/src/main/AndroidManifest.xml | 48 +++++ .../java/com/bnyro/contacts/ext/String.kt | 6 + .../com/bnyro/contacts/obj/CallLogEntry.kt | 8 + .../bnyro/contacts/services/CallService.kt | 30 ++++ .../contacts/ui/activities/CallActivity.kt | 38 ++++ .../contacts/ui/activities/MainActivity.kt | 15 +- .../ui/components/ContactIconPlaceholder.kt | 50 ++++++ .../contacts/ui/components/ContactItem.kt | 78 ++++---- .../contacts/ui/components/NumberInput.kt | 79 ++++++++ .../bnyro/contacts/ui/models/DialerModel.kt | 29 +++ .../contacts/ui/screens/CallLogsScreen.kt | 169 ++++++++++++++++++ .../bnyro/contacts/ui/screens/DialerScreen.kt | 122 +++++++++++++ .../contacts/ui/screens/MainAppContent.kt | 21 ++- .../contacts/ui/screens/SettingsScreen.kt | 2 +- .../com/bnyro/contacts/util/CallLogHelper.kt | 44 +++++ .../com/bnyro/contacts/util/CallManager.kt | 49 +++++ app/src/main/res/values/strings.xml | 6 + 18 files changed, 740 insertions(+), 56 deletions(-) create mode 100644 app/src/main/java/com/bnyro/contacts/ext/String.kt create mode 100644 app/src/main/java/com/bnyro/contacts/obj/CallLogEntry.kt create mode 100644 app/src/main/java/com/bnyro/contacts/services/CallService.kt create mode 100644 app/src/main/java/com/bnyro/contacts/ui/activities/CallActivity.kt create mode 100644 app/src/main/java/com/bnyro/contacts/ui/components/ContactIconPlaceholder.kt create mode 100644 app/src/main/java/com/bnyro/contacts/ui/components/NumberInput.kt create mode 100644 app/src/main/java/com/bnyro/contacts/ui/models/DialerModel.kt create mode 100644 app/src/main/java/com/bnyro/contacts/ui/screens/CallLogsScreen.kt create mode 100644 app/src/main/java/com/bnyro/contacts/ui/screens/DialerScreen.kt create mode 100644 app/src/main/java/com/bnyro/contacts/util/CallLogHelper.kt create mode 100644 app/src/main/java/com/bnyro/contacts/util/CallManager.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 7e7bc151..6b226add 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -10,7 +10,7 @@ android { defaultConfig { applicationId = "com.bnyro.contacts" - minSdk = 21 + minSdk = 23 targetSdk = 33 versionCode = 27 versionName = "8.1" diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 4feee5fb..683e0ba0 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -22,6 +22,18 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/bnyro/contacts/ext/String.kt b/app/src/main/java/com/bnyro/contacts/ext/String.kt new file mode 100644 index 00000000..d1554297 --- /dev/null +++ b/app/src/main/java/com/bnyro/contacts/ext/String.kt @@ -0,0 +1,6 @@ +package com.bnyro.contacts.ext + +fun String.removeLastChar(): String { + return if (isEmpty()) this + else substring(0, length - 1) +} diff --git a/app/src/main/java/com/bnyro/contacts/obj/CallLogEntry.kt b/app/src/main/java/com/bnyro/contacts/obj/CallLogEntry.kt new file mode 100644 index 00000000..8c2b5a5a --- /dev/null +++ b/app/src/main/java/com/bnyro/contacts/obj/CallLogEntry.kt @@ -0,0 +1,8 @@ +package com.bnyro.contacts.obj + +data class CallLogEntry( + val phoneNumber: String, + val type: Int, + val time: Long, + val duration: Long +) diff --git a/app/src/main/java/com/bnyro/contacts/services/CallService.kt b/app/src/main/java/com/bnyro/contacts/services/CallService.kt new file mode 100644 index 00000000..4bcf137b --- /dev/null +++ b/app/src/main/java/com/bnyro/contacts/services/CallService.kt @@ -0,0 +1,30 @@ +package com.bnyro.contacts.services + +import android.content.Intent +import android.telecom.Call +import android.telecom.InCallService +import com.bnyro.contacts.ui.activities.CallActivity +import com.bnyro.contacts.util.CallManager + +class CallService : InCallService() { + private val callCallback: Call.Callback = object : Call.Callback() { + override fun onStateChanged(call: Call, state: Int) { + CallManager.updateCallState(state) + } + } + + override fun onCallAdded(call: Call) { + super.onCallAdded(call) + call.registerCallback(callCallback) + val intent = Intent(applicationContext, CallActivity::class.java) + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK) + startActivity(intent) + CallManager.setCall(call) + } + + override fun onCallRemoved(call: Call) { + super.onCallRemoved(call) + call.unregisterCallback(callCallback) + CallManager.setCall(null) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/bnyro/contacts/ui/activities/CallActivity.kt b/app/src/main/java/com/bnyro/contacts/ui/activities/CallActivity.kt new file mode 100644 index 00000000..a90d7e8d --- /dev/null +++ b/app/src/main/java/com/bnyro/contacts/ui/activities/CallActivity.kt @@ -0,0 +1,38 @@ +package com.bnyro.contacts.ui.activities + +import android.os.Bundle +import androidx.activity.compose.setContent +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Scaffold +import androidx.compose.ui.Modifier +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.get +import com.bnyro.contacts.ui.models.DialerModel +import com.bnyro.contacts.ui.screens.DialerScreen +import com.bnyro.contacts.ui.theme.ConnectYouTheme + +class CallActivity: BaseActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + val dialerModel: DialerModel = ViewModelProvider(this).get() + + setContent { + ConnectYouTheme { + Scaffold { pV -> + Box( + modifier = Modifier + .fillMaxSize() + .padding(pV) + ) { + DialerScreen(contactsModel = contactsModel, dialerModel = dialerModel) { + // TODO: finish() + } + } + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/bnyro/contacts/ui/activities/MainActivity.kt b/app/src/main/java/com/bnyro/contacts/ui/activities/MainActivity.kt index 393f2f27..b22d0cb3 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/activities/MainActivity.kt +++ b/app/src/main/java/com/bnyro/contacts/ui/activities/MainActivity.kt @@ -14,6 +14,7 @@ import com.bnyro.contacts.obj.ContactData import com.bnyro.contacts.obj.ValueWithType import com.bnyro.contacts.ui.components.dialogs.AddToContactDialog import com.bnyro.contacts.ui.models.ContactsModel +import com.bnyro.contacts.ui.models.DialerModel import com.bnyro.contacts.ui.models.SmsModel import com.bnyro.contacts.ui.screens.MainAppContent import com.bnyro.contacts.ui.theme.ConnectYouTheme @@ -37,9 +38,12 @@ class MainActivity : BaseActivity() { smsModel = ViewModelProvider(this).get() smsModel?.initialAddressAndBody = getInitialSmsAddressAndBody() + dialerModel = ViewModelProvider(this).get() + dialerModel?.initialPhoneNumber = getInitialNumberToDial() + setContent { ConnectYouTheme(themeModel.themeMode) { - MainAppContent(smsModel!!) + MainAppContent(smsModel!!, dialerModel!!) getInsertOrEditNumber()?.let { AddToContactDialog(it) } @@ -79,6 +83,7 @@ class MainActivity : BaseActivity() { ) ) } + intent?.getStringExtra("action") == "create" -> ContactData() else -> null } @@ -112,6 +117,13 @@ class MainActivity : BaseActivity() { return address.replace(ContactsModel.normalizeNumberRegex, "") to body } + private fun getInitialNumberToDial(): String? { + if (intent?.action != Intent.ACTION_DIAL) return null + + return intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER) + .takeIf { !it.isNullOrBlank() } + } + private fun handleVcfShareAction(contactsModel: ContactsModel) { if (intent?.type !in BackupHelper.vCardMimeTypes) return val uri = when (intent.action) { @@ -133,5 +145,6 @@ class MainActivity : BaseActivity() { companion object { var smsModel: SmsModel? = null + var dialerModel: DialerModel? = null } } diff --git a/app/src/main/java/com/bnyro/contacts/ui/components/ContactIconPlaceholder.kt b/app/src/main/java/com/bnyro/contacts/ui/components/ContactIconPlaceholder.kt new file mode 100644 index 00000000..accd6f11 --- /dev/null +++ b/app/src/main/java/com/bnyro/contacts/ui/components/ContactIconPlaceholder.kt @@ -0,0 +1,50 @@ +package com.bnyro.contacts.ui.components + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import com.bnyro.contacts.ext.contentColor +import com.bnyro.contacts.ui.models.ThemeModel +import com.bnyro.contacts.util.ColorUtils + +@Composable +fun ContactIconPlaceholder( + themeModel: ThemeModel, + firstChar: Char? +) { + val backgroundColor = if (themeModel.colorfulIcons) { + remember { Color(ColorUtils.getRandomColor()) } + } else { + MaterialTheme.colorScheme.primary + } + val contentColor = when { + !themeModel.colorfulIcons -> MaterialTheme.colorScheme.onPrimary + else -> backgroundColor.contentColor() + } + + Box( + modifier = Modifier + .size(42.dp) + .background( + shape = CircleShape, + color = backgroundColor + ) + ) { + Text( + modifier = Modifier.align(Alignment.Center), + text = firstChar?.toString() ?: "?", + color = contentColor, + fontWeight = FontWeight.Bold + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/bnyro/contacts/ui/components/ContactItem.kt b/app/src/main/java/com/bnyro/contacts/ui/components/ContactItem.kt index d8538190..9a275692 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/components/ContactItem.kt +++ b/app/src/main/java/com/bnyro/contacts/ui/components/ContactItem.kt @@ -28,19 +28,15 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.asImageBitmap import androidx.compose.ui.layout.ContentScale -import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel import com.bnyro.contacts.enums.SortOrder -import com.bnyro.contacts.ext.contentColor import com.bnyro.contacts.obj.ContactData import com.bnyro.contacts.ui.models.ContactsModel import com.bnyro.contacts.ui.models.ThemeModel import com.bnyro.contacts.ui.screens.SingleContactScreen -import com.bnyro.contacts.util.ColorUtils import kotlinx.coroutines.runBlocking @OptIn(ExperimentalFoundationApi::class) @@ -84,49 +80,39 @@ fun ContactItem( .fillMaxWidth(), verticalAlignment = Alignment.CenterVertically ) { - val backgroundColor = if (themeModel.colorfulIcons) { - remember { Color(ColorUtils.getRandomColor()) } - } else { - MaterialTheme.colorScheme.primary - } - val contentColor = when { - !themeModel.colorfulIcons -> MaterialTheme.colorScheme.onPrimary - else -> backgroundColor.contentColor() - } - - Box( - modifier = Modifier - .size(42.dp) - .background( - shape = CircleShape, - color = backgroundColor - ) - ) { - val thumbnail = contact.thumbnail ?: contact.photo - if (selected) { - Icon( - modifier = Modifier.align(Alignment.Center), - imageVector = Icons.Default.Check, - contentDescription = null, - tint = contentColor - ) - } else if (thumbnail == null) { - Text( - modifier = Modifier.align(Alignment.Center), - text = (contactName.firstOrNull() ?: "").toString(), - color = contentColor, - fontWeight = FontWeight.Bold - ) - } else { - Image( - modifier = Modifier - .fillMaxSize() - .clip(CircleShape), - bitmap = thumbnail.asImageBitmap(), - contentDescription = null, - contentScale = ContentScale.Crop - ) + val thumbnail = contact.thumbnail ?: contact.photo + if (selected || thumbnail != null) { + Box( + modifier = Modifier + .size(42.dp) + .background( + shape = CircleShape, + color = MaterialTheme.colorScheme.primary + ) + ) { + if (selected) { + Icon( + modifier = Modifier.align(Alignment.Center), + imageVector = Icons.Default.Check, + contentDescription = null, + tint = MaterialTheme.colorScheme.onPrimary + ) + } else if (thumbnail != null) { + Image( + modifier = Modifier + .fillMaxSize() + .clip(CircleShape), + bitmap = thumbnail.asImageBitmap(), + contentDescription = null, + contentScale = ContentScale.Crop + ) + } } + } else { + ContactIconPlaceholder( + themeModel = themeModel, + firstChar = contactName.firstOrNull(), + ) } Spacer(modifier = Modifier.width(20.dp)) Text(contactName) diff --git a/app/src/main/java/com/bnyro/contacts/ui/components/NumberInput.kt b/app/src/main/java/com/bnyro/contacts/ui/components/NumberInput.kt new file mode 100644 index 00000000..40802bed --- /dev/null +++ b/app/src/main/java/com/bnyro/contacts/ui/components/NumberInput.kt @@ -0,0 +1,79 @@ +package com.bnyro.contacts.ui.components + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.grid.GridCells +import androidx.compose.foundation.lazy.grid.LazyVerticalGrid +import androidx.compose.foundation.lazy.grid.items +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Backspace +import androidx.compose.material3.FloatingActionButton +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.bnyro.contacts.ext.removeLastChar + +val phoneNumberInputs = listOf("1", "2", "3", "4", "5", "6", "7", "8", "9", "*", "0", "#") + +@Composable +fun NumberInput( + initialNumber: String, + onNumberChange: (String) -> Unit +) { + LazyVerticalGrid( + modifier = Modifier.fillMaxWidth(), + columns = GridCells.Fixed(3), + horizontalArrangement = Arrangement.Center + ) { + items(phoneNumberInputs) { + Box( + modifier = Modifier.fillMaxSize(), + contentAlignment = Alignment.Center + ) { + val interactionSource = remember { + MutableInteractionSource() + } + Text( + modifier = Modifier + .clip(CircleShape) + .clickable(interactionSource, indication = null) { + onNumberChange(initialNumber + it) + } + .padding(20.dp), + text = it, + fontSize = 20.sp, + fontWeight = FontWeight.Bold + ) + } + } + item {} + item {} + item { + Box( + modifier = Modifier + .fillMaxSize() + .padding(top = 20.dp), + contentAlignment = Alignment.Center + ) { + FloatingActionButton( + onClick = { onNumberChange(initialNumber.removeLastChar()) } + ) { + Icon(Icons.Default.Backspace, null) + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/bnyro/contacts/ui/models/DialerModel.kt b/app/src/main/java/com/bnyro/contacts/ui/models/DialerModel.kt new file mode 100644 index 00000000..0c58a54b --- /dev/null +++ b/app/src/main/java/com/bnyro/contacts/ui/models/DialerModel.kt @@ -0,0 +1,29 @@ +package com.bnyro.contacts.ui.models + +import android.Manifest +import android.content.Context +import android.content.Intent +import android.telecom.TelecomManager +import androidx.lifecycle.ViewModel + +class DialerModel: ViewModel() { + var initialPhoneNumber: String? = null + + fun requestDefaultDialerApp(context: Context) { + val telecomManager = context.getSystemService(Context.TELECOM_SERVICE) as TelecomManager? + val isAlreadyDefaultDialer = + context.packageName.equals(telecomManager!!.defaultDialerPackage) + if (!isAlreadyDefaultDialer) { + val intent: Intent = Intent(TelecomManager.ACTION_CHANGE_DEFAULT_DIALER) + .putExtra( + TelecomManager.EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME, + context.packageName + ) + context.startActivity(intent) + } + } + + companion object { + val phonePerms = arrayOf(Manifest.permission.CALL_PHONE) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/bnyro/contacts/ui/screens/CallLogsScreen.kt b/app/src/main/java/com/bnyro/contacts/ui/screens/CallLogsScreen.kt new file mode 100644 index 00000000..5841611c --- /dev/null +++ b/app/src/main/java/com/bnyro/contacts/ui/screens/CallLogsScreen.kt @@ -0,0 +1,169 @@ +package com.bnyro.contacts.ui.screens + +import android.content.Intent +import android.net.Uri +import android.text.format.DateUtils +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Call +import androidx.compose.material.icons.filled.Dialpad +import androidx.compose.material3.ElevatedCard +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.ExtendedFloatingActionButton +import androidx.compose.material3.FloatingActionButton +import androidx.compose.material3.Icon +import androidx.compose.material3.ModalBottomSheet +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.bnyro.contacts.R +import com.bnyro.contacts.obj.CallLogEntry +import com.bnyro.contacts.ui.components.ContactIconPlaceholder +import com.bnyro.contacts.ui.components.NothingHere +import com.bnyro.contacts.ui.components.NumberInput +import com.bnyro.contacts.ui.components.dialogs.ConfirmationDialog +import com.bnyro.contacts.ui.models.ContactsModel +import com.bnyro.contacts.ui.models.DialerModel +import com.bnyro.contacts.ui.models.ThemeModel +import com.bnyro.contacts.util.CallLogHelper +import com.bnyro.contacts.util.PermissionHelper + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun CallLogsScreen( + contactsModel: ContactsModel, + dialerModel: DialerModel, + themeModel: ThemeModel +) { + val context = LocalContext.current + + var showNumberPicker by remember { + mutableStateOf(false) + } + var numberToCall by remember { + mutableStateOf(dialerModel.initialPhoneNumber.orEmpty()) + } + var callLog by remember { + mutableStateOf(emptyList()) + } + + fun callNumber(number: String) { + if (!PermissionHelper.checkPermissions(context, DialerModel.phonePerms)) return + + val intent = Intent(Intent.ACTION_CALL, Uri.parse("tel:$number")) + context.startActivity(intent) + } + + LaunchedEffect(Unit) { + callLog = CallLogHelper.getCallLog(context) + } + + Scaffold( + floatingActionButton = { + FloatingActionButton( + onClick = { showNumberPicker = true } + ) { + Icon(Icons.Default.Dialpad, contentDescription = null) + } + } + ) { pV -> + if (callLog.isNotEmpty()) { + LazyColumn( + modifier = Modifier.padding(pV) + ) { + items(callLog) { + val contact = remember { + contactsModel.getContactByNumber(it.phoneNumber) + } + var showCallDialog by remember { + mutableStateOf(false) + } + + ElevatedCard ( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 10.dp, vertical = 5.dp) + .clickable { if (it.phoneNumber.isNotBlank()) showCallDialog = true }, + ) { + Row( + modifier = Modifier.padding(vertical = 10.dp, horizontal = 16.dp), + verticalAlignment = Alignment.CenterVertically + ) { + ContactIconPlaceholder( + themeModel = themeModel, + firstChar = contact?.displayName?.firstOrNull() + ) + + Spacer(modifier = Modifier.width(20.dp)) + + Column { + Text(text = contact?.displayName ?: it.phoneNumber, maxLines = 1) + Text(text = DateUtils.getRelativeTimeSpanString(it.time)?.toString().orEmpty()) + } + } + } + + if (showCallDialog) { + ConfirmationDialog( + onDismissRequest = { showCallDialog = false }, + title = stringResource(id = R.string.dial), + text = stringResource(id = R.string.confirm_start_call, contact?.displayName ?: it.phoneNumber) + ) { + callNumber(it.phoneNumber) + } + } + } + } + } else { + NothingHere() + } + } + + if (showNumberPicker) { + ModalBottomSheet(onDismissRequest = { showNumberPicker = false }) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 30.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text(text = numberToCall, fontSize = 20.sp) + NumberInput(initialNumber = numberToCall, onNumberChange = { numberToCall = it }) + ExtendedFloatingActionButton( + shape = CircleShape, + onClick = { + callNumber(numberToCall) + } + ) { + Icon(Icons.Default.Call, contentDescription = stringResource(R.string.dial)) + } + Spacer(modifier = Modifier.height(30.dp)) + } + } + } + + LaunchedEffect(Unit) { + dialerModel.requestDefaultDialerApp(context) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/bnyro/contacts/ui/screens/DialerScreen.kt b/app/src/main/java/com/bnyro/contacts/ui/screens/DialerScreen.kt new file mode 100644 index 00000000..131f61c9 --- /dev/null +++ b/app/src/main/java/com/bnyro/contacts/ui/screens/DialerScreen.kt @@ -0,0 +1,122 @@ +package com.bnyro.contacts.ui.screens + +import android.telecom.Call +import android.text.format.DateUtils +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.width +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Call +import androidx.compose.material.icons.filled.CallEnd +import androidx.compose.material3.ExtendedFloatingActionButton +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.mutableLongStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.bnyro.contacts.R +import com.bnyro.contacts.ui.models.ContactsModel +import com.bnyro.contacts.ui.models.DialerModel +import com.bnyro.contacts.util.CallManager +import java.util.Timer +import kotlin.concurrent.scheduleAtFixedRate + +@Composable +fun DialerScreen( + contactsModel: ContactsModel, + dialerModel: DialerModel, + onClose: () -> Unit +) { + val contactInfo = remember { + contactsModel.getContactByNumber(CallManager.callerDisplayNumber) + } + var callState by remember { + mutableIntStateOf(CallManager.currentCallState) + } + var elapsedTime by remember { + mutableLongStateOf(0L) + } + val timer = Timer() + + DisposableEffect(Unit) { + val listener: (Int) -> Unit = { + callState = it + + if (callState == Call.STATE_DISCONNECTED) onClose.invoke() + + if (callState == Call.STATE_ACTIVE) { + timer.cancel() + timer.scheduleAtFixedRate(1000, 1000) { + elapsedTime++ + } + } + } + + CallManager.onStateChangedListeners.add(listener) + + onDispose { + CallManager.onStateChangedListeners.remove(listener) + } + } + + Column( + modifier = Modifier.fillMaxSize(), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Spacer(modifier = Modifier.height(50.dp)) + Text( + text = when (callState) { + Call.STATE_RINGING, Call.STATE_DIALING, Call.STATE_PULLING_CALL -> stringResource(R.string.ringing) + Call.STATE_DISCONNECTING -> stringResource(R.string.disconnecting) + Call.STATE_ACTIVE -> stringResource(R.string.in_progress) + Call.STATE_CONNECTING -> stringResource(R.string.connecting) + else -> "" + }, + color = MaterialTheme.colorScheme.primary, + fontSize = 16.sp + ) + Spacer(modifier = Modifier.height(10.dp)) + Text( + text = contactInfo?.displayName ?: CallManager.callerDisplayNumber, + fontSize = 24.sp + ) + Spacer(modifier = Modifier.height(5.dp)) + Text(text = DateUtils.formatElapsedTime(elapsedTime)) + Spacer(modifier = Modifier.weight(1f)) + Row { + if (callState == Call.STATE_RINGING) { + ExtendedFloatingActionButton( + onClick = { CallManager.acceptCall() }, + containerColor = MaterialTheme.colorScheme.primary, + contentColor = MaterialTheme.colorScheme.onPrimary + ) { + Icon(Icons.Default.Call, contentDescription = null) + } + + Spacer(modifier = Modifier.width(20.dp)) + } + + ExtendedFloatingActionButton( + onClick = { CallManager.cancelCall() }, + containerColor = MaterialTheme.colorScheme.error, + contentColor = MaterialTheme.colorScheme.onError + ) { + Icon(Icons.Default.CallEnd, contentDescription = null) + } + } + Spacer(modifier = Modifier.height(50.dp)) + } +} diff --git a/app/src/main/java/com/bnyro/contacts/ui/screens/MainAppContent.kt b/app/src/main/java/com/bnyro/contacts/ui/screens/MainAppContent.kt index 94883bde..f28a4143 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/screens/MainAppContent.kt +++ b/app/src/main/java/com/bnyro/contacts/ui/screens/MainAppContent.kt @@ -8,6 +8,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons import androidx.compose.material.icons.rounded.Message import androidx.compose.material.icons.rounded.People +import androidx.compose.material.icons.rounded.Phone import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.NavigationBar @@ -34,13 +35,14 @@ import com.bnyro.contacts.R import com.bnyro.contacts.obj.NavBarItem import com.bnyro.contacts.ui.components.ContactsPage import com.bnyro.contacts.ui.models.ContactsModel +import com.bnyro.contacts.ui.models.DialerModel import com.bnyro.contacts.ui.models.SmsModel import com.bnyro.contacts.ui.models.ThemeModel import com.bnyro.contacts.util.Preferences @SuppressLint("UnusedMaterial3ScaffoldPaddingParameter") @Composable -fun MainAppContent(smsModel: SmsModel) { +fun MainAppContent(smsModel: SmsModel, dialerModel: DialerModel) { val themeModel: ThemeModel = viewModel() val contactsModel: ContactsModel = viewModel(factory = ContactsModel.Factory) @@ -59,6 +61,10 @@ fun MainAppContent(smsModel: SmsModel) { } val navItems = listOf( + NavBarItem( + stringResource(R.string.dial), + Icons.Rounded.Phone + ), NavBarItem( stringResource(R.string.contacts), Icons.Rounded.People @@ -71,10 +77,9 @@ fun MainAppContent(smsModel: SmsModel) { var currentPage by remember { mutableIntStateOf( - smsModel.initialAddressAndBody?.let { 1 } ?: Preferences.getInt( - Preferences.homeTabKey, - 0 - ) + dialerModel.initialPhoneNumber?.let { 0 } + ?: smsModel.initialAddressAndBody?.let { 2 } + ?: Preferences.getInt(Preferences.homeTabKey, 1) ) } @@ -119,11 +124,13 @@ fun MainAppContent(smsModel: SmsModel) { ) { Crossfade(targetState = currentPage, label = "crossfade pager") { index -> when (index) { - 0 -> ContactsPage( + 0 -> CallLogsScreen(contactsModel, dialerModel, themeModel) + + 1 -> ContactsPage( nestedScrollConnection.takeIf { themeModel.collapsableBottomBar } ) - 1 -> SmsListScreen(smsModel, contactsModel) + 2 -> SmsListScreen(smsModel, contactsModel) } } } diff --git a/app/src/main/java/com/bnyro/contacts/ui/screens/SettingsScreen.kt b/app/src/main/java/com/bnyro/contacts/ui/screens/SettingsScreen.kt index c5cffc3d..d3a85c25 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/screens/SettingsScreen.kt +++ b/app/src/main/java/com/bnyro/contacts/ui/screens/SettingsScreen.kt @@ -110,7 +110,7 @@ fun SettingsScreen(onDismissRequest: () -> Unit) { Text(stringResource(R.string.start_tab)) BlockPreference( preferenceKey = Preferences.homeTabKey, - entries = listOf(R.string.contacts, R.string.messages).map { + entries = listOf(R.string.dial, R.string.contacts, R.string.messages).map { stringResource(it) } ) diff --git a/app/src/main/java/com/bnyro/contacts/util/CallLogHelper.kt b/app/src/main/java/com/bnyro/contacts/util/CallLogHelper.kt new file mode 100644 index 00000000..c588ea5f --- /dev/null +++ b/app/src/main/java/com/bnyro/contacts/util/CallLogHelper.kt @@ -0,0 +1,44 @@ +package com.bnyro.contacts.util + +import android.Manifest +import android.content.Context +import android.provider.CallLog +import com.bnyro.contacts.ext.intValue +import com.bnyro.contacts.ext.longValue +import com.bnyro.contacts.ext.stringValue +import com.bnyro.contacts.obj.CallLogEntry +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext + +object CallLogHelper { + suspend fun getCallLog(context: Context): List = withContext(Dispatchers.IO) { + if (!PermissionHelper.checkPermissions(context, arrayOf(Manifest.permission.READ_CALL_LOG))) return@withContext emptyList() + + val callLog = mutableListOf() + + context.contentResolver.query( + CallLog.Calls.CONTENT_URI, + null, null, null, CallLog.Calls.DATE + " DESC" + )?.use { cursor -> + while (cursor.moveToNext()) { + val phoneNumber = cursor.stringValue(CallLog.Calls.NUMBER) + val callType = cursor.intValue(CallLog.Calls.TYPE)!! + val callDate = cursor.longValue(CallLog.Calls.DATE)!! + val callDuration = cursor.longValue(CallLog.Calls.DURATION)!! + callLog.add( + CallLogEntry(phoneNumber.orEmpty(), callType, callDate, callDuration) + ) + } + } + + return@withContext callLog + } + + suspend fun deleteAll(context: Context, callLog: List) = withContext(Dispatchers.IO) { + if (!PermissionHelper.checkPermissions(context, arrayOf(Manifest.permission.WRITE_CALL_LOG))) return@withContext + + callLog.distinctBy { it.phoneNumber }.forEach { entry -> + context.contentResolver.delete(CallLog.Calls.CONTENT_URI, "NUMBER=${entry.phoneNumber}", null) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/bnyro/contacts/util/CallManager.kt b/app/src/main/java/com/bnyro/contacts/util/CallManager.kt new file mode 100644 index 00000000..20f34365 --- /dev/null +++ b/app/src/main/java/com/bnyro/contacts/util/CallManager.kt @@ -0,0 +1,49 @@ +package com.bnyro.contacts.util + +import android.telecom.Call + +object CallManager { + private var currentCall: Call? = null + + var callerDisplayNumber = "" + var currentCallState: Int = Call.STATE_RINGING + var onStateChangedListeners: MutableList<(Int) -> Unit> = mutableListOf() + + fun setCall(call: Call?) { + currentCall = call + + if (call == null) return + + callerDisplayNumber = call.details.gatewayInfo?.originalAddress?.schemeSpecificPart + ?: call.details.handle.schemeSpecificPart + } + + fun updateCallState(state: Int) { + this.currentCallState = state + onStateChangedListeners.forEach { + it.invoke(state) + } + } + + fun cancelCall() { + if (currentCall == null) return + + if (currentCallState == Call.STATE_RINGING) { + rejectCall() + } else { + disconnectCall() + } + } + + fun acceptCall() { + currentCall?.let { it.answer(it.details.videoState) } + } + + private fun rejectCall() { + currentCall?.reject(false, "") + } + + private fun disconnectCall() { + currentCall?.disconnect() + } +} \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3f7be6e5..ff224c5a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -88,6 +88,12 @@ The SMS will only be stored within the app and not be accessible in other apps. Delete thread Delete message + + In progress + Ringing… + Disconnecting… + Connecting… + Do you want to call %1$s? Import vCard Export vCard From 9ff808813f97ee6b9c780ff9e572a36cce6439ff Mon Sep 17 00:00:00 2001 From: Suhas Dissanayake Date: Fri, 1 Dec 2023 12:22:05 +0530 Subject: [PATCH 002/159] style: improve dialer design --- .../contacts/ui/components/NumberInput.kt | 253 ++++++++++++++---- .../contacts/ui/screens/CallLogsScreen.kt | 51 ++-- 2 files changed, 232 insertions(+), 72 deletions(-) diff --git a/app/src/main/java/com/bnyro/contacts/ui/components/NumberInput.kt b/app/src/main/java/com/bnyro/contacts/ui/components/NumberInput.kt index 40802bed..3569ec13 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/components/NumberInput.kt +++ b/app/src/main/java/com/bnyro/contacts/ui/components/NumberInput.kt @@ -1,79 +1,236 @@ package com.bnyro.contacts.ui.components -import androidx.compose.foundation.clickable -import androidx.compose.foundation.interaction.MutableInteractionSource +import android.annotation.SuppressLint +import android.os.Build +import android.view.SoundEffectConstants +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.background +import androidx.compose.foundation.combinedClickable +import androidx.compose.foundation.horizontalScroll import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.BoxScope +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.RowScope +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.lazy.grid.GridCells -import androidx.compose.foundation.lazy.grid.LazyVerticalGrid -import androidx.compose.foundation.lazy.grid.items +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Backspace -import androidx.compose.material3.FloatingActionButton +import androidx.compose.material.icons.rounded.Backspace +import androidx.compose.material.icons.rounded.Call import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedButton import androidx.compose.material3.Text +import androidx.compose.material3.surfaceColorAtElevation import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip -import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.hapticfeedback.HapticFeedbackType +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalHapticFeedback +import androidx.compose.ui.platform.LocalView +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import com.bnyro.contacts.ext.removeLastChar +import com.bnyro.contacts.R +import com.bnyro.contacts.util.SmsUtil -val phoneNumberInputs = listOf("1", "2", "3", "4", "5", "6", "7", "8", "9", "*", "0", "#") +val keypadNumbers = arrayOf( + arrayOf("1", "2", "3"), + arrayOf("4", "5", "6"), + arrayOf("7", "8", "9"), + arrayOf("*", "0", "#") +) +@SuppressLint("NewApi") @Composable fun NumberInput( - initialNumber: String, - onNumberChange: (String) -> Unit + onNumberInput: (String) -> Unit, + onDelete: () -> Unit, + onDial: () -> Unit ) { - LazyVerticalGrid( - modifier = Modifier.fillMaxWidth(), - columns = GridCells.Fixed(3), - horizontalArrangement = Arrangement.Center + val buttonSpacing = 8.dp + val context = LocalContext.current + val subscriptions = remember { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) { + SmsUtil.getSubscriptions(context) + } else { + null + } + } + Column( + modifier = Modifier + .padding(8.dp) + .fillMaxWidth(), + verticalArrangement = Arrangement.spacedBy(buttonSpacing) ) { - items(phoneNumberInputs) { - Box( - modifier = Modifier.fillMaxSize(), - contentAlignment = Alignment.Center + keypadNumbers.forEach { col -> + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(buttonSpacing) ) { - val interactionSource = remember { - MutableInteractionSource() - } - Text( - modifier = Modifier - .clip(CircleShape) - .clickable(interactionSource, indication = null) { - onNumberChange(initialNumber + it) + col.forEach { + NumpadButton( + text = it, + onClick = { + onNumberInput(it) } - .padding(20.dp), - text = it, - fontSize = 20.sp, - fontWeight = FontWeight.Bold - ) + ) + } } } - item {} - item {} - item { - Box( - modifier = Modifier - .fillMaxSize() - .padding(top = 20.dp), - contentAlignment = Alignment.Center + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(buttonSpacing) + ) { + Spacer(modifier = Modifier.weight(1f)) + NumpadButton( + onClick = { onDial.invoke() }, + backgroundColor = MaterialTheme.colorScheme.secondaryContainer ) { - FloatingActionButton( - onClick = { onNumberChange(initialNumber.removeLastChar()) } - ) { - Icon(Icons.Default.Backspace, null) + Icon( + Icons.Rounded.Call, + contentDescription = stringResource(R.string.dial), + tint = MaterialTheme.colorScheme.onSecondaryContainer + ) + } + NumpadButton( + onClick = { onDelete.invoke() }, + backgroundColor = MaterialTheme.colorScheme.tertiaryContainer + ) { + Icon( + Icons.Rounded.Backspace, + contentDescription = stringResource(R.string.delete), + tint = MaterialTheme.colorScheme.onTertiaryContainer + ) + } + } + if (subscriptions != null && subscriptions.size >= 2) { + var currentSub by remember { mutableIntStateOf(0) } + LaunchedEffect(Unit) { + currentSub = 0 + } + Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) { + OutlinedButton(onClick = { + currentSub = if (currentSub == 0) 1 else 0 + }) { + Text( + text = "SIM ${subscriptions[currentSub].simSlotIndex + 1} - ${subscriptions[currentSub].displayName}" + ) } } } + + Spacer(modifier = Modifier.height(30.dp)) + } +} + +@Composable +fun RowScope.NumpadButton( + text: String, + textColor: Color = MaterialTheme.colorScheme.onSurface, + backgroundColor: Color = MaterialTheme.colorScheme.surfaceColorAtElevation(10.dp), + aspectRatio: Float = 1f, + onClick: () -> Unit, + onLongClick: () -> Unit = { } +) { + NumpadButton( + backgroundColor = backgroundColor, + aspectRatio = aspectRatio, + onClick = onClick, + onLongClick = onLongClick + ) { + Text( + text = text, + color = textColor, + style = MaterialTheme.typography.displaySmall + ) + } +} + +@OptIn(ExperimentalFoundationApi::class) +@Composable +fun RowScope.NumpadButton( + backgroundColor: Color = MaterialTheme.colorScheme.surfaceVariant, + aspectRatio: Float = 1f, + onClick: () -> Unit, + onLongClick: () -> Unit = { }, + content: @Composable (BoxScope.() -> Unit) +) { + val view = LocalView.current + val haptic = LocalHapticFeedback.current + Box( + contentAlignment = Alignment.Center, + modifier = Modifier + .clip(CircleShape) + .combinedClickable( + onClick = { + onClick.invoke() + view.playSoundEffect(SoundEffectConstants.CLICK) + }, + onLongClick = { + onLongClick.invoke() + haptic.performHapticFeedback(HapticFeedbackType.LongPress) + } + ) + .background(backgroundColor) + .aspectRatio(2f) + .weight(aspectRatio), + content = content + ) +} + +@Composable +fun ColumnScope.PhoneNumberDisplay(displayText: String) { + Column( + modifier = Modifier + .fillMaxWidth() + .weight(1.0f) + ) { + LazyColumn( + Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp, vertical = 8.dp) + .weight(1f), + verticalArrangement = Arrangement.spacedBy(8.dp, alignment = Alignment.Bottom) + ) { + // TODO: Contact Suggestions + } + val scroll = rememberScrollState() + Row( + Modifier + .fillMaxWidth() + .horizontalScroll(scroll) + .clip(RoundedCornerShape(24.dp)) + .background(MaterialTheme.colorScheme.surfaceVariant), + horizontalArrangement = Arrangement.Center + ) { + Text( + text = displayText, + textAlign = TextAlign.Center, + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 8.dp, horizontal = 16.dp), + style = MaterialTheme.typography.displayMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant, + maxLines = 1 + ) + } } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/bnyro/contacts/ui/screens/CallLogsScreen.kt b/app/src/main/java/com/bnyro/contacts/ui/screens/CallLogsScreen.kt index 5841611c..c42ee348 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/screens/CallLogsScreen.kt +++ b/app/src/main/java/com/bnyro/contacts/ui/screens/CallLogsScreen.kt @@ -7,24 +7,22 @@ import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items -import androidx.compose.foundation.shape.CircleShape import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Call import androidx.compose.material.icons.filled.Dialpad import androidx.compose.material3.ElevatedCard import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.ExtendedFloatingActionButton import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.Icon import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.Scaffold import androidx.compose.material3.Text +import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue @@ -36,12 +34,13 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp import com.bnyro.contacts.R +import com.bnyro.contacts.ext.removeLastChar import com.bnyro.contacts.obj.CallLogEntry import com.bnyro.contacts.ui.components.ContactIconPlaceholder import com.bnyro.contacts.ui.components.NothingHere import com.bnyro.contacts.ui.components.NumberInput +import com.bnyro.contacts.ui.components.PhoneNumberDisplay import com.bnyro.contacts.ui.components.dialogs.ConfirmationDialog import com.bnyro.contacts.ui.models.ContactsModel import com.bnyro.contacts.ui.models.DialerModel @@ -100,11 +99,11 @@ fun CallLogsScreen( mutableStateOf(false) } - ElevatedCard ( + ElevatedCard( modifier = Modifier .fillMaxWidth() .padding(horizontal = 10.dp, vertical = 5.dp) - .clickable { if (it.phoneNumber.isNotBlank()) showCallDialog = true }, + .clickable { if (it.phoneNumber.isNotBlank()) showCallDialog = true } ) { Row( modifier = Modifier.padding(vertical = 10.dp, horizontal = 16.dp), @@ -119,7 +118,10 @@ fun CallLogsScreen( Column { Text(text = contact?.displayName ?: it.phoneNumber, maxLines = 1) - Text(text = DateUtils.getRelativeTimeSpanString(it.time)?.toString().orEmpty()) + Text( + text = DateUtils.getRelativeTimeSpanString(it.time)?.toString() + .orEmpty() + ) } } } @@ -128,7 +130,10 @@ fun CallLogsScreen( ConfirmationDialog( onDismissRequest = { showCallDialog = false }, title = stringResource(id = R.string.dial), - text = stringResource(id = R.string.confirm_start_call, contact?.displayName ?: it.phoneNumber) + text = stringResource( + id = R.string.confirm_start_call, + contact?.displayName ?: it.phoneNumber + ) ) { callNumber(it.phoneNumber) } @@ -141,24 +146,22 @@ fun CallLogsScreen( } if (showNumberPicker) { - ModalBottomSheet(onDismissRequest = { showNumberPicker = false }) { + val state = rememberModalBottomSheetState(skipPartiallyExpanded = true) + ModalBottomSheet(onDismissRequest = { showNumberPicker = false }, sheetState = state) { Column( modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 30.dp), + .fillMaxSize() + .padding(horizontal = 8.dp), horizontalAlignment = Alignment.CenterHorizontally ) { - Text(text = numberToCall, fontSize = 20.sp) - NumberInput(initialNumber = numberToCall, onNumberChange = { numberToCall = it }) - ExtendedFloatingActionButton( - shape = CircleShape, - onClick = { - callNumber(numberToCall) - } - ) { - Icon(Icons.Default.Call, contentDescription = stringResource(R.string.dial)) - } - Spacer(modifier = Modifier.height(30.dp)) + PhoneNumberDisplay(displayText = numberToCall) + NumberInput(onNumberInput = { + numberToCall += it + }, onDelete = { + numberToCall = numberToCall.removeLastChar() + }, onDial = { + callNumber(numberToCall) + }) } } } @@ -166,4 +169,4 @@ fun CallLogsScreen( LaunchedEffect(Unit) { dialerModel.requestDefaultDialerApp(context) } -} \ No newline at end of file +} From fc5b21f599ba4fee2abf7c70ad204862c6d357ec Mon Sep 17 00:00:00 2001 From: Suhas Dissanayake Date: Fri, 8 Dec 2023 14:55:28 +0530 Subject: [PATCH 003/159] fix: dialer screen crashing --- .../contacts/ui/activities/CallActivity.kt | 6 ++--- .../bnyro/contacts/ui/screens/DialerScreen.kt | 26 ++++++++++++------- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/com/bnyro/contacts/ui/activities/CallActivity.kt b/app/src/main/java/com/bnyro/contacts/ui/activities/CallActivity.kt index a90d7e8d..6f074451 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/activities/CallActivity.kt +++ b/app/src/main/java/com/bnyro/contacts/ui/activities/CallActivity.kt @@ -13,7 +13,7 @@ import com.bnyro.contacts.ui.models.DialerModel import com.bnyro.contacts.ui.screens.DialerScreen import com.bnyro.contacts.ui.theme.ConnectYouTheme -class CallActivity: BaseActivity() { +class CallActivity : BaseActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -28,11 +28,11 @@ class CallActivity: BaseActivity() { .padding(pV) ) { DialerScreen(contactsModel = contactsModel, dialerModel = dialerModel) { - // TODO: finish() + this@CallActivity.finish() } } } } } } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/bnyro/contacts/ui/screens/DialerScreen.kt b/app/src/main/java/com/bnyro/contacts/ui/screens/DialerScreen.kt index 131f61c9..48ed5a3b 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/screens/DialerScreen.kt +++ b/app/src/main/java/com/bnyro/contacts/ui/screens/DialerScreen.kt @@ -1,5 +1,7 @@ package com.bnyro.contacts.ui.screens +import android.os.Handler +import android.os.Looper import android.telecom.Call import android.text.format.DateUtils import androidx.compose.foundation.layout.Column @@ -31,8 +33,6 @@ import com.bnyro.contacts.R import com.bnyro.contacts.ui.models.ContactsModel import com.bnyro.contacts.ui.models.DialerModel import com.bnyro.contacts.util.CallManager -import java.util.Timer -import kotlin.concurrent.scheduleAtFixedRate @Composable fun DialerScreen( @@ -49,19 +49,24 @@ fun DialerScreen( var elapsedTime by remember { mutableLongStateOf(0L) } - val timer = Timer() + val handler = remember { Handler(Looper.getMainLooper()) } + + fun updateTime() { + elapsedTime++ + handler.postDelayed(::updateTime, 1000L) + } DisposableEffect(Unit) { val listener: (Int) -> Unit = { callState = it - if (callState == Call.STATE_DISCONNECTED) onClose.invoke() + if (callState == Call.STATE_DISCONNECTED) { + handler.removeCallbacks(::updateTime) + onClose.invoke() + } if (callState == Call.STATE_ACTIVE) { - timer.cancel() - timer.scheduleAtFixedRate(1000, 1000) { - elapsedTime++ - } + updateTime() } } @@ -79,7 +84,10 @@ fun DialerScreen( Spacer(modifier = Modifier.height(50.dp)) Text( text = when (callState) { - Call.STATE_RINGING, Call.STATE_DIALING, Call.STATE_PULLING_CALL -> stringResource(R.string.ringing) + Call.STATE_RINGING, Call.STATE_DIALING, Call.STATE_PULLING_CALL -> stringResource( + R.string.ringing + ) + Call.STATE_DISCONNECTING -> stringResource(R.string.disconnecting) Call.STATE_ACTIVE -> stringResource(R.string.in_progress) Call.STATE_CONNECTING -> stringResource(R.string.connecting) From f84a6e7a42c1da9c8d50770364a1885b5f82e3a1 Mon Sep 17 00:00:00 2001 From: Bnyro Date: Tue, 12 Dec 2023 18:33:07 +0100 Subject: [PATCH 004/159] feat: buttons to mute the microphone and toggle speakers --- .idea/gradle.xml | 2 +- .idea/inspectionProfiles/Project_Default.xml | 9 ++++ .../contacts/ui/components/DialerButton.kt | 50 +++++++++++++++++++ .../bnyro/contacts/ui/models/DialerModel.kt | 18 +++++++ .../bnyro/contacts/ui/screens/DialerScreen.kt | 32 ++++++++++++ app/src/main/res/values/strings.xml | 2 + 6 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/com/bnyro/contacts/ui/components/DialerButton.kt diff --git a/.idea/gradle.xml b/.idea/gradle.xml index f01eec37..cb865f69 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -4,7 +4,6 @@ diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml index 103e00cb..44ca2d9b 100644 --- a/.idea/inspectionProfiles/Project_Default.xml +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -3,30 +3,39 @@ @@ -235,24 +235,24 @@ @@ -264,7 +264,7 @@ @@ -280,7 +280,7 @@ diff --git a/app/src/main/java/com/bnyro/contacts/App.kt b/app/src/main/java/com/bnyro/contacts/App.kt index 7cdcf386..355a5816 100644 --- a/app/src/main/java/com/bnyro/contacts/App.kt +++ b/app/src/main/java/com/bnyro/contacts/App.kt @@ -1,16 +1,16 @@ package com.bnyro.contacts import android.app.Application -import com.bnyro.contacts.db.DatabaseHolder -import com.bnyro.contacts.repo.DeviceContactsRepository -import com.bnyro.contacts.repo.DeviceSmsRepo -import com.bnyro.contacts.repo.LocalContactsRepository -import com.bnyro.contacts.repo.LocalSmsRepo -import com.bnyro.contacts.repo.SmsRepository +import com.bnyro.contacts.data.database.DatabaseHolder +import com.bnyro.contacts.domain.repositories.DeviceContactsRepository +import com.bnyro.contacts.domain.repositories.DeviceSmsRepo +import com.bnyro.contacts.domain.repositories.LocalContactsRepository +import com.bnyro.contacts.domain.repositories.LocalSmsRepo +import com.bnyro.contacts.domain.repositories.SmsRepository import com.bnyro.contacts.util.NotificationHelper import com.bnyro.contacts.util.Preferences import com.bnyro.contacts.util.ShortcutHelper -import com.bnyro.contacts.workers.BackupWorker +import com.bnyro.contacts.util.workers.BackupWorker class App : Application() { val deviceContactsRepository by lazy { diff --git a/app/src/main/java/com/bnyro/contacts/db/AppDatabase.kt b/app/src/main/java/com/bnyro/contacts/data/database/AppDatabase.kt similarity index 59% rename from app/src/main/java/com/bnyro/contacts/db/AppDatabase.kt rename to app/src/main/java/com/bnyro/contacts/data/database/AppDatabase.kt index b7dbf4cb..2e02620d 100644 --- a/app/src/main/java/com/bnyro/contacts/db/AppDatabase.kt +++ b/app/src/main/java/com/bnyro/contacts/data/database/AppDatabase.kt @@ -1,13 +1,13 @@ -package com.bnyro.contacts.db +package com.bnyro.contacts.data.database import androidx.room.AutoMigration import androidx.room.Database import androidx.room.RoomDatabase -import com.bnyro.contacts.db.dao.LocalContactsDao -import com.bnyro.contacts.db.dao.LocalSmsDao -import com.bnyro.contacts.db.obj.DbDataItem -import com.bnyro.contacts.db.obj.LocalContact -import com.bnyro.contacts.db.obj.SmsData +import com.bnyro.contacts.data.database.dao.LocalContactsDao +import com.bnyro.contacts.data.database.dao.LocalSmsDao +import com.bnyro.contacts.data.database.obj.DbDataItem +import com.bnyro.contacts.data.database.obj.LocalContact +import com.bnyro.contacts.data.database.obj.SmsData @Database( entities = [LocalContact::class, DbDataItem::class, SmsData::class], diff --git a/app/src/main/java/com/bnyro/contacts/db/DatabaseHolder.kt b/app/src/main/java/com/bnyro/contacts/data/database/DatabaseHolder.kt similarity index 95% rename from app/src/main/java/com/bnyro/contacts/db/DatabaseHolder.kt rename to app/src/main/java/com/bnyro/contacts/data/database/DatabaseHolder.kt index fb8f2d49..c802b578 100644 --- a/app/src/main/java/com/bnyro/contacts/db/DatabaseHolder.kt +++ b/app/src/main/java/com/bnyro/contacts/data/database/DatabaseHolder.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.db +package com.bnyro.contacts.data.database import android.content.Context import androidx.room.Room diff --git a/app/src/main/java/com/bnyro/contacts/db/dao/LocalContactsDao.kt b/app/src/main/java/com/bnyro/contacts/data/database/dao/LocalContactsDao.kt similarity index 78% rename from app/src/main/java/com/bnyro/contacts/db/dao/LocalContactsDao.kt rename to app/src/main/java/com/bnyro/contacts/data/database/dao/LocalContactsDao.kt index 5dda03b5..e935628c 100644 --- a/app/src/main/java/com/bnyro/contacts/db/dao/LocalContactsDao.kt +++ b/app/src/main/java/com/bnyro/contacts/data/database/dao/LocalContactsDao.kt @@ -1,12 +1,12 @@ -package com.bnyro.contacts.db.dao +package com.bnyro.contacts.data.database.dao import androidx.room.Dao import androidx.room.Insert import androidx.room.Query import androidx.room.Transaction -import com.bnyro.contacts.db.obj.ContactWithData -import com.bnyro.contacts.db.obj.DbDataItem -import com.bnyro.contacts.db.obj.LocalContact +import com.bnyro.contacts.data.database.obj.ContactWithData +import com.bnyro.contacts.data.database.obj.DbDataItem +import com.bnyro.contacts.data.database.obj.LocalContact @Dao interface LocalContactsDao { diff --git a/app/src/main/java/com/bnyro/contacts/db/dao/LocalSmsDao.kt b/app/src/main/java/com/bnyro/contacts/data/database/dao/LocalSmsDao.kt similarity index 86% rename from app/src/main/java/com/bnyro/contacts/db/dao/LocalSmsDao.kt rename to app/src/main/java/com/bnyro/contacts/data/database/dao/LocalSmsDao.kt index fbb5c3c1..1ce1e6be 100644 --- a/app/src/main/java/com/bnyro/contacts/db/dao/LocalSmsDao.kt +++ b/app/src/main/java/com/bnyro/contacts/data/database/dao/LocalSmsDao.kt @@ -1,9 +1,9 @@ -package com.bnyro.contacts.db.dao +package com.bnyro.contacts.data.database.dao import androidx.room.Dao import androidx.room.Insert import androidx.room.Query -import com.bnyro.contacts.db.obj.SmsData +import com.bnyro.contacts.data.database.obj.SmsData import kotlinx.coroutines.flow.Flow @Dao diff --git a/app/src/main/java/com/bnyro/contacts/db/obj/ContactWithData.kt b/app/src/main/java/com/bnyro/contacts/data/database/obj/ContactWithData.kt similarity index 86% rename from app/src/main/java/com/bnyro/contacts/db/obj/ContactWithData.kt rename to app/src/main/java/com/bnyro/contacts/data/database/obj/ContactWithData.kt index a45c95db..cf8c7e71 100644 --- a/app/src/main/java/com/bnyro/contacts/db/obj/ContactWithData.kt +++ b/app/src/main/java/com/bnyro/contacts/data/database/obj/ContactWithData.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.db.obj +package com.bnyro.contacts.data.database.obj import androidx.room.Embedded import androidx.room.Relation diff --git a/app/src/main/java/com/bnyro/contacts/db/obj/DbDataItem.kt b/app/src/main/java/com/bnyro/contacts/data/database/obj/DbDataItem.kt similarity index 85% rename from app/src/main/java/com/bnyro/contacts/db/obj/DbDataItem.kt rename to app/src/main/java/com/bnyro/contacts/data/database/obj/DbDataItem.kt index 2deb7ed1..c550bbbb 100644 --- a/app/src/main/java/com/bnyro/contacts/db/obj/DbDataItem.kt +++ b/app/src/main/java/com/bnyro/contacts/data/database/obj/DbDataItem.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.db.obj +package com.bnyro.contacts.data.database.obj import androidx.room.Entity import androidx.room.PrimaryKey diff --git a/app/src/main/java/com/bnyro/contacts/db/obj/LocalContact.kt b/app/src/main/java/com/bnyro/contacts/data/database/obj/LocalContact.kt similarity index 91% rename from app/src/main/java/com/bnyro/contacts/db/obj/LocalContact.kt rename to app/src/main/java/com/bnyro/contacts/data/database/obj/LocalContact.kt index 6b29a9d4..5c377696 100644 --- a/app/src/main/java/com/bnyro/contacts/db/obj/LocalContact.kt +++ b/app/src/main/java/com/bnyro/contacts/data/database/obj/LocalContact.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.db.obj +package com.bnyro.contacts.data.database.obj import androidx.room.ColumnInfo import androidx.room.Entity diff --git a/app/src/main/java/com/bnyro/contacts/db/obj/SmsData.kt b/app/src/main/java/com/bnyro/contacts/data/database/obj/SmsData.kt similarity index 91% rename from app/src/main/java/com/bnyro/contacts/db/obj/SmsData.kt rename to app/src/main/java/com/bnyro/contacts/data/database/obj/SmsData.kt index 535506a5..2da0e13d 100644 --- a/app/src/main/java/com/bnyro/contacts/db/obj/SmsData.kt +++ b/app/src/main/java/com/bnyro/contacts/data/database/obj/SmsData.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.db.obj +package com.bnyro.contacts.data.database.obj import androidx.room.ColumnInfo import androidx.room.Entity diff --git a/app/src/main/java/com/bnyro/contacts/enums/BackupType.kt b/app/src/main/java/com/bnyro/contacts/domain/enums/BackupType.kt similarity index 82% rename from app/src/main/java/com/bnyro/contacts/enums/BackupType.kt rename to app/src/main/java/com/bnyro/contacts/domain/enums/BackupType.kt index f9c1373a..f20d9698 100644 --- a/app/src/main/java/com/bnyro/contacts/enums/BackupType.kt +++ b/app/src/main/java/com/bnyro/contacts/domain/enums/BackupType.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.enums +package com.bnyro.contacts.domain.enums enum class BackupType { NONE, diff --git a/app/src/main/java/com/bnyro/contacts/enums/ContactAttributes.kt b/app/src/main/java/com/bnyro/contacts/domain/enums/ContactAttributes.kt similarity index 97% rename from app/src/main/java/com/bnyro/contacts/enums/ContactAttributes.kt rename to app/src/main/java/com/bnyro/contacts/domain/enums/ContactAttributes.kt index 713bce6a..fc341a43 100644 --- a/app/src/main/java/com/bnyro/contacts/enums/ContactAttributes.kt +++ b/app/src/main/java/com/bnyro/contacts/domain/enums/ContactAttributes.kt @@ -1,11 +1,11 @@ -package com.bnyro.contacts.enums +package com.bnyro.contacts.domain.enums import android.provider.ContactsContract import android.provider.ContactsContract.Intents import com.bnyro.contacts.R -import com.bnyro.contacts.obj.ContactData -import com.bnyro.contacts.obj.TranslatedType -import com.bnyro.contacts.obj.ValueWithType +import com.bnyro.contacts.domain.model.ContactData +import com.bnyro.contacts.domain.model.TranslatedType +import com.bnyro.contacts.domain.model.ValueWithType import com.bnyro.contacts.util.CalendarUtils import com.bnyro.contacts.util.ContactsHelper diff --git a/app/src/main/java/com/bnyro/contacts/enums/ContactsSource.kt b/app/src/main/java/com/bnyro/contacts/domain/enums/ContactsSource.kt similarity index 82% rename from app/src/main/java/com/bnyro/contacts/enums/ContactsSource.kt rename to app/src/main/java/com/bnyro/contacts/domain/enums/ContactsSource.kt index 221c85c1..df4079ae 100644 --- a/app/src/main/java/com/bnyro/contacts/enums/ContactsSource.kt +++ b/app/src/main/java/com/bnyro/contacts/domain/enums/ContactsSource.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.enums +package com.bnyro.contacts.domain.enums import androidx.annotation.StringRes import com.bnyro.contacts.R diff --git a/app/src/main/java/com/bnyro/contacts/enums/DataCategory.kt b/app/src/main/java/com/bnyro/contacts/domain/enums/DataCategory.kt similarity index 73% rename from app/src/main/java/com/bnyro/contacts/enums/DataCategory.kt rename to app/src/main/java/com/bnyro/contacts/domain/enums/DataCategory.kt index 1335ada4..b24df333 100644 --- a/app/src/main/java/com/bnyro/contacts/enums/DataCategory.kt +++ b/app/src/main/java/com/bnyro/contacts/domain/enums/DataCategory.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.enums +package com.bnyro.contacts.domain.enums enum class DataCategory { NUMBER, diff --git a/app/src/main/java/com/bnyro/contacts/enums/IntentActionType.kt b/app/src/main/java/com/bnyro/contacts/domain/enums/IntentActionType.kt similarity index 71% rename from app/src/main/java/com/bnyro/contacts/enums/IntentActionType.kt rename to app/src/main/java/com/bnyro/contacts/domain/enums/IntentActionType.kt index 30be91a2..e9bbbb73 100644 --- a/app/src/main/java/com/bnyro/contacts/enums/IntentActionType.kt +++ b/app/src/main/java/com/bnyro/contacts/domain/enums/IntentActionType.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.enums +package com.bnyro.contacts.domain.enums enum class IntentActionType { WEBSITE, diff --git a/app/src/main/java/com/bnyro/contacts/enums/SortOrder.kt b/app/src/main/java/com/bnyro/contacts/domain/enums/SortOrder.kt similarity index 81% rename from app/src/main/java/com/bnyro/contacts/enums/SortOrder.kt rename to app/src/main/java/com/bnyro/contacts/domain/enums/SortOrder.kt index e943f3a6..999d77e3 100644 --- a/app/src/main/java/com/bnyro/contacts/enums/SortOrder.kt +++ b/app/src/main/java/com/bnyro/contacts/domain/enums/SortOrder.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.enums +package com.bnyro.contacts.domain.enums enum class SortOrder { FIRSTNAME, diff --git a/app/src/main/java/com/bnyro/contacts/enums/ThemeMode.kt b/app/src/main/java/com/bnyro/contacts/domain/enums/ThemeMode.kt similarity index 82% rename from app/src/main/java/com/bnyro/contacts/enums/ThemeMode.kt rename to app/src/main/java/com/bnyro/contacts/domain/enums/ThemeMode.kt index fb86fd2a..f0538322 100644 --- a/app/src/main/java/com/bnyro/contacts/enums/ThemeMode.kt +++ b/app/src/main/java/com/bnyro/contacts/domain/enums/ThemeMode.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.enums +package com.bnyro.contacts.domain.enums enum class ThemeMode { SYSTEM, diff --git a/app/src/main/java/com/bnyro/contacts/obj/AccountType.kt b/app/src/main/java/com/bnyro/contacts/domain/model/AccountType.kt similarity index 90% rename from app/src/main/java/com/bnyro/contacts/obj/AccountType.kt rename to app/src/main/java/com/bnyro/contacts/domain/model/AccountType.kt index 3f035622..52f2e6f1 100644 --- a/app/src/main/java/com/bnyro/contacts/obj/AccountType.kt +++ b/app/src/main/java/com/bnyro/contacts/domain/model/AccountType.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.obj +package com.bnyro.contacts.domain.model data class AccountType( val name: String, diff --git a/app/src/main/java/com/bnyro/contacts/obj/CallLogEntry.kt b/app/src/main/java/com/bnyro/contacts/domain/model/CallLogEntry.kt similarity index 74% rename from app/src/main/java/com/bnyro/contacts/obj/CallLogEntry.kt rename to app/src/main/java/com/bnyro/contacts/domain/model/CallLogEntry.kt index 8c2b5a5a..668b6b85 100644 --- a/app/src/main/java/com/bnyro/contacts/obj/CallLogEntry.kt +++ b/app/src/main/java/com/bnyro/contacts/domain/model/CallLogEntry.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.obj +package com.bnyro.contacts.domain.model data class CallLogEntry( val phoneNumber: String, diff --git a/app/src/main/java/com/bnyro/contacts/obj/ContactData.kt b/app/src/main/java/com/bnyro/contacts/domain/model/ContactData.kt similarity index 93% rename from app/src/main/java/com/bnyro/contacts/obj/ContactData.kt rename to app/src/main/java/com/bnyro/contacts/domain/model/ContactData.kt index dc45cdf6..92a8a6fa 100644 --- a/app/src/main/java/com/bnyro/contacts/obj/ContactData.kt +++ b/app/src/main/java/com/bnyro/contacts/domain/model/ContactData.kt @@ -1,7 +1,7 @@ -package com.bnyro.contacts.obj +package com.bnyro.contacts.domain.model import android.graphics.Bitmap -import com.bnyro.contacts.enums.SortOrder +import com.bnyro.contacts.domain.enums.SortOrder data class ContactData( var dataId: Int = 0, diff --git a/app/src/main/java/com/bnyro/contacts/obj/ContactsGroup.kt b/app/src/main/java/com/bnyro/contacts/domain/model/ContactsGroup.kt similarity index 63% rename from app/src/main/java/com/bnyro/contacts/obj/ContactsGroup.kt rename to app/src/main/java/com/bnyro/contacts/domain/model/ContactsGroup.kt index e4aa15c1..8818cc57 100644 --- a/app/src/main/java/com/bnyro/contacts/obj/ContactsGroup.kt +++ b/app/src/main/java/com/bnyro/contacts/domain/model/ContactsGroup.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.obj +package com.bnyro.contacts.domain.model data class ContactsGroup( val title: String, diff --git a/app/src/main/java/com/bnyro/contacts/obj/FilterOptions.kt b/app/src/main/java/com/bnyro/contacts/domain/model/FilterOptions.kt similarity index 87% rename from app/src/main/java/com/bnyro/contacts/obj/FilterOptions.kt rename to app/src/main/java/com/bnyro/contacts/domain/model/FilterOptions.kt index 93f876b9..27ee47ea 100644 --- a/app/src/main/java/com/bnyro/contacts/obj/FilterOptions.kt +++ b/app/src/main/java/com/bnyro/contacts/domain/model/FilterOptions.kt @@ -1,6 +1,6 @@ -package com.bnyro.contacts.obj +package com.bnyro.contacts.domain.model -import com.bnyro.contacts.enums.SortOrder +import com.bnyro.contacts.domain.enums.SortOrder import com.bnyro.contacts.util.Preferences data class FilterOptions( diff --git a/app/src/main/java/com/bnyro/contacts/obj/NavBarItem.kt b/app/src/main/java/com/bnyro/contacts/domain/model/NavBarItem.kt similarity index 76% rename from app/src/main/java/com/bnyro/contacts/obj/NavBarItem.kt rename to app/src/main/java/com/bnyro/contacts/domain/model/NavBarItem.kt index 3970e122..19128da1 100644 --- a/app/src/main/java/com/bnyro/contacts/obj/NavBarItem.kt +++ b/app/src/main/java/com/bnyro/contacts/domain/model/NavBarItem.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.obj +package com.bnyro.contacts.domain.model import androidx.compose.ui.graphics.vector.ImageVector diff --git a/app/src/main/java/com/bnyro/contacts/obj/SmsThread.kt b/app/src/main/java/com/bnyro/contacts/domain/model/SmsThread.kt similarity index 60% rename from app/src/main/java/com/bnyro/contacts/obj/SmsThread.kt rename to app/src/main/java/com/bnyro/contacts/domain/model/SmsThread.kt index a8504bfc..5c6741e7 100644 --- a/app/src/main/java/com/bnyro/contacts/obj/SmsThread.kt +++ b/app/src/main/java/com/bnyro/contacts/domain/model/SmsThread.kt @@ -1,6 +1,6 @@ -package com.bnyro.contacts.obj +package com.bnyro.contacts.domain.model -import com.bnyro.contacts.db.obj.SmsData +import com.bnyro.contacts.data.database.obj.SmsData data class SmsThread( val threadId: Long, diff --git a/app/src/main/java/com/bnyro/contacts/obj/TranslatedType.kt b/app/src/main/java/com/bnyro/contacts/domain/model/TranslatedType.kt similarity index 83% rename from app/src/main/java/com/bnyro/contacts/obj/TranslatedType.kt rename to app/src/main/java/com/bnyro/contacts/domain/model/TranslatedType.kt index 6d6127e5..7d72275d 100644 --- a/app/src/main/java/com/bnyro/contacts/obj/TranslatedType.kt +++ b/app/src/main/java/com/bnyro/contacts/domain/model/TranslatedType.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.obj +package com.bnyro.contacts.domain.model import androidx.annotation.StringRes import ezvcard.parameter.VCardParameter diff --git a/app/src/main/java/com/bnyro/contacts/obj/ValueWithType.kt b/app/src/main/java/com/bnyro/contacts/domain/model/ValueWithType.kt similarity index 95% rename from app/src/main/java/com/bnyro/contacts/obj/ValueWithType.kt rename to app/src/main/java/com/bnyro/contacts/domain/model/ValueWithType.kt index 01ca0005..00cfe48e 100644 --- a/app/src/main/java/com/bnyro/contacts/obj/ValueWithType.kt +++ b/app/src/main/java/com/bnyro/contacts/domain/model/ValueWithType.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.obj +package com.bnyro.contacts.domain.model /** * A data class that represents a value with its type. diff --git a/app/src/main/java/com/bnyro/contacts/repo/ContactsRepository.kt b/app/src/main/java/com/bnyro/contacts/domain/repositories/ContactsRepository.kt similarity index 78% rename from app/src/main/java/com/bnyro/contacts/repo/ContactsRepository.kt rename to app/src/main/java/com/bnyro/contacts/domain/repositories/ContactsRepository.kt index f1c98a4c..815bea8a 100644 --- a/app/src/main/java/com/bnyro/contacts/repo/ContactsRepository.kt +++ b/app/src/main/java/com/bnyro/contacts/domain/repositories/ContactsRepository.kt @@ -1,7 +1,7 @@ -package com.bnyro.contacts.repo +package com.bnyro.contacts.domain.repositories -import com.bnyro.contacts.obj.ContactData -import com.bnyro.contacts.obj.ContactsGroup +import com.bnyro.contacts.domain.model.ContactData +import com.bnyro.contacts.domain.model.ContactsGroup interface ContactsRepository { val label: String diff --git a/app/src/main/java/com/bnyro/contacts/repo/DeviceContactsRepository.kt b/app/src/main/java/com/bnyro/contacts/domain/repositories/DeviceContactsRepository.kt similarity index 97% rename from app/src/main/java/com/bnyro/contacts/repo/DeviceContactsRepository.kt rename to app/src/main/java/com/bnyro/contacts/domain/repositories/DeviceContactsRepository.kt index 123b288f..885b2aa5 100644 --- a/app/src/main/java/com/bnyro/contacts/repo/DeviceContactsRepository.kt +++ b/app/src/main/java/com/bnyro/contacts/domain/repositories/DeviceContactsRepository.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.repo +package com.bnyro.contacts.domain.repositories import android.Manifest import android.accounts.AccountManager @@ -14,8 +14,6 @@ import android.provider.ContactsContract import android.provider.ContactsContract.AUTHORITY import android.provider.ContactsContract.CALLER_IS_SYNCADAPTER import android.provider.ContactsContract.CommonDataKinds.GroupMembership -import android.provider.ContactsContract.CommonDataKinds.Nickname -import android.provider.ContactsContract.CommonDataKinds.Organization import android.provider.ContactsContract.CommonDataKinds.Phone import android.provider.ContactsContract.CommonDataKinds.Photo import android.provider.ContactsContract.CommonDataKinds.StructuredName @@ -24,20 +22,20 @@ import android.provider.ContactsContract.Data import android.provider.ContactsContract.RawContacts import androidx.annotation.RequiresPermission import com.bnyro.contacts.R -import com.bnyro.contacts.enums.BackupType -import com.bnyro.contacts.enums.ListAttribute -import com.bnyro.contacts.enums.StringAttribute -import com.bnyro.contacts.ext.intValue -import com.bnyro.contacts.ext.longValue -import com.bnyro.contacts.ext.notAName -import com.bnyro.contacts.ext.stringValue -import com.bnyro.contacts.obj.AccountType -import com.bnyro.contacts.obj.ContactData -import com.bnyro.contacts.obj.ContactsGroup -import com.bnyro.contacts.obj.ValueWithType +import com.bnyro.contacts.domain.enums.BackupType +import com.bnyro.contacts.domain.enums.ListAttribute +import com.bnyro.contacts.domain.enums.StringAttribute +import com.bnyro.contacts.domain.model.AccountType +import com.bnyro.contacts.domain.model.ContactData +import com.bnyro.contacts.domain.model.ContactsGroup +import com.bnyro.contacts.domain.model.ValueWithType import com.bnyro.contacts.util.ContactsHelper import com.bnyro.contacts.util.ImageHelper import com.bnyro.contacts.util.Preferences +import com.bnyro.contacts.util.extension.intValue +import com.bnyro.contacts.util.extension.longValue +import com.bnyro.contacts.util.extension.notAName +import com.bnyro.contacts.util.extension.stringValue import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext diff --git a/app/src/main/java/com/bnyro/contacts/repo/DeviceSmsRepo.kt b/app/src/main/java/com/bnyro/contacts/domain/repositories/DeviceSmsRepo.kt similarity index 95% rename from app/src/main/java/com/bnyro/contacts/repo/DeviceSmsRepo.kt rename to app/src/main/java/com/bnyro/contacts/domain/repositories/DeviceSmsRepo.kt index 2767781a..9b6931e0 100644 --- a/app/src/main/java/com/bnyro/contacts/repo/DeviceSmsRepo.kt +++ b/app/src/main/java/com/bnyro/contacts/domain/repositories/DeviceSmsRepo.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.repo +package com.bnyro.contacts.domain.repositories import android.Manifest import android.annotation.SuppressLint @@ -10,13 +10,13 @@ import android.net.Uri import android.os.Build import android.provider.Telephony import androidx.annotation.RequiresPermission -import com.bnyro.contacts.db.obj.SmsData -import com.bnyro.contacts.ext.intValue -import com.bnyro.contacts.ext.longValue -import com.bnyro.contacts.ext.stringValue +import com.bnyro.contacts.data.database.obj.SmsData import com.bnyro.contacts.util.ContactsHelper import com.bnyro.contacts.util.PermissionHelper import com.bnyro.contacts.util.SmsUtil +import com.bnyro.contacts.util.extension.intValue +import com.bnyro.contacts.util.extension.longValue +import com.bnyro.contacts.util.extension.stringValue import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow diff --git a/app/src/main/java/com/bnyro/contacts/repo/LocalContactsRepository.kt b/app/src/main/java/com/bnyro/contacts/domain/repositories/LocalContactsRepository.kt similarity index 92% rename from app/src/main/java/com/bnyro/contacts/repo/LocalContactsRepository.kt rename to app/src/main/java/com/bnyro/contacts/domain/repositories/LocalContactsRepository.kt index d61006fa..7e0e45e5 100644 --- a/app/src/main/java/com/bnyro/contacts/repo/LocalContactsRepository.kt +++ b/app/src/main/java/com/bnyro/contacts/domain/repositories/LocalContactsRepository.kt @@ -1,23 +1,23 @@ -package com.bnyro.contacts.repo +package com.bnyro.contacts.domain.repositories import android.content.Context import android.graphics.Bitmap import android.graphics.BitmapFactory import com.bnyro.contacts.R -import com.bnyro.contacts.db.DatabaseHolder -import com.bnyro.contacts.db.obj.DbDataItem -import com.bnyro.contacts.db.obj.LocalContact -import com.bnyro.contacts.enums.BackupType -import com.bnyro.contacts.enums.DataCategory -import com.bnyro.contacts.ext.pmap -import com.bnyro.contacts.obj.ContactData -import com.bnyro.contacts.obj.ContactsGroup -import com.bnyro.contacts.obj.ValueWithType +import com.bnyro.contacts.data.database.DatabaseHolder +import com.bnyro.contacts.data.database.obj.DbDataItem +import com.bnyro.contacts.data.database.obj.LocalContact +import com.bnyro.contacts.domain.enums.BackupType +import com.bnyro.contacts.domain.enums.DataCategory +import com.bnyro.contacts.domain.model.ContactData +import com.bnyro.contacts.domain.model.ContactsGroup +import com.bnyro.contacts.domain.model.ValueWithType import com.bnyro.contacts.util.ImageHelper import com.bnyro.contacts.util.Preferences -import java.io.File +import com.bnyro.contacts.util.extension.pmap import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext +import java.io.File class LocalContactsRepository(context: Context) : ContactsRepository { override val label: String = context.getString(R.string.local) diff --git a/app/src/main/java/com/bnyro/contacts/repo/LocalSmsRepo.kt b/app/src/main/java/com/bnyro/contacts/domain/repositories/LocalSmsRepo.kt similarity index 86% rename from app/src/main/java/com/bnyro/contacts/repo/LocalSmsRepo.kt rename to app/src/main/java/com/bnyro/contacts/domain/repositories/LocalSmsRepo.kt index 61fc5a35..5239ad69 100644 --- a/app/src/main/java/com/bnyro/contacts/repo/LocalSmsRepo.kt +++ b/app/src/main/java/com/bnyro/contacts/domain/repositories/LocalSmsRepo.kt @@ -1,8 +1,8 @@ -package com.bnyro.contacts.repo +package com.bnyro.contacts.domain.repositories import android.content.Context -import com.bnyro.contacts.db.DatabaseHolder -import com.bnyro.contacts.db.obj.SmsData +import com.bnyro.contacts.data.database.DatabaseHolder +import com.bnyro.contacts.data.database.obj.SmsData import com.bnyro.contacts.util.ContactsHelper import kotlinx.coroutines.flow.Flow import kotlin.random.Random diff --git a/app/src/main/java/com/bnyro/contacts/repo/SmsRepository.kt b/app/src/main/java/com/bnyro/contacts/domain/repositories/SmsRepository.kt similarity index 80% rename from app/src/main/java/com/bnyro/contacts/repo/SmsRepository.kt rename to app/src/main/java/com/bnyro/contacts/domain/repositories/SmsRepository.kt index 88f250e8..0caa82aa 100644 --- a/app/src/main/java/com/bnyro/contacts/repo/SmsRepository.kt +++ b/app/src/main/java/com/bnyro/contacts/domain/repositories/SmsRepository.kt @@ -1,7 +1,7 @@ -package com.bnyro.contacts.repo +package com.bnyro.contacts.domain.repositories import android.content.Context -import com.bnyro.contacts.db.obj.SmsData +import com.bnyro.contacts.data.database.obj.SmsData import kotlinx.coroutines.flow.Flow interface SmsRepository { diff --git a/app/src/main/java/com/bnyro/contacts/nav/NavContainer.kt b/app/src/main/java/com/bnyro/contacts/navigation/NavContainer.kt similarity index 99% rename from app/src/main/java/com/bnyro/contacts/nav/NavContainer.kt rename to app/src/main/java/com/bnyro/contacts/navigation/NavContainer.kt index 13762834..2cf73773 100644 --- a/app/src/main/java/com/bnyro/contacts/nav/NavContainer.kt +++ b/app/src/main/java/com/bnyro/contacts/navigation/NavContainer.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.nav +package com.bnyro.contacts.navigation import android.content.res.Configuration import androidx.activity.addCallback diff --git a/app/src/main/java/com/bnyro/contacts/nav/NavHost.kt b/app/src/main/java/com/bnyro/contacts/navigation/NavHost.kt similarity index 82% rename from app/src/main/java/com/bnyro/contacts/nav/NavHost.kt rename to app/src/main/java/com/bnyro/contacts/navigation/NavHost.kt index 14aed676..4c8e675b 100644 --- a/app/src/main/java/com/bnyro/contacts/nav/NavHost.kt +++ b/app/src/main/java/com/bnyro/contacts/navigation/NavHost.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.nav +package com.bnyro.contacts.navigation import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider @@ -12,16 +12,16 @@ import androidx.navigation.NavType import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.navArgument -import com.bnyro.contacts.ui.models.ContactsModel -import com.bnyro.contacts.ui.models.DialerModel -import com.bnyro.contacts.ui.models.SmsModel -import com.bnyro.contacts.ui.models.ThemeModel -import com.bnyro.contacts.ui.screens.AboutScreen -import com.bnyro.contacts.ui.screens.CallLogsScreen -import com.bnyro.contacts.ui.screens.ContactsPage -import com.bnyro.contacts.ui.screens.SettingsScreen -import com.bnyro.contacts.ui.screens.SmsListScreen -import com.bnyro.contacts.ui.screens.SmsThreadScreen +import com.bnyro.contacts.presentation.screens.about.AboutScreen +import com.bnyro.contacts.presentation.screens.calllog.CallLogsScreen +import com.bnyro.contacts.presentation.screens.contacts.ContactsPage +import com.bnyro.contacts.presentation.screens.contacts.model.ContactsModel +import com.bnyro.contacts.presentation.screens.dialer.model.DialerModel +import com.bnyro.contacts.presentation.screens.settings.SettingsScreen +import com.bnyro.contacts.presentation.screens.settings.model.ThemeModel +import com.bnyro.contacts.presentation.screens.sms.SmsListScreen +import com.bnyro.contacts.presentation.screens.sms.SmsThreadScreen +import com.bnyro.contacts.presentation.screens.sms.model.SmsModel @Composable fun AppNavHost( diff --git a/app/src/main/java/com/bnyro/contacts/nav/NavRoutes.kt b/app/src/main/java/com/bnyro/contacts/navigation/NavRoutes.kt similarity index 96% rename from app/src/main/java/com/bnyro/contacts/nav/NavRoutes.kt rename to app/src/main/java/com/bnyro/contacts/navigation/NavRoutes.kt index d713c5b8..523550f8 100644 --- a/app/src/main/java/com/bnyro/contacts/nav/NavRoutes.kt +++ b/app/src/main/java/com/bnyro/contacts/navigation/NavRoutes.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.nav +package com.bnyro.contacts.navigation import androidx.annotation.StringRes import androidx.compose.material.icons.Icons diff --git a/app/src/main/java/com/bnyro/contacts/ui/components/BlockButton.kt b/app/src/main/java/com/bnyro/contacts/presentation/components/BlockButton.kt similarity index 97% rename from app/src/main/java/com/bnyro/contacts/ui/components/BlockButton.kt rename to app/src/main/java/com/bnyro/contacts/presentation/components/BlockButton.kt index b6f70db0..f35c2ceb 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/components/BlockButton.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/components/BlockButton.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.ui.components +package com.bnyro.contacts.presentation.components import androidx.compose.foundation.background import androidx.compose.foundation.clickable diff --git a/app/src/main/java/com/bnyro/contacts/ui/components/CharacterHeader.kt b/app/src/main/java/com/bnyro/contacts/presentation/components/CharacterHeader.kt similarity index 93% rename from app/src/main/java/com/bnyro/contacts/ui/components/CharacterHeader.kt rename to app/src/main/java/com/bnyro/contacts/presentation/components/CharacterHeader.kt index 32445d98..c440fabd 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/components/CharacterHeader.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/components/CharacterHeader.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.ui.components +package com.bnyro.contacts.presentation.components import androidx.compose.foundation.background import androidx.compose.foundation.layout.fillMaxWidth diff --git a/app/src/main/java/com/bnyro/contacts/ui/components/base/ChipSelector.kt b/app/src/main/java/com/bnyro/contacts/presentation/components/ChipSelector.kt similarity index 97% rename from app/src/main/java/com/bnyro/contacts/ui/components/base/ChipSelector.kt rename to app/src/main/java/com/bnyro/contacts/presentation/components/ChipSelector.kt index 6bc152cf..f75f5c1e 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/components/base/ChipSelector.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/components/ChipSelector.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.ui.components.base +package com.bnyro.contacts.presentation.components import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.height diff --git a/app/src/main/java/com/bnyro/contacts/ui/components/base/ClickableIcon.kt b/app/src/main/java/com/bnyro/contacts/presentation/components/ClickableIcon.kt similarity index 75% rename from app/src/main/java/com/bnyro/contacts/ui/components/base/ClickableIcon.kt rename to app/src/main/java/com/bnyro/contacts/presentation/components/ClickableIcon.kt index 74a30230..a3b54138 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/components/base/ClickableIcon.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/components/ClickableIcon.kt @@ -1,7 +1,15 @@ -package com.bnyro.contacts.ui.components.base +package com.bnyro.contacts.presentation.components import androidx.annotation.StringRes -import androidx.compose.material3.* +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.LocalContentColor +import androidx.compose.material3.PlainTooltip +import androidx.compose.material3.Text +import androidx.compose.material3.TooltipBox +import androidx.compose.material3.TooltipDefaults +import androidx.compose.material3.rememberTooltipState import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color diff --git a/app/src/main/java/com/bnyro/contacts/ui/components/ClickableText.kt b/app/src/main/java/com/bnyro/contacts/presentation/components/ClickableText.kt similarity index 93% rename from app/src/main/java/com/bnyro/contacts/ui/components/ClickableText.kt rename to app/src/main/java/com/bnyro/contacts/presentation/components/ClickableText.kt index d3325ed6..4e409b9e 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/components/ClickableText.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/components/ClickableText.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.ui.components +package com.bnyro.contacts.presentation.components import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.fillMaxWidth diff --git a/app/src/main/java/com/bnyro/contacts/ui/components/DialerButton.kt b/app/src/main/java/com/bnyro/contacts/presentation/components/DialerButton.kt similarity index 97% rename from app/src/main/java/com/bnyro/contacts/ui/components/DialerButton.kt rename to app/src/main/java/com/bnyro/contacts/presentation/components/DialerButton.kt index 2cca4268..bed4dd98 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/components/DialerButton.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/components/DialerButton.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.ui.components +package com.bnyro.contacts.presentation.components import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column diff --git a/app/src/main/java/com/bnyro/contacts/ui/components/base/ElevatedTextInputField.kt b/app/src/main/java/com/bnyro/contacts/presentation/components/ElevatedTextInputField.kt similarity index 91% rename from app/src/main/java/com/bnyro/contacts/ui/components/base/ElevatedTextInputField.kt rename to app/src/main/java/com/bnyro/contacts/presentation/components/ElevatedTextInputField.kt index 4dc14bd5..2adfd0da 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/components/base/ElevatedTextInputField.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/components/ElevatedTextInputField.kt @@ -1,7 +1,5 @@ -package com.bnyro.contacts.ui.components.base +package com.bnyro.contacts.presentation.components -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material.icons.Icons @@ -18,7 +16,6 @@ import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.text.input.ImeAction -import androidx.compose.ui.unit.dp @Composable fun ElevatedTextInputField( diff --git a/app/src/main/java/com/bnyro/contacts/ui/components/base/FullscreenDialog.kt b/app/src/main/java/com/bnyro/contacts/presentation/components/FullscreenDialog.kt similarity index 93% rename from app/src/main/java/com/bnyro/contacts/ui/components/base/FullscreenDialog.kt rename to app/src/main/java/com/bnyro/contacts/presentation/components/FullscreenDialog.kt index 0a3d295f..e47bae4f 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/components/base/FullscreenDialog.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/components/FullscreenDialog.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.ui.components.base +package com.bnyro.contacts.presentation.components import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material3.Surface diff --git a/app/src/main/java/com/bnyro/contacts/ui/components/base/LabeledTextField.kt b/app/src/main/java/com/bnyro/contacts/presentation/components/LabeledTextField.kt similarity index 98% rename from app/src/main/java/com/bnyro/contacts/ui/components/base/LabeledTextField.kt rename to app/src/main/java/com/bnyro/contacts/presentation/components/LabeledTextField.kt index 0410bebb..0191f269 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/components/base/LabeledTextField.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/components/LabeledTextField.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.ui.components.base +package com.bnyro.contacts.presentation.components import androidx.annotation.StringRes import androidx.compose.foundation.interaction.MutableInteractionSource diff --git a/app/src/main/java/com/bnyro/contacts/ui/components/base/LargeButtonWithIcon.kt b/app/src/main/java/com/bnyro/contacts/presentation/components/LargeButtonWithIcon.kt similarity index 96% rename from app/src/main/java/com/bnyro/contacts/ui/components/base/LargeButtonWithIcon.kt rename to app/src/main/java/com/bnyro/contacts/presentation/components/LargeButtonWithIcon.kt index 07b59cb5..94d8245b 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/components/base/LargeButtonWithIcon.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/components/LargeButtonWithIcon.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.ui.components.base +package com.bnyro.contacts.presentation.components import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer diff --git a/app/src/main/java/com/bnyro/contacts/ui/components/NothingHere.kt b/app/src/main/java/com/bnyro/contacts/presentation/components/NothingHere.kt similarity index 95% rename from app/src/main/java/com/bnyro/contacts/ui/components/NothingHere.kt rename to app/src/main/java/com/bnyro/contacts/presentation/components/NothingHere.kt index ab4f566c..0e5b0416 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/components/NothingHere.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/components/NothingHere.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.ui.components +package com.bnyro.contacts.presentation.components import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column diff --git a/app/src/main/java/com/bnyro/contacts/ui/components/NumberInput.kt b/app/src/main/java/com/bnyro/contacts/presentation/components/NumberInput.kt similarity index 99% rename from app/src/main/java/com/bnyro/contacts/ui/components/NumberInput.kt rename to app/src/main/java/com/bnyro/contacts/presentation/components/NumberInput.kt index 4965e122..a69f642b 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/components/NumberInput.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/components/NumberInput.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.ui.components +package com.bnyro.contacts.presentation.components import android.annotation.SuppressLint import android.telephony.SubscriptionInfo diff --git a/app/src/main/java/com/bnyro/contacts/ui/components/base/OptionMenu.kt b/app/src/main/java/com/bnyro/contacts/presentation/components/OptionMenu.kt similarity index 94% rename from app/src/main/java/com/bnyro/contacts/ui/components/base/OptionMenu.kt rename to app/src/main/java/com/bnyro/contacts/presentation/components/OptionMenu.kt index 4e0126d6..48bf1e41 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/components/base/OptionMenu.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/components/OptionMenu.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.ui.components.base +package com.bnyro.contacts.presentation.components import androidx.compose.material3.DropdownMenu import androidx.compose.material3.DropdownMenuItem diff --git a/app/src/main/java/com/bnyro/contacts/ui/components/base/SearchBar.kt b/app/src/main/java/com/bnyro/contacts/presentation/components/SearchBar.kt similarity index 98% rename from app/src/main/java/com/bnyro/contacts/ui/components/base/SearchBar.kt rename to app/src/main/java/com/bnyro/contacts/presentation/components/SearchBar.kt index 39fd3f77..271ab11d 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/components/base/SearchBar.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/components/SearchBar.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.ui.components.base +package com.bnyro.contacts.presentation.components import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box diff --git a/app/src/main/java/com/bnyro/contacts/ui/components/ShareOption.kt b/app/src/main/java/com/bnyro/contacts/presentation/components/ShareOption.kt similarity index 94% rename from app/src/main/java/com/bnyro/contacts/ui/components/ShareOption.kt rename to app/src/main/java/com/bnyro/contacts/presentation/components/ShareOption.kt index 5524d4ec..5a9b6214 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/components/ShareOption.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/components/ShareOption.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.ui.components +package com.bnyro.contacts.presentation.components import androidx.annotation.StringRes import androidx.compose.foundation.layout.Row diff --git a/app/src/main/java/com/bnyro/contacts/ui/components/base/SmallButtonWithIcon.kt b/app/src/main/java/com/bnyro/contacts/presentation/components/SmallButtonWithIcon.kt similarity index 87% rename from app/src/main/java/com/bnyro/contacts/ui/components/base/SmallButtonWithIcon.kt rename to app/src/main/java/com/bnyro/contacts/presentation/components/SmallButtonWithIcon.kt index 893bb264..a9bf8497 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/components/base/SmallButtonWithIcon.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/components/SmallButtonWithIcon.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.ui.components.base +package com.bnyro.contacts.presentation.components import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer @@ -30,7 +30,9 @@ fun SmallButtonWithIcon( verticalAlignment = Alignment.CenterVertically ) { Icon( - modifier = Modifier.size(16.dp).offset(x = (-4).dp), + modifier = Modifier + .size(16.dp) + .offset(x = (-4).dp), imageVector = imageVector, contentDescription = text ) diff --git a/app/src/main/java/com/bnyro/contacts/ui/components/TopBarMoreMenu.kt b/app/src/main/java/com/bnyro/contacts/presentation/components/TopBarMoreMenu.kt similarity index 85% rename from app/src/main/java/com/bnyro/contacts/ui/components/TopBarMoreMenu.kt rename to app/src/main/java/com/bnyro/contacts/presentation/components/TopBarMoreMenu.kt index 23649415..2e8be517 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/components/TopBarMoreMenu.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/components/TopBarMoreMenu.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.ui.components +package com.bnyro.contacts.presentation.components import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.MoreVert @@ -8,8 +8,6 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import com.bnyro.contacts.R -import com.bnyro.contacts.ui.components.base.ClickableIcon -import com.bnyro.contacts.ui.components.base.OptionMenu @Composable fun TopBarMoreMenu( diff --git a/app/src/main/java/com/bnyro/contacts/ui/components/base/ZoomableImage.kt b/app/src/main/java/com/bnyro/contacts/presentation/components/ZoomableImage.kt similarity index 97% rename from app/src/main/java/com/bnyro/contacts/ui/components/base/ZoomableImage.kt rename to app/src/main/java/com/bnyro/contacts/presentation/components/ZoomableImage.kt index 1258c2e3..5f88295c 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/components/base/ZoomableImage.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/components/ZoomableImage.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.ui.components.base +package com.bnyro.contacts.presentation.components import android.graphics.Bitmap import androidx.compose.foundation.Image diff --git a/app/src/main/java/com/bnyro/contacts/ui/components/shapes/CurlyCornerShape.kt b/app/src/main/java/com/bnyro/contacts/presentation/components/shapes/CurlyCornerShape.kt similarity index 97% rename from app/src/main/java/com/bnyro/contacts/ui/components/shapes/CurlyCornerShape.kt rename to app/src/main/java/com/bnyro/contacts/presentation/components/shapes/CurlyCornerShape.kt index 258a357d..5ef78881 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/components/shapes/CurlyCornerShape.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/components/shapes/CurlyCornerShape.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.ui.components.shapes +package com.bnyro.contacts.presentation.components.shapes import androidx.compose.foundation.shape.CornerBasedShape import androidx.compose.foundation.shape.CornerSize diff --git a/app/src/main/java/com/bnyro/contacts/ui/components/dialogs/AddToContactDialog.kt b/app/src/main/java/com/bnyro/contacts/presentation/features/AddToContactDialog.kt similarity index 91% rename from app/src/main/java/com/bnyro/contacts/ui/components/dialogs/AddToContactDialog.kt rename to app/src/main/java/com/bnyro/contacts/presentation/features/AddToContactDialog.kt index ad89b6bb..5adb20c7 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/components/dialogs/AddToContactDialog.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/features/AddToContactDialog.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.ui.components.dialogs +package com.bnyro.contacts.presentation.features import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.height @@ -20,11 +20,11 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel import com.bnyro.contacts.R -import com.bnyro.contacts.obj.ContactData -import com.bnyro.contacts.obj.ValueWithType -import com.bnyro.contacts.ui.components.ClickableText -import com.bnyro.contacts.ui.models.ContactsModel -import com.bnyro.contacts.ui.screens.EditorScreen +import com.bnyro.contacts.domain.model.ContactData +import com.bnyro.contacts.domain.model.ValueWithType +import com.bnyro.contacts.presentation.components.ClickableText +import com.bnyro.contacts.presentation.screens.contacts.model.ContactsModel +import com.bnyro.contacts.presentation.screens.editor.EditorScreen import kotlinx.coroutines.launch @Composable diff --git a/app/src/main/java/com/bnyro/contacts/ui/components/ConfirmImportContacts.kt b/app/src/main/java/com/bnyro/contacts/presentation/features/ConfirmImportContacts.kt similarity index 84% rename from app/src/main/java/com/bnyro/contacts/ui/components/ConfirmImportContacts.kt rename to app/src/main/java/com/bnyro/contacts/presentation/features/ConfirmImportContacts.kt index da30908b..5de130ad 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/components/ConfirmImportContacts.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/features/ConfirmImportContacts.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.ui.components +package com.bnyro.contacts.presentation.features import android.net.Uri import androidx.compose.runtime.Composable @@ -9,8 +9,7 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import com.bnyro.contacts.R -import com.bnyro.contacts.ui.components.dialogs.ConfirmationDialog -import com.bnyro.contacts.ui.models.ContactsModel +import com.bnyro.contacts.presentation.screens.contacts.model.ContactsModel @Composable fun ConfirmImportContactsDialog(contactsModel: ContactsModel, contactsUri: Uri) { diff --git a/app/src/main/java/com/bnyro/contacts/ui/components/dialogs/ConfirmationDialog.kt b/app/src/main/java/com/bnyro/contacts/presentation/features/ConfirmationDialog.kt similarity index 93% rename from app/src/main/java/com/bnyro/contacts/ui/components/dialogs/ConfirmationDialog.kt rename to app/src/main/java/com/bnyro/contacts/presentation/features/ConfirmationDialog.kt index e8039536..344d8ea3 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/components/dialogs/ConfirmationDialog.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/features/ConfirmationDialog.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.ui.components.dialogs +package com.bnyro.contacts.presentation.features import androidx.compose.material3.AlertDialog import androidx.compose.material3.Text diff --git a/app/src/main/java/com/bnyro/contacts/ui/components/dialogs/DialogButton.kt b/app/src/main/java/com/bnyro/contacts/presentation/features/DialogButton.kt similarity index 85% rename from app/src/main/java/com/bnyro/contacts/ui/components/dialogs/DialogButton.kt rename to app/src/main/java/com/bnyro/contacts/presentation/features/DialogButton.kt index a07fb4a9..8b288b26 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/components/dialogs/DialogButton.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/features/DialogButton.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.ui.components.dialogs +package com.bnyro.contacts.presentation.features import androidx.compose.material3.Text import androidx.compose.material3.TextButton diff --git a/app/src/main/java/com/bnyro/contacts/ui/components/dialogs/FilterDialog.kt b/app/src/main/java/com/bnyro/contacts/presentation/features/FilterDialog.kt similarity index 92% rename from app/src/main/java/com/bnyro/contacts/ui/components/dialogs/FilterDialog.kt rename to app/src/main/java/com/bnyro/contacts/presentation/features/FilterDialog.kt index 2be3a40f..0c864af8 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/components/dialogs/FilterDialog.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/features/FilterDialog.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.ui.components.dialogs +package com.bnyro.contacts.presentation.features import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer @@ -13,11 +13,11 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import com.bnyro.contacts.R -import com.bnyro.contacts.enums.SortOrder -import com.bnyro.contacts.obj.AccountType -import com.bnyro.contacts.obj.ContactsGroup -import com.bnyro.contacts.obj.FilterOptions -import com.bnyro.contacts.ui.components.base.ChipSelector +import com.bnyro.contacts.domain.enums.SortOrder +import com.bnyro.contacts.domain.model.AccountType +import com.bnyro.contacts.domain.model.ContactsGroup +import com.bnyro.contacts.domain.model.FilterOptions +import com.bnyro.contacts.presentation.components.ChipSelector @Composable fun FilterDialog( diff --git a/app/src/main/java/com/bnyro/contacts/ui/components/dialogs/GroupsDialog.kt b/app/src/main/java/com/bnyro/contacts/presentation/features/GroupsDialog.kt similarity index 95% rename from app/src/main/java/com/bnyro/contacts/ui/components/dialogs/GroupsDialog.kt rename to app/src/main/java/com/bnyro/contacts/presentation/features/GroupsDialog.kt index 9377ee30..7672d1da 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/components/dialogs/GroupsDialog.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/features/GroupsDialog.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.ui.components.dialogs +package com.bnyro.contacts.presentation.features import android.widget.Toast import androidx.compose.foundation.layout.Row @@ -25,9 +25,9 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel import com.bnyro.contacts.R -import com.bnyro.contacts.obj.ContactsGroup -import com.bnyro.contacts.ui.components.base.ClickableIcon -import com.bnyro.contacts.ui.models.ContactsModel +import com.bnyro.contacts.domain.model.ContactsGroup +import com.bnyro.contacts.presentation.components.ClickableIcon +import com.bnyro.contacts.presentation.screens.contacts.model.ContactsModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext diff --git a/app/src/main/java/com/bnyro/contacts/ui/components/NumberPickerDialog.kt b/app/src/main/java/com/bnyro/contacts/presentation/features/NumberPickerDialog.kt similarity index 90% rename from app/src/main/java/com/bnyro/contacts/ui/components/NumberPickerDialog.kt rename to app/src/main/java/com/bnyro/contacts/presentation/features/NumberPickerDialog.kt index 132622b0..84e35504 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/components/NumberPickerDialog.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/features/NumberPickerDialog.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.ui.components +package com.bnyro.contacts.presentation.features import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer @@ -22,11 +22,12 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import com.bnyro.contacts.R -import com.bnyro.contacts.enums.SortOrder -import com.bnyro.contacts.obj.ContactData -import com.bnyro.contacts.ui.components.base.ClickableIcon -import com.bnyro.contacts.ui.components.dialogs.DialogButton -import com.bnyro.contacts.ui.models.ContactsModel +import com.bnyro.contacts.domain.enums.SortOrder +import com.bnyro.contacts.domain.model.ContactData +import com.bnyro.contacts.presentation.components.ClickableIcon +import com.bnyro.contacts.presentation.components.ClickableText +import com.bnyro.contacts.presentation.screens.contacts.components.ContactItem +import com.bnyro.contacts.presentation.screens.contacts.model.ContactsModel @Composable fun NumberPickerDialog( diff --git a/app/src/main/java/com/bnyro/contacts/ui/components/ShareDialog.kt b/app/src/main/java/com/bnyro/contacts/presentation/features/ShareDialog.kt similarity index 91% rename from app/src/main/java/com/bnyro/contacts/ui/components/ShareDialog.kt rename to app/src/main/java/com/bnyro/contacts/presentation/features/ShareDialog.kt index 98c8073c..0e4dfe18 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/components/ShareDialog.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/features/ShareDialog.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.ui.components +package com.bnyro.contacts.presentation.features import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts @@ -14,11 +14,11 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.lifecycle.viewmodel.compose.viewModel import com.bnyro.contacts.R -import com.bnyro.contacts.enums.ListAttribute -import com.bnyro.contacts.enums.StringAttribute -import com.bnyro.contacts.obj.ContactData -import com.bnyro.contacts.ui.components.dialogs.DialogButton -import com.bnyro.contacts.ui.models.ContactsModel +import com.bnyro.contacts.domain.enums.ListAttribute +import com.bnyro.contacts.domain.enums.StringAttribute +import com.bnyro.contacts.domain.model.ContactData +import com.bnyro.contacts.presentation.components.ShareOption +import com.bnyro.contacts.presentation.screens.contacts.model.ContactsModel import com.bnyro.contacts.util.BackupHelper import com.bnyro.contacts.util.ContactsHelper diff --git a/app/src/main/java/com/bnyro/contacts/ui/components/dialogs/ShortcutDialog.kt b/app/src/main/java/com/bnyro/contacts/presentation/features/ShortcutDialog.kt similarity index 94% rename from app/src/main/java/com/bnyro/contacts/ui/components/dialogs/ShortcutDialog.kt rename to app/src/main/java/com/bnyro/contacts/presentation/features/ShortcutDialog.kt index 148b5f41..5880acb5 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/components/dialogs/ShortcutDialog.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/features/ShortcutDialog.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.ui.components.dialogs +package com.bnyro.contacts.presentation.features import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items @@ -12,9 +12,9 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import com.bnyro.contacts.R -import com.bnyro.contacts.enums.IntentActionType -import com.bnyro.contacts.obj.ContactData -import com.bnyro.contacts.ui.components.ClickableText +import com.bnyro.contacts.domain.enums.IntentActionType +import com.bnyro.contacts.domain.model.ContactData +import com.bnyro.contacts.presentation.components.ClickableText import com.bnyro.contacts.util.ShortcutHelper @Composable diff --git a/app/src/main/java/com/bnyro/contacts/ui/components/dialogs/SimImportDialog.kt b/app/src/main/java/com/bnyro/contacts/presentation/features/SimImportDialog.kt similarity index 96% rename from app/src/main/java/com/bnyro/contacts/ui/components/dialogs/SimImportDialog.kt rename to app/src/main/java/com/bnyro/contacts/presentation/features/SimImportDialog.kt index 2094c59e..95f8b6b6 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/components/dialogs/SimImportDialog.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/features/SimImportDialog.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.ui.components.dialogs +package com.bnyro.contacts.presentation.features import android.widget.Toast import androidx.compose.foundation.layout.Box @@ -28,8 +28,8 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel import com.bnyro.contacts.R -import com.bnyro.contacts.obj.ContactData -import com.bnyro.contacts.ui.models.ContactsModel +import com.bnyro.contacts.domain.model.ContactData +import com.bnyro.contacts.presentation.screens.contacts.model.ContactsModel import com.bnyro.contacts.util.SimContactsHelper import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext diff --git a/app/src/main/java/com/bnyro/contacts/ui/screens/AboutScreen.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/about/AboutScreen.kt similarity index 94% rename from app/src/main/java/com/bnyro/contacts/ui/screens/AboutScreen.kt rename to app/src/main/java/com/bnyro/contacts/presentation/screens/about/AboutScreen.kt index 3e40657d..5b1a8abe 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/screens/AboutScreen.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/about/AboutScreen.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.ui.screens +package com.bnyro.contacts.presentation.screens.about import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Column @@ -21,8 +21,8 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import com.bnyro.contacts.BuildConfig import com.bnyro.contacts.R -import com.bnyro.contacts.ui.components.about.AboutRow -import com.bnyro.contacts.ui.components.base.ClickableIcon +import com.bnyro.contacts.presentation.components.ClickableIcon +import com.bnyro.contacts.presentation.screens.about.components.AboutRow @OptIn(ExperimentalMaterial3Api::class) @Composable diff --git a/app/src/main/java/com/bnyro/contacts/ui/components/about/AboutRow.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/about/components/AboutRow.kt similarity index 96% rename from app/src/main/java/com/bnyro/contacts/ui/components/about/AboutRow.kt rename to app/src/main/java/com/bnyro/contacts/presentation/screens/about/components/AboutRow.kt index daa4753d..e698c81c 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/components/about/AboutRow.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/about/components/AboutRow.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.ui.components.about +package com.bnyro.contacts.presentation.screens.about.components import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.combinedClickable diff --git a/app/src/main/java/com/bnyro/contacts/ui/screens/CallLogsScreen.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/CallLogsScreen.kt similarity index 90% rename from app/src/main/java/com/bnyro/contacts/ui/screens/CallLogsScreen.kt rename to app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/CallLogsScreen.kt index 8ca2caae..f53d77ae 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/screens/CallLogsScreen.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/CallLogsScreen.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.ui.screens +package com.bnyro.contacts.presentation.screens.calllog import android.content.ComponentName import android.content.Intent @@ -39,19 +39,19 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import com.bnyro.contacts.R -import com.bnyro.contacts.ext.removeLastChar -import com.bnyro.contacts.obj.CallLogEntry -import com.bnyro.contacts.ui.components.ContactIconPlaceholder -import com.bnyro.contacts.ui.components.NothingHere -import com.bnyro.contacts.ui.components.NumberInput -import com.bnyro.contacts.ui.components.PhoneNumberDisplay -import com.bnyro.contacts.ui.components.dialogs.ConfirmationDialog -import com.bnyro.contacts.ui.models.ContactsModel -import com.bnyro.contacts.ui.models.DialerModel -import com.bnyro.contacts.ui.models.ThemeModel +import com.bnyro.contacts.domain.model.CallLogEntry +import com.bnyro.contacts.presentation.components.NothingHere +import com.bnyro.contacts.presentation.components.NumberInput +import com.bnyro.contacts.presentation.components.PhoneNumberDisplay +import com.bnyro.contacts.presentation.features.ConfirmationDialog +import com.bnyro.contacts.presentation.screens.contacts.model.ContactsModel +import com.bnyro.contacts.presentation.screens.dialer.model.DialerModel +import com.bnyro.contacts.presentation.screens.editor.components.ContactIconPlaceholder +import com.bnyro.contacts.presentation.screens.settings.model.ThemeModel import com.bnyro.contacts.util.CallLogHelper import com.bnyro.contacts.util.PermissionHelper import com.bnyro.contacts.util.SmsUtil +import com.bnyro.contacts.util.extension.removeLastChar @OptIn(ExperimentalMaterial3Api::class) @Composable diff --git a/app/src/main/java/com/bnyro/contacts/ui/screens/SingleContactScreen.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/contact/SingleContactScreen.kt similarity index 89% rename from app/src/main/java/com/bnyro/contacts/ui/screens/SingleContactScreen.kt rename to app/src/main/java/com/bnyro/contacts/presentation/screens/contact/SingleContactScreen.kt index 5db01eaa..36b01716 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/screens/SingleContactScreen.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/contact/SingleContactScreen.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.ui.screens +package com.bnyro.contacts.presentation.screens.contact import androidx.compose.foundation.Image import androidx.compose.foundation.background @@ -46,23 +46,24 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.lifecycle.viewmodel.compose.viewModel import com.bnyro.contacts.R -import com.bnyro.contacts.enums.IntentActionType -import com.bnyro.contacts.enums.ListAttribute -import com.bnyro.contacts.enums.Notes -import com.bnyro.contacts.enums.StringAttribute -import com.bnyro.contacts.obj.ContactData -import com.bnyro.contacts.ui.components.ContactEntryGroup -import com.bnyro.contacts.ui.components.ContactEntryTextGroup -import com.bnyro.contacts.ui.components.ContactProfilePicture -import com.bnyro.contacts.ui.components.ShareDialog -import com.bnyro.contacts.ui.components.base.ClickableIcon -import com.bnyro.contacts.ui.components.base.FullScreenDialog -import com.bnyro.contacts.ui.components.base.LargeButtonWithIcon -import com.bnyro.contacts.ui.components.base.SmallButtonWithIcon -import com.bnyro.contacts.ui.components.dialogs.ConfirmationDialog -import com.bnyro.contacts.ui.components.dialogs.ShortcutDialog -import com.bnyro.contacts.ui.components.shapes.curlyCornerShape -import com.bnyro.contacts.ui.models.ContactsModel +import com.bnyro.contacts.domain.enums.IntentActionType +import com.bnyro.contacts.domain.enums.ListAttribute +import com.bnyro.contacts.domain.enums.Notes +import com.bnyro.contacts.domain.enums.StringAttribute +import com.bnyro.contacts.domain.model.ContactData +import com.bnyro.contacts.presentation.components.ClickableIcon +import com.bnyro.contacts.presentation.components.FullScreenDialog +import com.bnyro.contacts.presentation.components.LargeButtonWithIcon +import com.bnyro.contacts.presentation.components.SmallButtonWithIcon +import com.bnyro.contacts.presentation.components.shapes.curlyCornerShape +import com.bnyro.contacts.presentation.features.ConfirmationDialog +import com.bnyro.contacts.presentation.features.ShareDialog +import com.bnyro.contacts.presentation.features.ShortcutDialog +import com.bnyro.contacts.presentation.screens.contact.components.ContactProfilePicture +import com.bnyro.contacts.presentation.screens.contacts.model.ContactsModel +import com.bnyro.contacts.presentation.screens.editor.EditorScreen +import com.bnyro.contacts.presentation.screens.editor.components.ContactEntryGroup +import com.bnyro.contacts.presentation.screens.editor.components.ContactEntryTextGroup import com.bnyro.contacts.util.ContactsHelper import com.bnyro.contacts.util.IntentHelper diff --git a/app/src/main/java/com/bnyro/contacts/ui/components/ContactProfilePicture.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/contact/components/ContactProfilePicture.kt similarity index 81% rename from app/src/main/java/com/bnyro/contacts/ui/components/ContactProfilePicture.kt rename to app/src/main/java/com/bnyro/contacts/presentation/screens/contact/components/ContactProfilePicture.kt index cb4e0801..b6cffb41 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/components/ContactProfilePicture.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/contact/components/ContactProfilePicture.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.ui.components +package com.bnyro.contacts.presentation.screens.contact.components import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons @@ -10,10 +10,10 @@ import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import com.bnyro.contacts.R -import com.bnyro.contacts.obj.ContactData -import com.bnyro.contacts.ui.components.base.ClickableIcon -import com.bnyro.contacts.ui.components.base.FullScreenDialog -import com.bnyro.contacts.ui.components.base.ZoomableImage +import com.bnyro.contacts.domain.model.ContactData +import com.bnyro.contacts.presentation.components.ClickableIcon +import com.bnyro.contacts.presentation.components.FullScreenDialog +import com.bnyro.contacts.presentation.components.ZoomableImage @OptIn(ExperimentalMaterial3Api::class) @Composable diff --git a/app/src/main/java/com/bnyro/contacts/ui/screens/ContactsScreen.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/contacts/ContactsScreen.kt similarity index 93% rename from app/src/main/java/com/bnyro/contacts/ui/screens/ContactsScreen.kt rename to app/src/main/java/com/bnyro/contacts/presentation/screens/contacts/ContactsScreen.kt index e9b042ea..04543b0c 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/screens/ContactsScreen.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/contacts/ContactsScreen.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.ui.screens +package com.bnyro.contacts.presentation.screens.contacts import androidx.activity.compose.BackHandler import androidx.activity.compose.rememberLauncherForActivityResult @@ -46,19 +46,21 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel import com.bnyro.contacts.R -import com.bnyro.contacts.enums.ContactsSource -import com.bnyro.contacts.nav.NavRoutes -import com.bnyro.contacts.obj.ContactData -import com.bnyro.contacts.obj.FilterOptions -import com.bnyro.contacts.ui.components.ContactsList -import com.bnyro.contacts.ui.components.NothingHere -import com.bnyro.contacts.ui.components.TopBarMoreMenu -import com.bnyro.contacts.ui.components.base.ClickableIcon -import com.bnyro.contacts.ui.components.dialogs.ConfirmationDialog -import com.bnyro.contacts.ui.components.dialogs.FilterDialog -import com.bnyro.contacts.ui.components.dialogs.SimImportDialog -import com.bnyro.contacts.ui.models.ContactsModel -import com.bnyro.contacts.ui.models.state.ContactListState +import com.bnyro.contacts.domain.enums.ContactsSource +import com.bnyro.contacts.domain.model.ContactData +import com.bnyro.contacts.domain.model.FilterOptions +import com.bnyro.contacts.navigation.NavRoutes +import com.bnyro.contacts.presentation.components.ClickableIcon +import com.bnyro.contacts.presentation.components.NothingHere +import com.bnyro.contacts.presentation.components.TopBarMoreMenu +import com.bnyro.contacts.presentation.features.ConfirmationDialog +import com.bnyro.contacts.presentation.features.FilterDialog +import com.bnyro.contacts.presentation.features.SimImportDialog +import com.bnyro.contacts.presentation.screens.contacts.components.ContactSearchScreen +import com.bnyro.contacts.presentation.screens.contacts.components.ContactsList +import com.bnyro.contacts.presentation.screens.contacts.model.ContactsModel +import com.bnyro.contacts.presentation.screens.contacts.model.state.ContactListState +import com.bnyro.contacts.presentation.screens.editor.EditorScreen import com.bnyro.contacts.util.BackupHelper import com.bnyro.contacts.util.Preferences diff --git a/app/src/main/java/com/bnyro/contacts/ui/components/ContactItem.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/contacts/components/ContactItem.kt similarity index 90% rename from app/src/main/java/com/bnyro/contacts/ui/components/ContactItem.kt rename to app/src/main/java/com/bnyro/contacts/presentation/screens/contacts/components/ContactItem.kt index 9a275692..23a6279a 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/components/ContactItem.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/contacts/components/ContactItem.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.ui.components +package com.bnyro.contacts.presentation.screens.contacts.components import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.Image @@ -32,11 +32,12 @@ import androidx.compose.ui.graphics.asImageBitmap import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel -import com.bnyro.contacts.enums.SortOrder -import com.bnyro.contacts.obj.ContactData -import com.bnyro.contacts.ui.models.ContactsModel -import com.bnyro.contacts.ui.models.ThemeModel -import com.bnyro.contacts.ui.screens.SingleContactScreen +import com.bnyro.contacts.domain.enums.SortOrder +import com.bnyro.contacts.domain.model.ContactData +import com.bnyro.contacts.presentation.screens.contact.SingleContactScreen +import com.bnyro.contacts.presentation.screens.contacts.model.ContactsModel +import com.bnyro.contacts.presentation.screens.editor.components.ContactIconPlaceholder +import com.bnyro.contacts.presentation.screens.settings.model.ThemeModel import kotlinx.coroutines.runBlocking @OptIn(ExperimentalFoundationApi::class) diff --git a/app/src/main/java/com/bnyro/contacts/ui/screens/ContactSearchScreen.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/contacts/components/ContactSearchScreen.kt similarity index 89% rename from app/src/main/java/com/bnyro/contacts/ui/screens/ContactSearchScreen.kt rename to app/src/main/java/com/bnyro/contacts/presentation/screens/contacts/components/ContactSearchScreen.kt index b9d154f9..7c01e61c 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/screens/ContactSearchScreen.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/contacts/components/ContactSearchScreen.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.ui.screens +package com.bnyro.contacts.presentation.screens.contacts.components import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize @@ -18,11 +18,10 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.unit.dp import com.bnyro.contacts.R -import com.bnyro.contacts.obj.ContactData -import com.bnyro.contacts.obj.FilterOptions -import com.bnyro.contacts.ui.components.ContactsList -import com.bnyro.contacts.ui.components.base.ElevatedTextInputField -import com.bnyro.contacts.ui.components.base.FullScreenDialog +import com.bnyro.contacts.domain.model.ContactData +import com.bnyro.contacts.domain.model.FilterOptions +import com.bnyro.contacts.presentation.components.ElevatedTextInputField +import com.bnyro.contacts.presentation.components.FullScreenDialog import com.bnyro.contacts.util.ContactsHelper import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay diff --git a/app/src/main/java/com/bnyro/contacts/ui/components/ContactsList.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/contacts/components/ContactsList.kt similarity index 93% rename from app/src/main/java/com/bnyro/contacts/ui/components/ContactsList.kt rename to app/src/main/java/com/bnyro/contacts/presentation/screens/contacts/components/ContactsList.kt index 86fd3d88..450a0b3e 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/components/ContactsList.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/contacts/components/ContactsList.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.ui.components +package com.bnyro.contacts.presentation.screens.contacts.components import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.layout.Spacer @@ -14,8 +14,9 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.input.nestedscroll.NestedScrollConnection import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.unit.dp -import com.bnyro.contacts.obj.ContactData -import com.bnyro.contacts.obj.FilterOptions +import com.bnyro.contacts.domain.model.ContactData +import com.bnyro.contacts.domain.model.FilterOptions +import com.bnyro.contacts.presentation.components.CharacterHeader import my.nanihadesuka.compose.LazyColumnScrollbar @OptIn(ExperimentalFoundationApi::class) diff --git a/app/src/main/java/com/bnyro/contacts/ui/models/ContactsModel.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/contacts/model/ContactsModel.kt similarity index 93% rename from app/src/main/java/com/bnyro/contacts/ui/models/ContactsModel.kt rename to app/src/main/java/com/bnyro/contacts/presentation/screens/contacts/model/ContactsModel.kt index e10dff10..ee6b97a9 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/models/ContactsModel.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/contacts/model/ContactsModel.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.ui.models +package com.bnyro.contacts.presentation.screens.contacts.model import android.Manifest import android.annotation.SuppressLint @@ -14,19 +14,19 @@ import androidx.lifecycle.viewmodel.initializer import androidx.lifecycle.viewmodel.viewModelFactory import com.bnyro.contacts.App import com.bnyro.contacts.R -import com.bnyro.contacts.enums.ContactsSource -import com.bnyro.contacts.ext.toast -import com.bnyro.contacts.obj.AccountType -import com.bnyro.contacts.obj.ContactData -import com.bnyro.contacts.repo.ContactsRepository -import com.bnyro.contacts.repo.DeviceContactsRepository -import com.bnyro.contacts.repo.LocalContactsRepository -import com.bnyro.contacts.ui.models.state.ContactListState +import com.bnyro.contacts.domain.enums.ContactsSource +import com.bnyro.contacts.domain.model.AccountType +import com.bnyro.contacts.domain.model.ContactData +import com.bnyro.contacts.domain.repositories.ContactsRepository +import com.bnyro.contacts.domain.repositories.DeviceContactsRepository +import com.bnyro.contacts.domain.repositories.LocalContactsRepository +import com.bnyro.contacts.presentation.screens.contacts.model.state.ContactListState import com.bnyro.contacts.util.ContactsHelper import com.bnyro.contacts.util.ExportHelper import com.bnyro.contacts.util.IntentHelper import com.bnyro.contacts.util.PermissionHelper import com.bnyro.contacts.util.Preferences +import com.bnyro.contacts.util.extension.toast import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll diff --git a/app/src/main/java/com/bnyro/contacts/ui/models/state/ContactListState.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/contacts/model/state/ContactListState.kt similarity index 65% rename from app/src/main/java/com/bnyro/contacts/ui/models/state/ContactListState.kt rename to app/src/main/java/com/bnyro/contacts/presentation/screens/contacts/model/state/ContactListState.kt index f6701d02..0677b3dc 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/models/state/ContactListState.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/contacts/model/state/ContactListState.kt @@ -1,6 +1,6 @@ -package com.bnyro.contacts.ui.models.state +package com.bnyro.contacts.presentation.screens.contacts.model.state -import com.bnyro.contacts.obj.ContactData +import com.bnyro.contacts.domain.model.ContactData sealed interface ContactListState { object Loading : ContactListState diff --git a/app/src/main/java/com/bnyro/contacts/ui/screens/DialerScreen.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/dialer/DialerScreen.kt similarity index 94% rename from app/src/main/java/com/bnyro/contacts/ui/screens/DialerScreen.kt rename to app/src/main/java/com/bnyro/contacts/presentation/screens/dialer/DialerScreen.kt index aa83559e..e978d3be 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/screens/DialerScreen.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/dialer/DialerScreen.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.ui.screens +package com.bnyro.contacts.presentation.screens.dialer import android.os.Handler import android.os.Looper @@ -43,11 +43,11 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.bnyro.contacts.R -import com.bnyro.contacts.ui.components.DialerButton -import com.bnyro.contacts.ui.components.NumberInput -import com.bnyro.contacts.ui.components.PhoneNumberOnlyDisplay -import com.bnyro.contacts.ui.models.ContactsModel -import com.bnyro.contacts.ui.models.DialerModel +import com.bnyro.contacts.presentation.components.DialerButton +import com.bnyro.contacts.presentation.components.NumberInput +import com.bnyro.contacts.presentation.components.PhoneNumberOnlyDisplay +import com.bnyro.contacts.presentation.screens.contacts.model.ContactsModel +import com.bnyro.contacts.presentation.screens.dialer.model.DialerModel import com.bnyro.contacts.util.CallManager @OptIn(ExperimentalMaterial3Api::class) diff --git a/app/src/main/java/com/bnyro/contacts/ui/models/DialerModel.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/dialer/model/DialerModel.kt similarity index 97% rename from app/src/main/java/com/bnyro/contacts/ui/models/DialerModel.kt rename to app/src/main/java/com/bnyro/contacts/presentation/screens/dialer/model/DialerModel.kt index 8cd4380a..02f86c54 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/models/DialerModel.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/dialer/model/DialerModel.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.ui.models +package com.bnyro.contacts.presentation.screens.dialer.model import android.Manifest import android.content.Context diff --git a/app/src/main/java/com/bnyro/contacts/ui/screens/EditorScreen.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/editor/EditorScreen.kt similarity index 90% rename from app/src/main/java/com/bnyro/contacts/ui/screens/EditorScreen.kt rename to app/src/main/java/com/bnyro/contacts/presentation/screens/editor/EditorScreen.kt index 685b9391..31d84171 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/screens/EditorScreen.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/editor/EditorScreen.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.ui.screens +package com.bnyro.contacts.presentation.screens.editor import android.view.ViewTreeObserver import android.widget.Toast @@ -17,9 +17,9 @@ import androidx.compose.ui.platform.LocalView import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat import com.bnyro.contacts.R -import com.bnyro.contacts.obj.ContactData -import com.bnyro.contacts.ui.components.ContactEditor -import com.bnyro.contacts.ui.components.base.FullScreenDialog +import com.bnyro.contacts.domain.model.ContactData +import com.bnyro.contacts.presentation.components.FullScreenDialog +import com.bnyro.contacts.presentation.screens.editor.components.ContactEditor import com.bnyro.contacts.util.ContactsHelper @Composable diff --git a/app/src/main/java/com/bnyro/contacts/ui/components/ContactEditor.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/editor/components/ContactEditor.kt similarity index 97% rename from app/src/main/java/com/bnyro/contacts/ui/components/ContactEditor.kt rename to app/src/main/java/com/bnyro/contacts/presentation/screens/editor/components/ContactEditor.kt index 7c3afaf8..4a6f89c3 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/components/ContactEditor.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/editor/components/ContactEditor.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.ui.components +package com.bnyro.contacts.presentation.screens.editor.components import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.PickVisualMediaRequest @@ -64,14 +64,12 @@ import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel import com.bnyro.contacts.R -import com.bnyro.contacts.obj.AccountType -import com.bnyro.contacts.obj.ContactData -import com.bnyro.contacts.obj.ValueWithType -import com.bnyro.contacts.ui.components.base.LabeledTextField -import com.bnyro.contacts.ui.components.dialogs.GroupsDialog -import com.bnyro.contacts.ui.components.editor.EventFieldGroup -import com.bnyro.contacts.ui.components.editor.TextFieldGroup -import com.bnyro.contacts.ui.models.ContactsModel +import com.bnyro.contacts.domain.model.AccountType +import com.bnyro.contacts.domain.model.ContactData +import com.bnyro.contacts.domain.model.ValueWithType +import com.bnyro.contacts.presentation.components.LabeledTextField +import com.bnyro.contacts.presentation.features.GroupsDialog +import com.bnyro.contacts.presentation.screens.contacts.model.ContactsModel import com.bnyro.contacts.util.ContactsHelper import com.bnyro.contacts.util.ImageHelper import com.bnyro.contacts.util.Preferences diff --git a/app/src/main/java/com/bnyro/contacts/ui/components/ContactEntry.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/editor/components/ContactEntry.kt similarity index 97% rename from app/src/main/java/com/bnyro/contacts/ui/components/ContactEntry.kt rename to app/src/main/java/com/bnyro/contacts/presentation/screens/editor/components/ContactEntry.kt index 394d29a8..d2eb1b2f 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/components/ContactEntry.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/editor/components/ContactEntry.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.ui.components +package com.bnyro.contacts.presentation.screens.editor.components import androidx.annotation.StringRes import androidx.compose.foundation.ExperimentalFoundationApi diff --git a/app/src/main/java/com/bnyro/contacts/ui/components/ContactEntryGroup.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/editor/components/ContactEntryGroup.kt similarity index 90% rename from app/src/main/java/com/bnyro/contacts/ui/components/ContactEntryGroup.kt rename to app/src/main/java/com/bnyro/contacts/presentation/screens/editor/components/ContactEntryGroup.kt index 1fb5a346..42a043a3 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/components/ContactEntryGroup.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/editor/components/ContactEntryGroup.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.ui.components +package com.bnyro.contacts.presentation.screens.editor.components import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding @@ -8,8 +8,8 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp -import com.bnyro.contacts.obj.TranslatedType -import com.bnyro.contacts.obj.ValueWithType +import com.bnyro.contacts.domain.model.TranslatedType +import com.bnyro.contacts.domain.model.ValueWithType @Composable fun ContactEntryGroup( diff --git a/app/src/main/java/com/bnyro/contacts/ui/components/ContactIconPlaceholder.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/editor/components/ContactIconPlaceholder.kt similarity index 88% rename from app/src/main/java/com/bnyro/contacts/ui/components/ContactIconPlaceholder.kt rename to app/src/main/java/com/bnyro/contacts/presentation/screens/editor/components/ContactIconPlaceholder.kt index accd6f11..67d61e95 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/components/ContactIconPlaceholder.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/editor/components/ContactIconPlaceholder.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.ui.components +package com.bnyro.contacts.presentation.screens.editor.components import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box @@ -13,9 +13,9 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp -import com.bnyro.contacts.ext.contentColor -import com.bnyro.contacts.ui.models.ThemeModel +import com.bnyro.contacts.presentation.screens.settings.model.ThemeModel import com.bnyro.contacts.util.ColorUtils +import com.bnyro.contacts.util.extension.contentColor @Composable fun ContactIconPlaceholder( diff --git a/app/src/main/java/com/bnyro/contacts/ui/components/editor/DatePickerEditor.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/editor/components/DatePickerEditor.kt similarity index 96% rename from app/src/main/java/com/bnyro/contacts/ui/components/editor/DatePickerEditor.kt rename to app/src/main/java/com/bnyro/contacts/presentation/screens/editor/components/DatePickerEditor.kt index 0a4433a6..443d4b26 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/components/editor/DatePickerEditor.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/editor/components/DatePickerEditor.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.ui.components.editor +package com.bnyro.contacts.presentation.screens.editor.components import androidx.annotation.StringRes import androidx.compose.foundation.background @@ -40,9 +40,9 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.bnyro.contacts.R -import com.bnyro.contacts.obj.TranslatedType -import com.bnyro.contacts.obj.ValueWithType -import com.bnyro.contacts.ui.components.dialogs.DialogButton +import com.bnyro.contacts.domain.model.TranslatedType +import com.bnyro.contacts.domain.model.ValueWithType +import com.bnyro.contacts.presentation.features.DialogButton import com.bnyro.contacts.util.CalendarUtils import java.util.TimeZone diff --git a/app/src/main/java/com/bnyro/contacts/ui/components/editor/EventFieldGroup.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/editor/components/EventFieldGroup.kt similarity index 93% rename from app/src/main/java/com/bnyro/contacts/ui/components/editor/EventFieldGroup.kt rename to app/src/main/java/com/bnyro/contacts/presentation/screens/editor/components/EventFieldGroup.kt index 63a0be22..dac61bd3 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/components/editor/EventFieldGroup.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/editor/components/EventFieldGroup.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.ui.components.editor +package com.bnyro.contacts.presentation.screens.editor.components import androidx.annotation.StringRes import androidx.compose.foundation.layout.Column @@ -19,8 +19,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.RectangleShape import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp -import com.bnyro.contacts.obj.TranslatedType -import com.bnyro.contacts.obj.ValueWithType +import com.bnyro.contacts.domain.model.TranslatedType +import com.bnyro.contacts.domain.model.ValueWithType /** * A composable function that creates a group of text fields. diff --git a/app/src/main/java/com/bnyro/contacts/ui/components/editor/TextFieldEditor.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/editor/components/TextFieldEditor.kt similarity index 94% rename from app/src/main/java/com/bnyro/contacts/ui/components/editor/TextFieldEditor.kt rename to app/src/main/java/com/bnyro/contacts/presentation/screens/editor/components/TextFieldEditor.kt index a34a68f3..a6064cad 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/components/editor/TextFieldEditor.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/editor/components/TextFieldEditor.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.ui.components.editor +package com.bnyro.contacts.presentation.screens.editor.components import androidx.annotation.StringRes import androidx.compose.foundation.clickable @@ -25,9 +25,9 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType import com.bnyro.contacts.R -import com.bnyro.contacts.obj.TranslatedType -import com.bnyro.contacts.obj.ValueWithType -import com.bnyro.contacts.ui.components.base.LabeledTextField +import com.bnyro.contacts.domain.model.TranslatedType +import com.bnyro.contacts.domain.model.ValueWithType +import com.bnyro.contacts.presentation.components.LabeledTextField /** * A composable function that renders a text field with a dropdown menu to select the type and a button to delete the text field. diff --git a/app/src/main/java/com/bnyro/contacts/ui/components/editor/TextFieldGroup.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/editor/components/TextFieldGroup.kt similarity index 94% rename from app/src/main/java/com/bnyro/contacts/ui/components/editor/TextFieldGroup.kt rename to app/src/main/java/com/bnyro/contacts/presentation/screens/editor/components/TextFieldGroup.kt index 140d5a62..349194e8 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/components/editor/TextFieldGroup.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/editor/components/TextFieldGroup.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.ui.components.editor +package com.bnyro.contacts.presentation.screens.editor.components import androidx.annotation.StringRes import androidx.compose.foundation.layout.Column @@ -22,8 +22,8 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.unit.dp -import com.bnyro.contacts.obj.TranslatedType -import com.bnyro.contacts.obj.ValueWithType +import com.bnyro.contacts.domain.model.TranslatedType +import com.bnyro.contacts.domain.model.ValueWithType /** * A composable function that creates a group of text fields. diff --git a/app/src/main/java/com/bnyro/contacts/ui/screens/SettingsScreen.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/settings/SettingsScreen.kt similarity index 87% rename from app/src/main/java/com/bnyro/contacts/ui/screens/SettingsScreen.kt rename to app/src/main/java/com/bnyro/contacts/presentation/screens/settings/SettingsScreen.kt index 26748700..70509b1d 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/screens/SettingsScreen.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/settings/SettingsScreen.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.ui.screens +package com.bnyro.contacts.presentation.screens.settings import android.os.Build import androidx.compose.foundation.layout.Column @@ -26,16 +26,16 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import com.bnyro.contacts.R -import com.bnyro.contacts.enums.ThemeMode -import com.bnyro.contacts.ui.components.base.ClickableIcon -import com.bnyro.contacts.ui.components.prefs.AutoBackupPref -import com.bnyro.contacts.ui.components.prefs.BlockPreference -import com.bnyro.contacts.ui.components.prefs.EncryptBackupsPref -import com.bnyro.contacts.ui.components.prefs.SettingsCategory -import com.bnyro.contacts.ui.components.prefs.SwitchPref -import com.bnyro.contacts.ui.components.prefs.SwitchPrefBase -import com.bnyro.contacts.ui.models.SmsModel -import com.bnyro.contacts.ui.models.ThemeModel +import com.bnyro.contacts.domain.enums.ThemeMode +import com.bnyro.contacts.presentation.components.ClickableIcon +import com.bnyro.contacts.presentation.screens.settings.components.AutoBackupPref +import com.bnyro.contacts.presentation.screens.settings.components.BlockPreference +import com.bnyro.contacts.presentation.screens.settings.components.EncryptBackupsPref +import com.bnyro.contacts.presentation.screens.settings.components.SettingsCategory +import com.bnyro.contacts.presentation.screens.settings.components.SwitchPref +import com.bnyro.contacts.presentation.screens.settings.components.SwitchPrefBase +import com.bnyro.contacts.presentation.screens.settings.model.ThemeModel +import com.bnyro.contacts.presentation.screens.sms.model.SmsModel import com.bnyro.contacts.util.BiometricAuthUtil import com.bnyro.contacts.util.Preferences diff --git a/app/src/main/java/com/bnyro/contacts/ui/components/prefs/BackupPref.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/settings/components/BackupPref.kt similarity index 93% rename from app/src/main/java/com/bnyro/contacts/ui/components/prefs/BackupPref.kt rename to app/src/main/java/com/bnyro/contacts/presentation/screens/settings/components/BackupPref.kt index 2ec8cc7b..094a624a 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/components/prefs/BackupPref.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/settings/components/BackupPref.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.ui.components.prefs +package com.bnyro.contacts.presentation.screens.settings.components import android.content.Intent import androidx.activity.compose.rememberLauncherForActivityResult @@ -13,10 +13,10 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import com.bnyro.contacts.R -import com.bnyro.contacts.enums.BackupType +import com.bnyro.contacts.domain.enums.BackupType import com.bnyro.contacts.util.PickFolderContract import com.bnyro.contacts.util.Preferences -import com.bnyro.contacts.workers.BackupWorker +import com.bnyro.contacts.util.workers.BackupWorker @Composable fun AutoBackupPref() { diff --git a/app/src/main/java/com/bnyro/contacts/ui/components/prefs/BlockPreference.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/settings/components/BlockPreference.kt similarity index 96% rename from app/src/main/java/com/bnyro/contacts/ui/components/prefs/BlockPreference.kt rename to app/src/main/java/com/bnyro/contacts/presentation/screens/settings/components/BlockPreference.kt index 9b4a90d4..32ad6924 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/components/prefs/BlockPreference.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/settings/components/BlockPreference.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.ui.components.prefs +package com.bnyro.contacts.presentation.screens.settings.components import androidx.compose.foundation.horizontalScroll import androidx.compose.foundation.layout.fillMaxWidth diff --git a/app/src/main/java/com/bnyro/contacts/ui/components/prefs/EncryptBackupsPref.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/settings/components/EncryptBackupsPref.kt similarity index 95% rename from app/src/main/java/com/bnyro/contacts/ui/components/prefs/EncryptBackupsPref.kt rename to app/src/main/java/com/bnyro/contacts/presentation/screens/settings/components/EncryptBackupsPref.kt index f4f9a101..ee9a3552 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/components/prefs/EncryptBackupsPref.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/settings/components/EncryptBackupsPref.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.ui.components.prefs +package com.bnyro.contacts.presentation.screens.settings.components import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.layout.padding @@ -22,8 +22,8 @@ import androidx.compose.ui.text.input.PasswordVisualTransformation import androidx.compose.ui.text.input.VisualTransformation import androidx.compose.ui.unit.dp import com.bnyro.contacts.R -import com.bnyro.contacts.ui.components.base.ClickableIcon -import com.bnyro.contacts.ui.components.dialogs.DialogButton +import com.bnyro.contacts.presentation.components.ClickableIcon +import com.bnyro.contacts.presentation.features.DialogButton import com.bnyro.contacts.util.BackupHelper import com.bnyro.contacts.util.PasswordUtils import com.bnyro.contacts.util.Preferences diff --git a/app/src/main/java/com/bnyro/contacts/ui/components/prefs/ListPreference.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/settings/components/ListPreference.kt similarity index 91% rename from app/src/main/java/com/bnyro/contacts/ui/components/prefs/ListPreference.kt rename to app/src/main/java/com/bnyro/contacts/presentation/screens/settings/components/ListPreference.kt index 490aa381..ca0b8984 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/components/prefs/ListPreference.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/settings/components/ListPreference.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.ui.components.prefs +package com.bnyro.contacts.presentation.screens.settings.components import androidx.annotation.StringRes import androidx.compose.foundation.layout.padding @@ -16,8 +16,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import com.bnyro.contacts.R -import com.bnyro.contacts.ui.components.ClickableText -import com.bnyro.contacts.ui.components.dialogs.DialogButton +import com.bnyro.contacts.presentation.components.ClickableText +import com.bnyro.contacts.presentation.features.DialogButton import com.bnyro.contacts.util.rememberPreference @Composable diff --git a/app/src/main/java/com/bnyro/contacts/ui/components/prefs/SettingsCategory.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/settings/components/SettingsCategory.kt similarity index 89% rename from app/src/main/java/com/bnyro/contacts/ui/components/prefs/SettingsCategory.kt rename to app/src/main/java/com/bnyro/contacts/presentation/screens/settings/components/SettingsCategory.kt index 0d7775a5..a22d84ad 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/components/prefs/SettingsCategory.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/settings/components/SettingsCategory.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.ui.components.prefs +package com.bnyro.contacts.presentation.screens.settings.components import androidx.compose.foundation.layout.padding import androidx.compose.material3.MaterialTheme diff --git a/app/src/main/java/com/bnyro/contacts/ui/components/prefs/SwitchPref.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/settings/components/SwitchPref.kt similarity index 97% rename from app/src/main/java/com/bnyro/contacts/ui/components/prefs/SwitchPref.kt rename to app/src/main/java/com/bnyro/contacts/presentation/screens/settings/components/SwitchPref.kt index c18f097f..d2a269b7 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/components/prefs/SwitchPref.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/settings/components/SwitchPref.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.ui.components.prefs +package com.bnyro.contacts.presentation.screens.settings.components import androidx.compose.foundation.clickable import androidx.compose.foundation.interaction.MutableInteractionSource diff --git a/app/src/main/java/com/bnyro/contacts/ui/models/ThemeModel.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/settings/model/ThemeModel.kt similarity index 85% rename from app/src/main/java/com/bnyro/contacts/ui/models/ThemeModel.kt rename to app/src/main/java/com/bnyro/contacts/presentation/screens/settings/model/ThemeModel.kt index 0e2ca10e..34314a36 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/models/ThemeModel.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/settings/model/ThemeModel.kt @@ -1,10 +1,10 @@ -package com.bnyro.contacts.ui.models +package com.bnyro.contacts.presentation.screens.settings.model import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.lifecycle.ViewModel -import com.bnyro.contacts.enums.ThemeMode +import com.bnyro.contacts.domain.enums.ThemeMode import com.bnyro.contacts.util.Preferences class ThemeModel : ViewModel() { diff --git a/app/src/main/java/com/bnyro/contacts/ui/screens/SmsListScreen.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/SmsListScreen.kt similarity index 86% rename from app/src/main/java/com/bnyro/contacts/ui/screens/SmsListScreen.kt rename to app/src/main/java/com/bnyro/contacts/presentation/screens/sms/SmsListScreen.kt index 317ad026..efcea8e1 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/screens/SmsListScreen.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/SmsListScreen.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.ui.screens +package com.bnyro.contacts.presentation.screens.sms import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize @@ -26,16 +26,17 @@ import androidx.compose.ui.input.nestedscroll.NestedScrollConnection import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.res.stringResource import com.bnyro.contacts.R -import com.bnyro.contacts.nav.NavRoutes -import com.bnyro.contacts.obj.ContactData -import com.bnyro.contacts.obj.SmsThread -import com.bnyro.contacts.ui.components.NothingHere -import com.bnyro.contacts.ui.components.NumberPickerDialog -import com.bnyro.contacts.ui.components.SmsThreadItem -import com.bnyro.contacts.ui.components.TopBarMoreMenu -import com.bnyro.contacts.ui.components.base.ClickableIcon -import com.bnyro.contacts.ui.models.ContactsModel -import com.bnyro.contacts.ui.models.SmsModel +import com.bnyro.contacts.domain.model.ContactData +import com.bnyro.contacts.domain.model.SmsThread +import com.bnyro.contacts.navigation.NavRoutes +import com.bnyro.contacts.presentation.components.ClickableIcon +import com.bnyro.contacts.presentation.components.NothingHere +import com.bnyro.contacts.presentation.components.TopBarMoreMenu +import com.bnyro.contacts.presentation.features.NumberPickerDialog +import com.bnyro.contacts.presentation.screens.contacts.model.ContactsModel +import com.bnyro.contacts.presentation.screens.sms.components.SmsSearchScreen +import com.bnyro.contacts.presentation.screens.sms.components.SmsThreadItem +import com.bnyro.contacts.presentation.screens.sms.model.SmsModel @OptIn(ExperimentalMaterial3Api::class) @Composable diff --git a/app/src/main/java/com/bnyro/contacts/ui/screens/SmsThreadScreen.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/SmsThreadScreen.kt similarity index 92% rename from app/src/main/java/com/bnyro/contacts/ui/screens/SmsThreadScreen.kt rename to app/src/main/java/com/bnyro/contacts/presentation/screens/sms/SmsThreadScreen.kt index 76ade6d2..669ff0ea 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/screens/SmsThreadScreen.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/SmsThreadScreen.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.ui.screens +package com.bnyro.contacts.presentation.screens.sms import android.annotation.SuppressLint import android.os.Build @@ -43,14 +43,15 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import com.bnyro.contacts.R -import com.bnyro.contacts.obj.ContactData -import com.bnyro.contacts.ui.components.base.ClickableIcon -import com.bnyro.contacts.ui.components.base.ElevatedTextInputField -import com.bnyro.contacts.ui.components.conversation.Messages -import com.bnyro.contacts.ui.components.dialogs.AddToContactDialog -import com.bnyro.contacts.ui.components.dialogs.ConfirmationDialog -import com.bnyro.contacts.ui.models.ContactsModel -import com.bnyro.contacts.ui.models.SmsModel +import com.bnyro.contacts.domain.model.ContactData +import com.bnyro.contacts.presentation.components.ClickableIcon +import com.bnyro.contacts.presentation.components.ElevatedTextInputField +import com.bnyro.contacts.presentation.features.AddToContactDialog +import com.bnyro.contacts.presentation.features.ConfirmationDialog +import com.bnyro.contacts.presentation.screens.contact.SingleContactScreen +import com.bnyro.contacts.presentation.screens.contacts.model.ContactsModel +import com.bnyro.contacts.presentation.screens.sms.components.Messages +import com.bnyro.contacts.presentation.screens.sms.model.SmsModel import com.bnyro.contacts.util.SmsUtil @SuppressLint("NewApi") diff --git a/app/src/main/java/com/bnyro/contacts/ui/components/conversation/Message.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/components/Message.kt similarity index 82% rename from app/src/main/java/com/bnyro/contacts/ui/components/conversation/Message.kt rename to app/src/main/java/com/bnyro/contacts/presentation/screens/sms/components/Message.kt index 380cbcfc..ebac696d 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/components/conversation/Message.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/components/Message.kt @@ -1,16 +1,39 @@ -package com.bnyro.contacts.ui.components.conversation +package com.bnyro.contacts.presentation.screens.sms.components import android.provider.Telephony import android.text.format.DateUtils -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.RowScope +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.items import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.ClickableText import androidx.compose.foundation.text.selection.SelectionContainer -import androidx.compose.material3.* -import androidx.compose.runtime.* +import androidx.compose.material3.Divider +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.LocalContentColor +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.SwipeToDismissBox +import androidx.compose.material3.SwipeToDismissBoxValue +import androidx.compose.material3.Text +import androidx.compose.material3.rememberSwipeToDismissBoxState +import androidx.compose.material3.surfaceColorAtElevation +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext @@ -18,9 +41,9 @@ import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import com.bnyro.contacts.R -import com.bnyro.contacts.db.obj.SmsData -import com.bnyro.contacts.ui.components.dialogs.ConfirmationDialog -import com.bnyro.contacts.ui.models.SmsModel +import com.bnyro.contacts.data.database.obj.SmsData +import com.bnyro.contacts.presentation.features.ConfirmationDialog +import com.bnyro.contacts.presentation.screens.sms.model.SmsModel import com.bnyro.contacts.util.generateAnnotations @OptIn(ExperimentalMaterial3Api::class) diff --git a/app/src/main/java/com/bnyro/contacts/ui/screens/SmsSearchScreen.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/components/SmsSearchScreen.kt similarity index 89% rename from app/src/main/java/com/bnyro/contacts/ui/screens/SmsSearchScreen.kt rename to app/src/main/java/com/bnyro/contacts/presentation/screens/sms/components/SmsSearchScreen.kt index e2e97fed..33eb88d0 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/screens/SmsSearchScreen.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/components/SmsSearchScreen.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.ui.screens +package com.bnyro.contacts.presentation.screens.sms.components import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize @@ -20,12 +20,11 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.unit.dp import com.bnyro.contacts.R -import com.bnyro.contacts.obj.ContactData -import com.bnyro.contacts.obj.SmsThread -import com.bnyro.contacts.ui.components.SmsThreadItem -import com.bnyro.contacts.ui.components.base.ElevatedTextInputField -import com.bnyro.contacts.ui.components.base.FullScreenDialog -import com.bnyro.contacts.ui.models.SmsModel +import com.bnyro.contacts.domain.model.ContactData +import com.bnyro.contacts.domain.model.SmsThread +import com.bnyro.contacts.presentation.components.ElevatedTextInputField +import com.bnyro.contacts.presentation.components.FullScreenDialog +import com.bnyro.contacts.presentation.screens.sms.model.SmsModel import com.bnyro.contacts.util.ContactsHelper import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay diff --git a/app/src/main/java/com/bnyro/contacts/ui/components/SmsThreadItem.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/components/SmsThreadItem.kt similarity index 95% rename from app/src/main/java/com/bnyro/contacts/ui/components/SmsThreadItem.kt rename to app/src/main/java/com/bnyro/contacts/presentation/screens/sms/components/SmsThreadItem.kt index 39b5d464..30e667dd 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/components/SmsThreadItem.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/components/SmsThreadItem.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.ui.components +package com.bnyro.contacts.presentation.screens.sms.components import androidx.compose.foundation.Image import androidx.compose.foundation.background @@ -17,7 +17,6 @@ import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Person -import androidx.compose.material3.DismissDirection import androidx.compose.material3.ElevatedCard import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.MaterialTheme @@ -42,10 +41,10 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.bnyro.contacts.R -import com.bnyro.contacts.obj.ContactData -import com.bnyro.contacts.obj.SmsThread -import com.bnyro.contacts.ui.components.dialogs.ConfirmationDialog -import com.bnyro.contacts.ui.models.SmsModel +import com.bnyro.contacts.domain.model.ContactData +import com.bnyro.contacts.domain.model.SmsThread +import com.bnyro.contacts.presentation.features.ConfirmationDialog +import com.bnyro.contacts.presentation.screens.sms.model.SmsModel @OptIn(ExperimentalMaterial3Api::class) @Composable diff --git a/app/src/main/java/com/bnyro/contacts/ui/models/SmsModel.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/model/SmsModel.kt similarity index 95% rename from app/src/main/java/com/bnyro/contacts/ui/models/SmsModel.kt rename to app/src/main/java/com/bnyro/contacts/presentation/screens/sms/model/SmsModel.kt index 0266ec13..4a68503b 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/models/SmsModel.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/model/SmsModel.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.ui.models +package com.bnyro.contacts.presentation.screens.sms.model import android.annotation.SuppressLint import android.content.Context @@ -12,7 +12,7 @@ import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewmodel.initializer import androidx.lifecycle.viewmodel.viewModelFactory import com.bnyro.contacts.App -import com.bnyro.contacts.obj.ContactData +import com.bnyro.contacts.domain.model.ContactData import com.bnyro.contacts.util.SmsUtil import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.SharingStarted diff --git a/app/src/main/java/com/bnyro/contacts/ui/activities/BaseActivity.kt b/app/src/main/java/com/bnyro/contacts/ui/activities/BaseActivity.kt index 35d95aa0..59c875ad 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/activities/BaseActivity.kt +++ b/app/src/main/java/com/bnyro/contacts/ui/activities/BaseActivity.kt @@ -11,10 +11,10 @@ import androidx.activity.viewModels import androidx.fragment.app.FragmentActivity import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.get -import com.bnyro.contacts.ui.models.ContactsModel -import com.bnyro.contacts.ui.models.DialerModel -import com.bnyro.contacts.ui.models.SmsModel -import com.bnyro.contacts.ui.models.ThemeModel +import com.bnyro.contacts.presentation.screens.contacts.model.ContactsModel +import com.bnyro.contacts.presentation.screens.dialer.model.DialerModel +import com.bnyro.contacts.presentation.screens.settings.model.ThemeModel +import com.bnyro.contacts.presentation.screens.sms.model.SmsModel import com.bnyro.contacts.util.NotificationHelper import com.bnyro.contacts.util.PermissionHelper diff --git a/app/src/main/java/com/bnyro/contacts/ui/activities/CallActivity.kt b/app/src/main/java/com/bnyro/contacts/ui/activities/CallActivity.kt index f99b0879..d4268e53 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/activities/CallActivity.kt +++ b/app/src/main/java/com/bnyro/contacts/ui/activities/CallActivity.kt @@ -16,10 +16,10 @@ import androidx.compose.material3.Scaffold import androidx.compose.ui.Modifier import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.get -import com.bnyro.contacts.services.CallService -import com.bnyro.contacts.ui.models.DialerModel -import com.bnyro.contacts.ui.screens.DialerScreen +import com.bnyro.contacts.presentation.screens.dialer.DialerScreen +import com.bnyro.contacts.presentation.screens.dialer.model.DialerModel import com.bnyro.contacts.ui.theme.ConnectYouTheme +import com.bnyro.contacts.util.services.CallService class CallActivity : BaseActivity() { diff --git a/app/src/main/java/com/bnyro/contacts/ui/activities/MainActivity.kt b/app/src/main/java/com/bnyro/contacts/ui/activities/MainActivity.kt index 6d6fb6d4..33879738 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/activities/MainActivity.kt +++ b/app/src/main/java/com/bnyro/contacts/ui/activities/MainActivity.kt @@ -13,17 +13,17 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.platform.LocalContext -import com.bnyro.contacts.ext.parcelable -import com.bnyro.contacts.nav.NavContainer -import com.bnyro.contacts.obj.ContactData -import com.bnyro.contacts.ui.components.ConfirmImportContactsDialog -import com.bnyro.contacts.ui.components.dialogs.AddToContactDialog +import com.bnyro.contacts.domain.model.ContactData +import com.bnyro.contacts.navigation.NavContainer +import com.bnyro.contacts.presentation.features.AddToContactDialog +import com.bnyro.contacts.presentation.features.ConfirmImportContactsDialog import com.bnyro.contacts.ui.theme.ConnectYouTheme import com.bnyro.contacts.util.BackupHelper import com.bnyro.contacts.util.BiometricAuthUtil import com.bnyro.contacts.util.ContactsHelper import com.bnyro.contacts.util.IntentHelper import com.bnyro.contacts.util.Preferences +import com.bnyro.contacts.util.extension.parcelable import java.net.URLDecoder class MainActivity : BaseActivity() { diff --git a/app/src/main/java/com/bnyro/contacts/ui/activities/PickContactActivity.kt b/app/src/main/java/com/bnyro/contacts/ui/activities/PickContactActivity.kt index 11715fca..bf2abe70 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/activities/PickContactActivity.kt +++ b/app/src/main/java/com/bnyro/contacts/ui/activities/PickContactActivity.kt @@ -31,9 +31,9 @@ import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import com.bnyro.contacts.R -import com.bnyro.contacts.enums.SortOrder -import com.bnyro.contacts.obj.ContactData -import com.bnyro.contacts.ui.components.ContactItem +import com.bnyro.contacts.domain.enums.SortOrder +import com.bnyro.contacts.domain.model.ContactData +import com.bnyro.contacts.presentation.screens.contacts.components.ContactItem import com.bnyro.contacts.ui.theme.ConnectYouTheme class PickContactActivity : BaseActivity() { diff --git a/app/src/main/java/com/bnyro/contacts/ui/theme/Theme.kt b/app/src/main/java/com/bnyro/contacts/ui/theme/Theme.kt index 71adea1d..352dea4b 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/theme/Theme.kt +++ b/app/src/main/java/com/bnyro/contacts/ui/theme/Theme.kt @@ -3,7 +3,12 @@ package com.bnyro.contacts.ui.theme import android.app.Activity import android.os.Build import androidx.compose.foundation.isSystemInDarkTheme -import androidx.compose.material3.* +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.darkColorScheme +import androidx.compose.material3.dynamicDarkColorScheme +import androidx.compose.material3.dynamicLightColorScheme +import androidx.compose.material3.lightColorScheme +import androidx.compose.material3.surfaceColorAtElevation import androidx.compose.runtime.Composable import androidx.compose.runtime.SideEffect import androidx.compose.ui.graphics.Color @@ -12,7 +17,7 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalView import androidx.compose.ui.unit.dp import androidx.core.view.WindowCompat -import com.bnyro.contacts.enums.ThemeMode +import com.bnyro.contacts.domain.enums.ThemeMode private val DarkColorScheme = darkColorScheme( primary = Purple80, diff --git a/app/src/main/java/com/bnyro/contacts/util/BackupHelper.kt b/app/src/main/java/com/bnyro/contacts/util/BackupHelper.kt index 8ce085b7..0532d727 100644 --- a/app/src/main/java/com/bnyro/contacts/util/BackupHelper.kt +++ b/app/src/main/java/com/bnyro/contacts/util/BackupHelper.kt @@ -4,7 +4,7 @@ import android.content.Context import android.net.Uri import android.util.Log import androidx.documentfile.provider.DocumentFile -import com.bnyro.contacts.repo.ContactsRepository +import com.bnyro.contacts.domain.repositories.ContactsRepository object BackupHelper { val vCardMimeTypes = arrayOf("text/vcard", "text/x-vcard", "text/directory") diff --git a/app/src/main/java/com/bnyro/contacts/util/CallLogHelper.kt b/app/src/main/java/com/bnyro/contacts/util/CallLogHelper.kt index c588ea5f..2d652679 100644 --- a/app/src/main/java/com/bnyro/contacts/util/CallLogHelper.kt +++ b/app/src/main/java/com/bnyro/contacts/util/CallLogHelper.kt @@ -3,10 +3,10 @@ package com.bnyro.contacts.util import android.Manifest import android.content.Context import android.provider.CallLog -import com.bnyro.contacts.ext.intValue -import com.bnyro.contacts.ext.longValue -import com.bnyro.contacts.ext.stringValue -import com.bnyro.contacts.obj.CallLogEntry +import com.bnyro.contacts.domain.model.CallLogEntry +import com.bnyro.contacts.util.extension.intValue +import com.bnyro.contacts.util.extension.longValue +import com.bnyro.contacts.util.extension.stringValue import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext diff --git a/app/src/main/java/com/bnyro/contacts/util/ContactsHelper.kt b/app/src/main/java/com/bnyro/contacts/util/ContactsHelper.kt index 11c1a540..d40a9c72 100644 --- a/app/src/main/java/com/bnyro/contacts/util/ContactsHelper.kt +++ b/app/src/main/java/com/bnyro/contacts/util/ContactsHelper.kt @@ -6,17 +6,17 @@ import android.graphics.BitmapFactory import android.net.Uri import android.provider.ContactsContract import com.bnyro.contacts.R -import com.bnyro.contacts.enums.Addresses -import com.bnyro.contacts.enums.Emails -import com.bnyro.contacts.enums.Events -import com.bnyro.contacts.enums.Nickname -import com.bnyro.contacts.enums.Notes -import com.bnyro.contacts.enums.Numbers -import com.bnyro.contacts.enums.Organization -import com.bnyro.contacts.enums.Title -import com.bnyro.contacts.enums.Websites -import com.bnyro.contacts.obj.ContactData -import com.bnyro.contacts.obj.TranslatedType +import com.bnyro.contacts.domain.enums.Addresses +import com.bnyro.contacts.domain.enums.Emails +import com.bnyro.contacts.domain.enums.Events +import com.bnyro.contacts.domain.enums.Nickname +import com.bnyro.contacts.domain.enums.Notes +import com.bnyro.contacts.domain.enums.Numbers +import com.bnyro.contacts.domain.enums.Organization +import com.bnyro.contacts.domain.enums.Title +import com.bnyro.contacts.domain.enums.Websites +import com.bnyro.contacts.domain.model.ContactData +import com.bnyro.contacts.domain.model.TranslatedType import com.google.i18n.phonenumbers.PhoneNumberUtil import ezvcard.parameter.AddressType import ezvcard.parameter.EmailType diff --git a/app/src/main/java/com/bnyro/contacts/util/ExportHelper.kt b/app/src/main/java/com/bnyro/contacts/util/ExportHelper.kt index d868344e..e6d71874 100644 --- a/app/src/main/java/com/bnyro/contacts/util/ExportHelper.kt +++ b/app/src/main/java/com/bnyro/contacts/util/ExportHelper.kt @@ -4,12 +4,12 @@ import android.annotation.SuppressLint import android.content.Context import android.net.Uri import androidx.core.content.FileProvider -import com.bnyro.contacts.ext.pmap -import com.bnyro.contacts.obj.ContactData -import com.bnyro.contacts.repo.ContactsRepository -import java.io.File +import com.bnyro.contacts.domain.model.ContactData +import com.bnyro.contacts.domain.repositories.ContactsRepository +import com.bnyro.contacts.util.extension.pmap import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext +import java.io.File class ExportHelper( private val context: Context, diff --git a/app/src/main/java/com/bnyro/contacts/util/IntentHelper.kt b/app/src/main/java/com/bnyro/contacts/util/IntentHelper.kt index cc9d9f82..f1bb5028 100644 --- a/app/src/main/java/com/bnyro/contacts/util/IntentHelper.kt +++ b/app/src/main/java/com/bnyro/contacts/util/IntentHelper.kt @@ -7,12 +7,12 @@ import android.net.Uri import android.provider.ContactsContract import androidx.core.net.toUri import com.bnyro.contacts.R -import com.bnyro.contacts.enums.IntentActionType -import com.bnyro.contacts.enums.ListAttribute -import com.bnyro.contacts.enums.StringAttribute -import com.bnyro.contacts.obj.ContactData -import com.bnyro.contacts.obj.TranslatedType -import com.bnyro.contacts.obj.ValueWithType +import com.bnyro.contacts.domain.enums.IntentActionType +import com.bnyro.contacts.domain.enums.ListAttribute +import com.bnyro.contacts.domain.enums.StringAttribute +import com.bnyro.contacts.domain.model.ContactData +import com.bnyro.contacts.domain.model.TranslatedType +import com.bnyro.contacts.domain.model.ValueWithType object IntentHelper { fun launchAction(context: Context, type: IntentActionType, argument: String) { diff --git a/app/src/main/java/com/bnyro/contacts/util/Preferences.kt b/app/src/main/java/com/bnyro/contacts/util/Preferences.kt index fd5b99a9..a981497d 100644 --- a/app/src/main/java/com/bnyro/contacts/util/Preferences.kt +++ b/app/src/main/java/com/bnyro/contacts/util/Preferences.kt @@ -7,8 +7,8 @@ import androidx.compose.runtime.MutableState import androidx.compose.runtime.SnapshotMutationPolicy import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import com.bnyro.contacts.enums.BackupType -import com.bnyro.contacts.obj.AccountType +import com.bnyro.contacts.domain.enums.BackupType +import com.bnyro.contacts.domain.model.AccountType object Preferences { private const val prefFile = "preferences" diff --git a/app/src/main/java/com/bnyro/contacts/util/ShortcutsHelper.kt b/app/src/main/java/com/bnyro/contacts/util/ShortcutsHelper.kt index 92b96c4f..2a9cb89b 100644 --- a/app/src/main/java/com/bnyro/contacts/util/ShortcutsHelper.kt +++ b/app/src/main/java/com/bnyro/contacts/util/ShortcutsHelper.kt @@ -8,8 +8,8 @@ import androidx.core.content.pm.ShortcutInfoCompat import androidx.core.content.pm.ShortcutManagerCompat import androidx.core.graphics.drawable.IconCompat import com.bnyro.contacts.R -import com.bnyro.contacts.enums.IntentActionType -import com.bnyro.contacts.obj.ContactData +import com.bnyro.contacts.domain.enums.IntentActionType +import com.bnyro.contacts.domain.model.ContactData import com.bnyro.contacts.ui.activities.MainActivity object ShortcutHelper { diff --git a/app/src/main/java/com/bnyro/contacts/util/SimContactsHelper.kt b/app/src/main/java/com/bnyro/contacts/util/SimContactsHelper.kt index 8adb9f0f..37dbd984 100644 --- a/app/src/main/java/com/bnyro/contacts/util/SimContactsHelper.kt +++ b/app/src/main/java/com/bnyro/contacts/util/SimContactsHelper.kt @@ -2,9 +2,9 @@ package com.bnyro.contacts.util import android.content.Context import android.net.Uri -import com.bnyro.contacts.ext.stringValue -import com.bnyro.contacts.obj.ContactData -import com.bnyro.contacts.obj.ValueWithType +import com.bnyro.contacts.domain.model.ContactData +import com.bnyro.contacts.domain.model.ValueWithType +import com.bnyro.contacts.util.extension.stringValue object SimContactsHelper { fun getSimContacts(context: Context): List { diff --git a/app/src/main/java/com/bnyro/contacts/util/SmsUtil.kt b/app/src/main/java/com/bnyro/contacts/util/SmsUtil.kt index 9c70dcf6..0eaa54f3 100644 --- a/app/src/main/java/com/bnyro/contacts/util/SmsUtil.kt +++ b/app/src/main/java/com/bnyro/contacts/util/SmsUtil.kt @@ -12,7 +12,7 @@ import android.widget.Toast import androidx.annotation.RequiresApi import com.bnyro.contacts.App import com.bnyro.contacts.R -import com.bnyro.contacts.db.obj.SmsData +import com.bnyro.contacts.data.database.obj.SmsData import java.lang.Character.UnicodeBlock import java.util.Calendar diff --git a/app/src/main/java/com/bnyro/contacts/util/VcardHelper.kt b/app/src/main/java/com/bnyro/contacts/util/VcardHelper.kt index e90692a4..d2209ab9 100644 --- a/app/src/main/java/com/bnyro/contacts/util/VcardHelper.kt +++ b/app/src/main/java/com/bnyro/contacts/util/VcardHelper.kt @@ -2,8 +2,8 @@ package com.bnyro.contacts.util import android.graphics.BitmapFactory import android.provider.ContactsContract -import com.bnyro.contacts.obj.ContactData -import com.bnyro.contacts.obj.ValueWithType +import com.bnyro.contacts.domain.model.ContactData +import com.bnyro.contacts.domain.model.ValueWithType import ezvcard.Ezvcard import ezvcard.VCard import ezvcard.VCardVersion diff --git a/app/src/main/java/com/bnyro/contacts/ext/ContentColor.kt b/app/src/main/java/com/bnyro/contacts/util/extension/ContentColor.kt similarity index 92% rename from app/src/main/java/com/bnyro/contacts/ext/ContentColor.kt rename to app/src/main/java/com/bnyro/contacts/util/extension/ContentColor.kt index 14306b6c..ef375629 100644 --- a/app/src/main/java/com/bnyro/contacts/ext/ContentColor.kt +++ b/app/src/main/java/com/bnyro/contacts/util/extension/ContentColor.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.ext +package com.bnyro.contacts.util.extension import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable diff --git a/app/src/main/java/com/bnyro/contacts/ext/Cursor.kt b/app/src/main/java/com/bnyro/contacts/util/extension/Cursor.kt similarity index 91% rename from app/src/main/java/com/bnyro/contacts/ext/Cursor.kt rename to app/src/main/java/com/bnyro/contacts/util/extension/Cursor.kt index 3d605c28..a581d52d 100644 --- a/app/src/main/java/com/bnyro/contacts/ext/Cursor.kt +++ b/app/src/main/java/com/bnyro/contacts/util/extension/Cursor.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.ext +package com.bnyro.contacts.util.extension import android.database.Cursor import androidx.core.database.getIntOrNull diff --git a/app/src/main/java/com/bnyro/contacts/ext/NotAName.kt b/app/src/main/java/com/bnyro/contacts/util/extension/NotAName.kt similarity index 77% rename from app/src/main/java/com/bnyro/contacts/ext/NotAName.kt rename to app/src/main/java/com/bnyro/contacts/util/extension/NotAName.kt index f3c960c6..474598c4 100644 --- a/app/src/main/java/com/bnyro/contacts/ext/NotAName.kt +++ b/app/src/main/java/com/bnyro/contacts/util/extension/NotAName.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.ext +package com.bnyro.contacts.util.extension fun String?.notAName(): Boolean { this ?: return true diff --git a/app/src/main/java/com/bnyro/contacts/ext/Parcelable.kt b/app/src/main/java/com/bnyro/contacts/util/extension/Parcelable.kt similarity index 82% rename from app/src/main/java/com/bnyro/contacts/ext/Parcelable.kt rename to app/src/main/java/com/bnyro/contacts/util/extension/Parcelable.kt index 3f827a1b..84da213a 100644 --- a/app/src/main/java/com/bnyro/contacts/ext/Parcelable.kt +++ b/app/src/main/java/com/bnyro/contacts/util/extension/Parcelable.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.ext +package com.bnyro.contacts.util.extension import android.content.Intent import androidx.core.content.IntentCompat diff --git a/app/src/main/java/com/bnyro/contacts/ext/Pmap.kt b/app/src/main/java/com/bnyro/contacts/util/extension/Pmap.kt similarity index 85% rename from app/src/main/java/com/bnyro/contacts/ext/Pmap.kt rename to app/src/main/java/com/bnyro/contacts/util/extension/Pmap.kt index c5436d6f..2f5ea677 100644 --- a/app/src/main/java/com/bnyro/contacts/ext/Pmap.kt +++ b/app/src/main/java/com/bnyro/contacts/util/extension/Pmap.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.ext +package com.bnyro.contacts.util.extension import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.async diff --git a/app/src/main/java/com/bnyro/contacts/ext/String.kt b/app/src/main/java/com/bnyro/contacts/util/extension/String.kt similarity index 71% rename from app/src/main/java/com/bnyro/contacts/ext/String.kt rename to app/src/main/java/com/bnyro/contacts/util/extension/String.kt index d1554297..4968972e 100644 --- a/app/src/main/java/com/bnyro/contacts/ext/String.kt +++ b/app/src/main/java/com/bnyro/contacts/util/extension/String.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.ext +package com.bnyro.contacts.util.extension fun String.removeLastChar(): String { return if (isEmpty()) this diff --git a/app/src/main/java/com/bnyro/contacts/ext/Toast.kt b/app/src/main/java/com/bnyro/contacts/util/extension/Toast.kt similarity index 87% rename from app/src/main/java/com/bnyro/contacts/ext/Toast.kt rename to app/src/main/java/com/bnyro/contacts/util/extension/Toast.kt index 447dbd50..e1d23a7c 100644 --- a/app/src/main/java/com/bnyro/contacts/ext/Toast.kt +++ b/app/src/main/java/com/bnyro/contacts/util/extension/Toast.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.ext +package com.bnyro.contacts.util.extension import android.content.Context import android.os.Handler diff --git a/app/src/main/java/com/bnyro/contacts/receivers/CallActionReceiver.kt b/app/src/main/java/com/bnyro/contacts/util/receivers/CallActionReceiver.kt similarity index 93% rename from app/src/main/java/com/bnyro/contacts/receivers/CallActionReceiver.kt rename to app/src/main/java/com/bnyro/contacts/util/receivers/CallActionReceiver.kt index 984f38ef..9f084b2e 100644 --- a/app/src/main/java/com/bnyro/contacts/receivers/CallActionReceiver.kt +++ b/app/src/main/java/com/bnyro/contacts/util/receivers/CallActionReceiver.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.receivers; +package com.bnyro.contacts.util.receivers; import android.content.BroadcastReceiver import android.content.Context diff --git a/app/src/main/java/com/bnyro/contacts/receivers/CopyTextReceiver.kt b/app/src/main/java/com/bnyro/contacts/util/receivers/CopyTextReceiver.kt similarity index 90% rename from app/src/main/java/com/bnyro/contacts/receivers/CopyTextReceiver.kt rename to app/src/main/java/com/bnyro/contacts/util/receivers/CopyTextReceiver.kt index 26a3fab0..d7958952 100644 --- a/app/src/main/java/com/bnyro/contacts/receivers/CopyTextReceiver.kt +++ b/app/src/main/java/com/bnyro/contacts/util/receivers/CopyTextReceiver.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.receivers +package com.bnyro.contacts.util.receivers import android.content.BroadcastReceiver import android.content.Context diff --git a/app/src/main/java/com/bnyro/contacts/receivers/DeleteSmsReceiver.kt b/app/src/main/java/com/bnyro/contacts/util/receivers/DeleteSmsReceiver.kt similarity index 94% rename from app/src/main/java/com/bnyro/contacts/receivers/DeleteSmsReceiver.kt rename to app/src/main/java/com/bnyro/contacts/util/receivers/DeleteSmsReceiver.kt index 877980c9..eb8ed045 100644 --- a/app/src/main/java/com/bnyro/contacts/receivers/DeleteSmsReceiver.kt +++ b/app/src/main/java/com/bnyro/contacts/util/receivers/DeleteSmsReceiver.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.receivers +package com.bnyro.contacts.util.receivers import android.content.BroadcastReceiver import android.content.Context diff --git a/app/src/main/java/com/bnyro/contacts/receivers/MmsReceiver.kt b/app/src/main/java/com/bnyro/contacts/util/receivers/MmsReceiver.kt similarity index 87% rename from app/src/main/java/com/bnyro/contacts/receivers/MmsReceiver.kt rename to app/src/main/java/com/bnyro/contacts/util/receivers/MmsReceiver.kt index e050a0e2..a6a260ca 100644 --- a/app/src/main/java/com/bnyro/contacts/receivers/MmsReceiver.kt +++ b/app/src/main/java/com/bnyro/contacts/util/receivers/MmsReceiver.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.receivers +package com.bnyro.contacts.util.receivers import android.annotation.SuppressLint import android.content.BroadcastReceiver diff --git a/app/src/main/java/com/bnyro/contacts/receivers/ReplyReceiver.kt b/app/src/main/java/com/bnyro/contacts/util/receivers/ReplyReceiver.kt similarity index 96% rename from app/src/main/java/com/bnyro/contacts/receivers/ReplyReceiver.kt rename to app/src/main/java/com/bnyro/contacts/util/receivers/ReplyReceiver.kt index 75fd9b4d..88c9ac8f 100644 --- a/app/src/main/java/com/bnyro/contacts/receivers/ReplyReceiver.kt +++ b/app/src/main/java/com/bnyro/contacts/util/receivers/ReplyReceiver.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.receivers +package com.bnyro.contacts.util.receivers import android.content.BroadcastReceiver import android.content.Context diff --git a/app/src/main/java/com/bnyro/contacts/receivers/SmsReceiver.kt b/app/src/main/java/com/bnyro/contacts/util/receivers/SmsReceiver.kt similarity index 97% rename from app/src/main/java/com/bnyro/contacts/receivers/SmsReceiver.kt rename to app/src/main/java/com/bnyro/contacts/util/receivers/SmsReceiver.kt index cc5867ce..e2d29c47 100644 --- a/app/src/main/java/com/bnyro/contacts/receivers/SmsReceiver.kt +++ b/app/src/main/java/com/bnyro/contacts/util/receivers/SmsReceiver.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.receivers +package com.bnyro.contacts.util.receivers import android.annotation.SuppressLint import android.app.Notification @@ -12,8 +12,8 @@ import androidx.core.app.NotificationManagerCompat import androidx.core.app.RemoteInput import com.bnyro.contacts.App import com.bnyro.contacts.R -import com.bnyro.contacts.db.obj.SmsData -import com.bnyro.contacts.enums.IntentActionType +import com.bnyro.contacts.data.database.obj.SmsData +import com.bnyro.contacts.domain.enums.IntentActionType import com.bnyro.contacts.util.IntentHelper import com.bnyro.contacts.util.NotificationHelper import com.bnyro.contacts.util.NotificationHelper.MESSAGES_CHANNEL_ID diff --git a/app/src/main/java/com/bnyro/contacts/services/CallService.kt b/app/src/main/java/com/bnyro/contacts/util/services/CallService.kt similarity index 95% rename from app/src/main/java/com/bnyro/contacts/services/CallService.kt rename to app/src/main/java/com/bnyro/contacts/util/services/CallService.kt index 3f8dd574..c80becb9 100644 --- a/app/src/main/java/com/bnyro/contacts/services/CallService.kt +++ b/app/src/main/java/com/bnyro/contacts/util/services/CallService.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.services +package com.bnyro.contacts.util.services import android.annotation.SuppressLint import android.app.Notification @@ -17,14 +17,14 @@ import android.widget.RemoteViews import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat import com.bnyro.contacts.R -import com.bnyro.contacts.ext.stringValue -import com.bnyro.contacts.receivers.CallActionReceiver -import com.bnyro.contacts.receivers.CallActionReceiver.Companion.ACCEPT_CALL -import com.bnyro.contacts.receivers.CallActionReceiver.Companion.DECLINE_CALL import com.bnyro.contacts.ui.activities.CallActivity import com.bnyro.contacts.util.CallManager import com.bnyro.contacts.util.ContactsHelper import com.bnyro.contacts.util.NotificationHelper +import com.bnyro.contacts.util.extension.stringValue +import com.bnyro.contacts.util.receivers.CallActionReceiver +import com.bnyro.contacts.util.receivers.CallActionReceiver.Companion.ACCEPT_CALL +import com.bnyro.contacts.util.receivers.CallActionReceiver.Companion.DECLINE_CALL import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay diff --git a/app/src/main/java/com/bnyro/contacts/services/HeadlessSmsSendService.kt b/app/src/main/java/com/bnyro/contacts/util/services/HeadlessSmsSendService.kt similarity index 78% rename from app/src/main/java/com/bnyro/contacts/services/HeadlessSmsSendService.kt rename to app/src/main/java/com/bnyro/contacts/util/services/HeadlessSmsSendService.kt index 274a89d6..817877ad 100644 --- a/app/src/main/java/com/bnyro/contacts/services/HeadlessSmsSendService.kt +++ b/app/src/main/java/com/bnyro/contacts/util/services/HeadlessSmsSendService.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.services +package com.bnyro.contacts.util.services import android.app.Service import android.content.Intent diff --git a/app/src/main/java/com/bnyro/contacts/workers/BackupWorker.kt b/app/src/main/java/com/bnyro/contacts/util/workers/BackupWorker.kt similarity index 95% rename from app/src/main/java/com/bnyro/contacts/workers/BackupWorker.kt rename to app/src/main/java/com/bnyro/contacts/util/workers/BackupWorker.kt index a87f39c8..ac6800a2 100644 --- a/app/src/main/java/com/bnyro/contacts/workers/BackupWorker.kt +++ b/app/src/main/java/com/bnyro/contacts/util/workers/BackupWorker.kt @@ -1,4 +1,4 @@ -package com.bnyro.contacts.workers +package com.bnyro.contacts.util.workers import android.content.Context import androidx.work.CoroutineWorker @@ -7,7 +7,7 @@ import androidx.work.PeriodicWorkRequestBuilder import androidx.work.WorkManager import androidx.work.WorkerParameters import com.bnyro.contacts.App -import com.bnyro.contacts.enums.BackupType +import com.bnyro.contacts.domain.enums.BackupType import com.bnyro.contacts.util.BackupHelper import com.bnyro.contacts.util.Preferences import java.util.concurrent.TimeUnit diff --git a/app/src/test/java/com/bnyro/contacts/ExampleUnitTest.kt b/app/src/test/java/com/bnyro/contacts/ExampleUnitTest.kt index a4c616e1..972ff932 100644 --- a/app/src/test/java/com/bnyro/contacts/ExampleUnitTest.kt +++ b/app/src/test/java/com/bnyro/contacts/ExampleUnitTest.kt @@ -1,8 +1,8 @@ package com.bnyro.contacts -import com.bnyro.contacts.receivers.SmsReceiver import com.bnyro.contacts.util.CalendarUtils import com.bnyro.contacts.util.SmsUtil +import com.bnyro.contacts.util.receivers.SmsReceiver import org.junit.Assert.assertEquals import org.junit.Test From 66edda8b4ecec093952105aa4bcc8ed0338ff37a Mon Sep 17 00:00:00 2001 From: Suhas Dissanayake Date: Sat, 27 Apr 2024 11:40:28 +0530 Subject: [PATCH 024/159] feat: use nested navigation for home tabs --- .../contacts/navigation/HomeNavContainer.kt | 116 +++++++++++++++ .../bnyro/contacts/navigation/HomeNavHost.kt | 63 ++++++++ .../bnyro/contacts/navigation/NavContainer.kt | 137 +++--------------- .../com/bnyro/contacts/navigation/NavHost.kt | 109 ++++++++------ .../bnyro/contacts/navigation/NavRoutes.kt | 38 +++-- .../screens/contact/SingleContactScreen.kt | 4 +- .../contacts/components/ContactItem.kt | 2 +- .../screens/sms/SmsThreadScreen.kt | 2 +- .../screens/sms/model/SmsModel.kt | 18 +-- .../contacts/ui/activities/BaseActivity.kt | 2 +- .../contacts/ui/activities/MainActivity.kt | 7 +- 11 files changed, 298 insertions(+), 200 deletions(-) create mode 100644 app/src/main/java/com/bnyro/contacts/navigation/HomeNavContainer.kt create mode 100644 app/src/main/java/com/bnyro/contacts/navigation/HomeNavHost.kt diff --git a/app/src/main/java/com/bnyro/contacts/navigation/HomeNavContainer.kt b/app/src/main/java/com/bnyro/contacts/navigation/HomeNavContainer.kt new file mode 100644 index 00000000..27190da9 --- /dev/null +++ b/app/src/main/java/com/bnyro/contacts/navigation/HomeNavContainer.kt @@ -0,0 +1,116 @@ +package com.bnyro.contacts.navigation + +import android.content.res.Configuration +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Icon +import androidx.compose.material3.NavigationBar +import androidx.compose.material3.NavigationBarItem +import androidx.compose.material3.NavigationRail +import androidx.compose.material3.NavigationRailItem +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalConfiguration +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.navigation.NavController +import androidx.navigation.compose.rememberNavController +import com.bnyro.contacts.presentation.screens.contacts.model.ContactsModel +import com.bnyro.contacts.presentation.screens.dialer.model.DialerModel +import com.bnyro.contacts.presentation.screens.settings.model.ThemeModel +import com.bnyro.contacts.presentation.screens.sms.model.SmsModel + +@Composable +fun HomeNavContainer( + initialTab: HomeRoutes, + onNavigate: (String) -> Unit, + smsModel: SmsModel, + contactsModel: ContactsModel, + dialerModel: DialerModel, + themeModel: ThemeModel +) { + val navController = rememberNavController() + + var selectedRoute by remember { + mutableStateOf(initialTab) + } + + // listen for destination changes (e.g. back presses) + DisposableEffect(Unit) { + val listener = NavController.OnDestinationChangedListener { _, destination, _ -> + HomeRoutes.all.firstOrNull { it.route == destination.route } + ?.also { selectedRoute = it } + } + navController.addOnDestinationChangedListener(listener) + + onDispose { + navController.removeOnDestinationChangedListener(listener) + } + } + + val orientation = LocalConfiguration.current.orientation + Scaffold( + bottomBar = { + if (orientation == Configuration.ORIENTATION_PORTRAIT) { + NavigationBar( + tonalElevation = 5.dp + ) { + HomeRoutes.all.forEach { + NavigationBarItem( + label = { + Text(stringResource(it.stringRes)) + }, + icon = { + Icon(it.icon, null) + }, + selected = it == selectedRoute, + onClick = { + navController.popBackStack() + navController.navigate(it.route) + } + ) + } + } + } + } + ) { pV -> + Row( + Modifier + .fillMaxSize() + .padding(pV) + ) { + if (orientation == Configuration.ORIENTATION_LANDSCAPE) { + NavigationRail { + HomeRoutes.all.forEach { + NavigationRailItem(selected = it == selectedRoute, + onClick = { + navController.popBackStack() + navController.navigate(it.route) + }, + icon = { Icon(it.icon, null) }, + label = { + Text(stringResource(it.stringRes)) + }) + } + } + } + HomeNavHost( + navController = navController, + startTab = initialTab, + onNavigate = onNavigate, + smsModel = smsModel, + contactsModel = contactsModel, + dialerModel = dialerModel, + themeModel = themeModel + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/bnyro/contacts/navigation/HomeNavHost.kt b/app/src/main/java/com/bnyro/contacts/navigation/HomeNavHost.kt new file mode 100644 index 00000000..b11bd6c3 --- /dev/null +++ b/app/src/main/java/com/bnyro/contacts/navigation/HomeNavHost.kt @@ -0,0 +1,63 @@ +package com.bnyro.contacts.navigation + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.ui.Modifier +import androidx.lifecycle.ViewModelStoreOwner +import androidx.lifecycle.viewmodel.compose.LocalViewModelStoreOwner +import androidx.navigation.NavHostController +import androidx.navigation.compose.NavHost +import androidx.navigation.compose.composable +import com.bnyro.contacts.presentation.screens.calllog.CallLogsScreen +import com.bnyro.contacts.presentation.screens.contacts.ContactsPage +import com.bnyro.contacts.presentation.screens.contacts.model.ContactsModel +import com.bnyro.contacts.presentation.screens.dialer.model.DialerModel +import com.bnyro.contacts.presentation.screens.settings.model.ThemeModel +import com.bnyro.contacts.presentation.screens.sms.SmsListScreen +import com.bnyro.contacts.presentation.screens.sms.model.SmsModel + +@Composable +fun HomeNavHost( + navController: NavHostController, + onNavigate: (String) -> Unit, + startTab: HomeRoutes, + modifier: Modifier = Modifier, + smsModel: SmsModel, + contactsModel: ContactsModel, + dialerModel: DialerModel, + themeModel: ThemeModel +) { + val viewModelStoreOwner: ViewModelStoreOwner = LocalViewModelStoreOwner.current!! + + NavHost(navController, startDestination = startTab.route, modifier = modifier) { + composable(HomeRoutes.Contacts.route) { + CompositionLocalProvider(LocalViewModelStoreOwner provides viewModelStoreOwner) { + ContactsPage(null, + onNavigate = { + onNavigate.invoke(it.route) + }) + } + } + composable(HomeRoutes.Phone.route) { + CompositionLocalProvider(LocalViewModelStoreOwner provides viewModelStoreOwner) { + CallLogsScreen(contactsModel, dialerModel, themeModel) + } + } + composable(HomeRoutes.Messages.route) { + CompositionLocalProvider(LocalViewModelStoreOwner provides viewModelStoreOwner) { + SmsListScreen( + smsModel = smsModel, + contactsModel = contactsModel, + scrollConnection = null, + onNavigate = { + onNavigate.invoke(it.route) + }, + onClickMessage = { address, contactData -> + smsModel.currentContactData = contactData + onNavigate.invoke("${NavRoutes.MessageThread.route}/$address") + } + ) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/bnyro/contacts/navigation/NavContainer.kt b/app/src/main/java/com/bnyro/contacts/navigation/NavContainer.kt index 2cf73773..1b5a5184 100644 --- a/app/src/main/java/com/bnyro/contacts/navigation/NavContainer.kt +++ b/app/src/main/java/com/bnyro/contacts/navigation/NavContainer.kt @@ -1,129 +1,32 @@ package com.bnyro.contacts.navigation -import android.content.res.Configuration -import androidx.activity.addCallback -import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.padding -import androidx.compose.material3.Icon -import androidx.compose.material3.NavigationBar -import androidx.compose.material3.NavigationBarItem -import androidx.compose.material3.NavigationRail -import androidx.compose.material3.NavigationRailItem -import androidx.compose.material3.Scaffold -import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.DisposableEffect -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalConfiguration -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.unit.dp -import androidx.navigation.NavController +import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.compose.rememberNavController -import com.bnyro.contacts.ui.activities.MainActivity - -val bottomNavItems = listOf( - NavRoutes.Phone, - NavRoutes.Contacts, - NavRoutes.Messages -) +import com.bnyro.contacts.presentation.screens.contacts.model.ContactsModel +import com.bnyro.contacts.presentation.screens.dialer.model.DialerModel +import com.bnyro.contacts.presentation.screens.settings.model.ThemeModel +import com.bnyro.contacts.presentation.screens.sms.model.SmsModel @Composable fun NavContainer( - initialTabIndex: Int + initialTab: HomeRoutes ) { - val context = LocalContext.current val navController = rememberNavController() - - val initialTab = bottomNavItems[initialTabIndex.coerceIn(0, 1)] - var selectedRoute by remember { - mutableStateOf(initialTab) - } - LaunchedEffect(Unit) { - val activity = context as MainActivity - activity.onBackPressedDispatcher.addCallback { - if (selectedRoute != NavRoutes.Settings && selectedRoute != NavRoutes.About) { - activity.finish() - } else { - navController.popBackStack() - } - } - } - - // listen for destination changes (e.g. back presses) - DisposableEffect(Unit) { - val listener = NavController.OnDestinationChangedListener { _, destination, _ -> - allRoutes.firstOrNull { it.route == destination.route?.split("/")?.first() } - ?.let { selectedRoute = it } - } - navController.addOnDestinationChangedListener(listener) - - onDispose { - navController.removeOnDestinationChangedListener(listener) - } - } - - val orientation = LocalConfiguration.current.orientation - Scaffold( - bottomBar = { - if (orientation == Configuration.ORIENTATION_PORTRAIT && bottomNavItems.contains( - selectedRoute - ) - ) { - NavigationBar( - tonalElevation = 5.dp - ) { - bottomNavItems.forEach { - NavigationBarItem( - label = { - Text(stringResource(it.stringRes!!)) - }, - icon = { - Icon(it.icon!!, null) - }, - selected = it == selectedRoute, - onClick = { - selectedRoute = it - navController.navigate(it.route) - } - ) - } - } - } - } - ) { pV -> - Row( - Modifier - .fillMaxSize() - .padding(pV) - ) { - if (orientation == Configuration.ORIENTATION_LANDSCAPE) { - NavigationRail { - bottomNavItems.forEach { - NavigationRailItem(selected = it == selectedRoute, - onClick = { - selectedRoute = it - navController.navigate(it.route) - }, - icon = { Icon(it.icon!!, null) }, - label = { - Text(stringResource(it.stringRes!!)) - }) - } - } - } - AppNavHost( - navController, - startDestination = initialTab, - modifier = Modifier - .fillMaxSize() - ) - } - } + val smsModel: SmsModel = viewModel() + val contactsModel: ContactsModel = viewModel(factory = ContactsModel.Factory) + val dialerModel: DialerModel = viewModel() + val themeModel: ThemeModel = viewModel() + AppNavHost( + navController, + initialTab = initialTab, + modifier = Modifier + .fillMaxSize(), + smsModel = smsModel, + contactsModel = contactsModel, + dialerModel = dialerModel, + themeModel = themeModel + ) } diff --git a/app/src/main/java/com/bnyro/contacts/navigation/NavHost.kt b/app/src/main/java/com/bnyro/contacts/navigation/NavHost.kt index 4c8e675b..4377f60d 100644 --- a/app/src/main/java/com/bnyro/contacts/navigation/NavHost.kt +++ b/app/src/main/java/com/bnyro/contacts/navigation/NavHost.kt @@ -1,84 +1,105 @@ package com.bnyro.contacts.navigation +import androidx.compose.animation.AnimatedContentTransitionScope +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.lifecycle.ViewModelStoreOwner import androidx.lifecycle.viewmodel.compose.LocalViewModelStoreOwner -import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavHostController import androidx.navigation.NavType import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.navArgument import com.bnyro.contacts.presentation.screens.about.AboutScreen -import com.bnyro.contacts.presentation.screens.calllog.CallLogsScreen -import com.bnyro.contacts.presentation.screens.contacts.ContactsPage import com.bnyro.contacts.presentation.screens.contacts.model.ContactsModel import com.bnyro.contacts.presentation.screens.dialer.model.DialerModel import com.bnyro.contacts.presentation.screens.settings.SettingsScreen import com.bnyro.contacts.presentation.screens.settings.model.ThemeModel -import com.bnyro.contacts.presentation.screens.sms.SmsListScreen import com.bnyro.contacts.presentation.screens.sms.SmsThreadScreen import com.bnyro.contacts.presentation.screens.sms.model.SmsModel @Composable fun AppNavHost( navController: NavHostController, - startDestination: NavRoutes, - modifier: Modifier = Modifier + modifier: Modifier = Modifier, + initialTab: HomeRoutes, + smsModel: SmsModel, + contactsModel: ContactsModel, + dialerModel: DialerModel, + themeModel: ThemeModel ) { - val smsModel: SmsModel = viewModel(factory = SmsModel.Factory) - val contactsModel: ContactsModel = viewModel(factory = ContactsModel.Factory) - val dialerModel: DialerModel = viewModel() - val themeModel: ThemeModel = viewModel() - val viewModelStoreOwner: ViewModelStoreOwner = LocalViewModelStoreOwner.current!! - NavHost(navController, startDestination = startDestination.route, modifier = modifier) { - composable(NavRoutes.About.route) { + NavHost(navController, startDestination = NavRoutes.Home.route, modifier = modifier) { + composable(NavRoutes.Home.route, + enterTransition = { + slideIntoContainer( + AnimatedContentTransitionScope.SlideDirection.Down, + initialOffset = { it / 4 } + ) + fadeIn() + }, + exitTransition = { + slideOutOfContainer(AnimatedContentTransitionScope.SlideDirection.Up, + targetOffset = { it / 4 }) + fadeOut() + }) { + HomeNavContainer( + initialTab = initialTab, + onNavigate = { + navController.navigate(it) + }, + smsModel = smsModel, + contactsModel = contactsModel, + dialerModel = dialerModel, + themeModel = themeModel + ) + } + composable(NavRoutes.About.route, + enterTransition = { + slideIntoContainer( + AnimatedContentTransitionScope.SlideDirection.Up, + initialOffset = { it / 4 }) + fadeIn() + }, + exitTransition = { + slideOutOfContainer( + AnimatedContentTransitionScope.SlideDirection.Down, + targetOffset = { it / 4 }) + fadeOut() + }) { AboutScreen { navController.popBackStack() } } - composable(NavRoutes.Settings.route) { + composable(NavRoutes.Settings.route, + enterTransition = { + slideIntoContainer( + AnimatedContentTransitionScope.SlideDirection.Up, + initialOffset = { it / 4 }) + fadeIn() + }, + exitTransition = { + slideOutOfContainer( + AnimatedContentTransitionScope.SlideDirection.Down, + targetOffset = { it / 4 }) + fadeOut() + }) { SettingsScreen(themeModel = themeModel, smsModel = smsModel) { navController.popBackStack() } } - composable(NavRoutes.Contacts.route) { - CompositionLocalProvider(LocalViewModelStoreOwner provides viewModelStoreOwner) { - ContactsPage(null, - onNavigate = { - navController.navigate(it.route) - }) - } - } - composable(NavRoutes.Phone.route) { - CompositionLocalProvider(LocalViewModelStoreOwner provides viewModelStoreOwner) { - CallLogsScreen(contactsModel, dialerModel, themeModel) - } - } - composable(NavRoutes.Messages.route) { - CompositionLocalProvider(LocalViewModelStoreOwner provides viewModelStoreOwner) { - SmsListScreen( - smsModel = smsModel, - contactsModel = contactsModel, - scrollConnection = null, - onNavigate = { - navController.navigate(it.route) - }, - onClickMessage = { address, contactData -> - smsModel.currentContactData = contactData - navController.navigate("${NavRoutes.MessageThread.route}/$address") - } - ) - } - } composable( "${NavRoutes.MessageThread.route}/{address}", - listOf(navArgument("address") { type = NavType.StringType }) + listOf(navArgument("address") { type = NavType.StringType }), + enterTransition = { + slideIntoContainer( + AnimatedContentTransitionScope.SlideDirection.Up, + initialOffset = { it / 4 }) + fadeIn() + }, + exitTransition = { + slideOutOfContainer( + AnimatedContentTransitionScope.SlideDirection.Down, + targetOffset = { it / 4 }) + fadeOut() + } ) { val address = it.arguments?.getString("address") CompositionLocalProvider(LocalViewModelStoreOwner provides viewModelStoreOwner) { diff --git a/app/src/main/java/com/bnyro/contacts/navigation/NavRoutes.kt b/app/src/main/java/com/bnyro/contacts/navigation/NavRoutes.kt index 523550f8..c3e0d1ed 100644 --- a/app/src/main/java/com/bnyro/contacts/navigation/NavRoutes.kt +++ b/app/src/main/java/com/bnyro/contacts/navigation/NavRoutes.kt @@ -9,22 +9,28 @@ import androidx.compose.ui.graphics.vector.ImageVector import com.bnyro.contacts.R sealed class NavRoutes( - val route: String, - @StringRes val stringRes: Int? = null, - val icon: ImageVector? = null + val route: String ) { - object About : NavRoutes("about", null, null) - object Settings : NavRoutes("settings", null, null) - object Phone : NavRoutes("phone", R.string.dial, Icons.Rounded.Phone) - object Contacts : NavRoutes("contacts", R.string.contacts, Icons.Rounded.Person) - object Messages : NavRoutes("messages", R.string.messages, Icons.Rounded.Message) - object MessageThread : NavRoutes("message_thread", null, null) + object Home : NavRoutes("home") + object About : NavRoutes("about") + object Settings : NavRoutes("settings") + object MessageThread : NavRoutes("message_thread") } -val allRoutes = listOf( - NavRoutes.About, - NavRoutes.Settings, - NavRoutes.Contacts, - NavRoutes.Messages, - NavRoutes.MessageThread, -) \ No newline at end of file +sealed class HomeRoutes( + val route: String, + @StringRes val stringRes: Int, + val icon: ImageVector +) { + object Phone : HomeRoutes("phone", R.string.dial, Icons.Rounded.Phone) + object Contacts : HomeRoutes("contacts", R.string.contacts, Icons.Rounded.Person) + object Messages : HomeRoutes("messages", R.string.messages, Icons.Rounded.Message) + + companion object { + val all = listOf( + Phone, + Contacts, + Messages + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/bnyro/contacts/presentation/screens/contact/SingleContactScreen.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/contact/SingleContactScreen.kt index 36b01716..71c0cdc7 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/screens/contact/SingleContactScreen.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/contact/SingleContactScreen.kt @@ -44,7 +44,6 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import androidx.lifecycle.viewmodel.compose.viewModel import com.bnyro.contacts.R import com.bnyro.contacts.domain.enums.IntentActionType import com.bnyro.contacts.domain.enums.ListAttribute @@ -69,8 +68,7 @@ import com.bnyro.contacts.util.IntentHelper @OptIn(ExperimentalMaterial3Api::class) @Composable -fun SingleContactScreen(contact: ContactData, onClose: () -> Unit) { - val viewModel: ContactsModel = viewModel() +fun SingleContactScreen(contact: ContactData, viewModel: ContactsModel, onClose: () -> Unit) { val context = LocalContext.current var showDelete by remember { mutableStateOf(false) diff --git a/app/src/main/java/com/bnyro/contacts/presentation/screens/contacts/components/ContactItem.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/contacts/components/ContactItem.kt index 23a6279a..989944d3 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/screens/contacts/components/ContactItem.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/contacts/components/ContactItem.kt @@ -124,7 +124,7 @@ fun ContactItem( val data = runBlocking { viewModel.loadAdvancedContactData(contact) } - SingleContactScreen(data) { + SingleContactScreen(data, viewModel) { showContactScreen = false } } diff --git a/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/SmsThreadScreen.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/SmsThreadScreen.kt index 669ff0ea..9ce417ab 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/SmsThreadScreen.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/SmsThreadScreen.kt @@ -217,7 +217,7 @@ fun SmsThreadScreen( } if (showContactScreen && contactData != null) { - SingleContactScreen(contact = contactData) { + SingleContactScreen(contact = contactData, contactsModel) { showContactScreen = false } } diff --git a/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/model/SmsModel.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/model/SmsModel.kt index 4a68503b..f4afd8ca 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/model/SmsModel.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/model/SmsModel.kt @@ -1,16 +1,14 @@ package com.bnyro.contacts.presentation.screens.sms.model import android.annotation.SuppressLint +import android.app.Application import android.content.Context import android.telephony.SubscriptionInfo import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue -import androidx.lifecycle.ViewModel -import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.Companion.APPLICATION_KEY +import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.viewModelScope -import androidx.lifecycle.viewmodel.initializer -import androidx.lifecycle.viewmodel.viewModelFactory import com.bnyro.contacts.App import com.bnyro.contacts.domain.model.ContactData import com.bnyro.contacts.util.SmsUtil @@ -19,7 +17,8 @@ import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch -class SmsModel(val app: App) : ViewModel() { +class SmsModel(application: Application) : AndroidViewModel(application) { + val app = application as App var smsList = app.smsRepo.getSmsStream(context = app.applicationContext).stateIn( viewModelScope, started = SharingStarted.WhileSubscribed(5000L), @@ -63,13 +62,4 @@ class SmsModel(val app: App) : ViewModel() { app.initSmsRepo() updateSmsList() } - - companion object { - val Factory = viewModelFactory { - initializer { - val application = this[APPLICATION_KEY] as App - SmsModel(application) - } - } - } } diff --git a/app/src/main/java/com/bnyro/contacts/ui/activities/BaseActivity.kt b/app/src/main/java/com/bnyro/contacts/ui/activities/BaseActivity.kt index 59c875ad..278a5ef3 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/activities/BaseActivity.kt +++ b/app/src/main/java/com/bnyro/contacts/ui/activities/BaseActivity.kt @@ -23,7 +23,7 @@ abstract class BaseActivity : FragmentActivity() { val contactsModel by viewModels { ContactsModel.Factory } - val smsModel by viewModels { SmsModel.Factory } + val smsModel by viewModels() val dialerModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { diff --git a/app/src/main/java/com/bnyro/contacts/ui/activities/MainActivity.kt b/app/src/main/java/com/bnyro/contacts/ui/activities/MainActivity.kt index 33879738..49f56c7b 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/activities/MainActivity.kt +++ b/app/src/main/java/com/bnyro/contacts/ui/activities/MainActivity.kt @@ -14,6 +14,7 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.platform.LocalContext import com.bnyro.contacts.domain.model.ContactData +import com.bnyro.contacts.navigation.HomeRoutes import com.bnyro.contacts.navigation.NavContainer import com.bnyro.contacts.presentation.features.AddToContactDialog import com.bnyro.contacts.presentation.features.ConfirmImportContactsDialog @@ -44,8 +45,8 @@ class MainActivity : BaseActivity() { dialerModel.initialPhoneNumber = getInitialNumberToDial() val initialTabIndex = dialerModel.initialPhoneNumber?.let { 0 } - ?: smsModel.initialAddressAndBody?.let { 1 } - ?: Preferences.getInt(Preferences.homeTabKey, 0) + ?: smsModel.initialAddressAndBody?.let { 2 } + ?: Preferences.getInt(Preferences.homeTabKey, 1) setContent { ConnectYouTheme(themeModel.themeMode) { val context = LocalContext.current @@ -55,7 +56,7 @@ class MainActivity : BaseActivity() { } if (authSuccess) { - NavContainer(initialTabIndex) + NavContainer(HomeRoutes.all[initialTabIndex.coerceIn(0, 2)]) getInsertOrEditNumber()?.let { AddToContactDialog(it) } From f21f6ea50beec0422ab4c6d7f0b0a38dc284cb1a Mon Sep 17 00:00:00 2001 From: Suhas Dissanayake Date: Sat, 27 Apr 2024 16:21:11 +0530 Subject: [PATCH 025/159] feat: improved dialer screen --- app/build.gradle.kts | 1 + app/src/main/AndroidManifest.xml | 3 - .../bnyro/contacts/domain/model/CallerInfo.kt | 10 + .../contacts/navigation/HomeNavContainer.kt | 6 +- .../bnyro/contacts/navigation/HomeNavHost.kt | 6 +- .../bnyro/contacts/navigation/NavContainer.kt | 6 +- .../com/bnyro/contacts/navigation/NavHost.kt | 6 +- .../screens/calllog/CallLogsScreen.kt | 35 +-- .../screens/calllog/model/CallModel.kt | 32 +++ .../screens/dialer/DialerScreen.kt | 243 ++++++++++++------ .../screens/dialer/model/DialerModel.kt | 60 ++--- .../screens/dialer/model/state/CallState.kt | 13 + .../contacts/ui/activities/BaseActivity.kt | 4 +- .../contacts/ui/activities/CallActivity.kt | 48 +++- .../contacts/ui/activities/MainActivity.kt | 4 +- .../com/bnyro/contacts/util/CallManager.kt | 49 ---- .../util/receivers/CallActionReceiver.kt | 23 -- .../contacts/util/services/CallService.kt | 163 ++++++++++-- app/src/main/res/values/strings.xml | 3 + 19 files changed, 462 insertions(+), 253 deletions(-) create mode 100644 app/src/main/java/com/bnyro/contacts/domain/model/CallerInfo.kt create mode 100644 app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/model/CallModel.kt create mode 100644 app/src/main/java/com/bnyro/contacts/presentation/screens/dialer/model/state/CallState.kt delete mode 100644 app/src/main/java/com/bnyro/contacts/util/CallManager.kt delete mode 100644 app/src/main/java/com/bnyro/contacts/util/receivers/CallActionReceiver.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 82c29f7d..29e4173e 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -88,6 +88,7 @@ dependencies { // Image parsing implementation("androidx.exifinterface:exifinterface:1.3.7") + implementation("io.coil-kt:coil-compose:2.4.0") // Phone number formatting implementation("com.googlecode.libphonenumber:libphonenumber:8.2.0") diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index fd6fe96d..93b1fe04 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -246,9 +246,6 @@ android:name=".util.receivers.CopyTextReceiver" android:exported="false" /> - Unit, smsModel: SmsModel, contactsModel: ContactsModel, - dialerModel: DialerModel, + callModel: CallModel, themeModel: ThemeModel ) { val navController = rememberNavController() @@ -108,7 +108,7 @@ fun HomeNavContainer( onNavigate = onNavigate, smsModel = smsModel, contactsModel = contactsModel, - dialerModel = dialerModel, + callModel = callModel, themeModel = themeModel ) } diff --git a/app/src/main/java/com/bnyro/contacts/navigation/HomeNavHost.kt b/app/src/main/java/com/bnyro/contacts/navigation/HomeNavHost.kt index b11bd6c3..2ae40ae5 100644 --- a/app/src/main/java/com/bnyro/contacts/navigation/HomeNavHost.kt +++ b/app/src/main/java/com/bnyro/contacts/navigation/HomeNavHost.kt @@ -9,9 +9,9 @@ import androidx.navigation.NavHostController import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import com.bnyro.contacts.presentation.screens.calllog.CallLogsScreen +import com.bnyro.contacts.presentation.screens.calllog.model.CallModel import com.bnyro.contacts.presentation.screens.contacts.ContactsPage import com.bnyro.contacts.presentation.screens.contacts.model.ContactsModel -import com.bnyro.contacts.presentation.screens.dialer.model.DialerModel import com.bnyro.contacts.presentation.screens.settings.model.ThemeModel import com.bnyro.contacts.presentation.screens.sms.SmsListScreen import com.bnyro.contacts.presentation.screens.sms.model.SmsModel @@ -24,7 +24,7 @@ fun HomeNavHost( modifier: Modifier = Modifier, smsModel: SmsModel, contactsModel: ContactsModel, - dialerModel: DialerModel, + callModel: CallModel, themeModel: ThemeModel ) { val viewModelStoreOwner: ViewModelStoreOwner = LocalViewModelStoreOwner.current!! @@ -40,7 +40,7 @@ fun HomeNavHost( } composable(HomeRoutes.Phone.route) { CompositionLocalProvider(LocalViewModelStoreOwner provides viewModelStoreOwner) { - CallLogsScreen(contactsModel, dialerModel, themeModel) + CallLogsScreen(contactsModel, callModel, themeModel) } } composable(HomeRoutes.Messages.route) { diff --git a/app/src/main/java/com/bnyro/contacts/navigation/NavContainer.kt b/app/src/main/java/com/bnyro/contacts/navigation/NavContainer.kt index 1b5a5184..9665de01 100644 --- a/app/src/main/java/com/bnyro/contacts/navigation/NavContainer.kt +++ b/app/src/main/java/com/bnyro/contacts/navigation/NavContainer.kt @@ -5,8 +5,8 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.compose.rememberNavController +import com.bnyro.contacts.presentation.screens.calllog.model.CallModel import com.bnyro.contacts.presentation.screens.contacts.model.ContactsModel -import com.bnyro.contacts.presentation.screens.dialer.model.DialerModel import com.bnyro.contacts.presentation.screens.settings.model.ThemeModel import com.bnyro.contacts.presentation.screens.sms.model.SmsModel @@ -17,7 +17,7 @@ fun NavContainer( val navController = rememberNavController() val smsModel: SmsModel = viewModel() val contactsModel: ContactsModel = viewModel(factory = ContactsModel.Factory) - val dialerModel: DialerModel = viewModel() + val callModel: CallModel = viewModel() val themeModel: ThemeModel = viewModel() AppNavHost( navController, @@ -26,7 +26,7 @@ fun NavContainer( .fillMaxSize(), smsModel = smsModel, contactsModel = contactsModel, - dialerModel = dialerModel, + callModel = callModel, themeModel = themeModel ) } diff --git a/app/src/main/java/com/bnyro/contacts/navigation/NavHost.kt b/app/src/main/java/com/bnyro/contacts/navigation/NavHost.kt index 4377f60d..71326470 100644 --- a/app/src/main/java/com/bnyro/contacts/navigation/NavHost.kt +++ b/app/src/main/java/com/bnyro/contacts/navigation/NavHost.kt @@ -15,8 +15,8 @@ import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.navArgument import com.bnyro.contacts.presentation.screens.about.AboutScreen +import com.bnyro.contacts.presentation.screens.calllog.model.CallModel import com.bnyro.contacts.presentation.screens.contacts.model.ContactsModel -import com.bnyro.contacts.presentation.screens.dialer.model.DialerModel import com.bnyro.contacts.presentation.screens.settings.SettingsScreen import com.bnyro.contacts.presentation.screens.settings.model.ThemeModel import com.bnyro.contacts.presentation.screens.sms.SmsThreadScreen @@ -29,7 +29,7 @@ fun AppNavHost( initialTab: HomeRoutes, smsModel: SmsModel, contactsModel: ContactsModel, - dialerModel: DialerModel, + callModel: CallModel, themeModel: ThemeModel ) { val viewModelStoreOwner: ViewModelStoreOwner = LocalViewModelStoreOwner.current!! @@ -53,7 +53,7 @@ fun AppNavHost( }, smsModel = smsModel, contactsModel = contactsModel, - dialerModel = dialerModel, + callModel = callModel, themeModel = themeModel ) } diff --git a/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/CallLogsScreen.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/CallLogsScreen.kt index f53d77ae..60257ce1 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/CallLogsScreen.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/CallLogsScreen.kt @@ -3,7 +3,6 @@ package com.bnyro.contacts.presentation.screens.calllog import android.content.ComponentName import android.content.Intent import android.net.Uri -import android.os.Build import android.telecom.PhoneAccountHandle import android.telecom.TelecomManager import android.text.format.DateUtils @@ -44,8 +43,8 @@ import com.bnyro.contacts.presentation.components.NothingHere import com.bnyro.contacts.presentation.components.NumberInput import com.bnyro.contacts.presentation.components.PhoneNumberDisplay import com.bnyro.contacts.presentation.features.ConfirmationDialog +import com.bnyro.contacts.presentation.screens.calllog.model.CallModel import com.bnyro.contacts.presentation.screens.contacts.model.ContactsModel -import com.bnyro.contacts.presentation.screens.dialer.model.DialerModel import com.bnyro.contacts.presentation.screens.editor.components.ContactIconPlaceholder import com.bnyro.contacts.presentation.screens.settings.model.ThemeModel import com.bnyro.contacts.util.CallLogHelper @@ -57,7 +56,7 @@ import com.bnyro.contacts.util.extension.removeLastChar @Composable fun CallLogsScreen( contactsModel: ContactsModel, - dialerModel: DialerModel, + dialerModel: CallModel, themeModel: ThemeModel ) { val context = LocalContext.current @@ -72,32 +71,26 @@ fun CallLogsScreen( mutableStateOf(emptyList()) } val subscriptions = remember { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - SmsUtil.getSubscriptions(context) - } else { - null - } + SmsUtil.getSubscriptions(context) } var chosenSubInfo = remember { - subscriptions?.firstOrNull() + subscriptions.firstOrNull() } fun callNumber(number: String) { - if (!PermissionHelper.checkPermissions(context, DialerModel.phonePerms)) return + if (!PermissionHelper.checkPermissions(context, CallModel.phonePerms)) return val intent = Intent(Intent.ACTION_CALL, Uri.parse("tel:$number")).apply { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - chosenSubInfo?.let { - val phoneAccountHandle = PhoneAccountHandle( - ComponentName( - "com.android.phone", - "com.android.services.telephony.TelephonyConnectionService" - ), - it.subscriptionId.toString() - ) - putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle) - } + chosenSubInfo?.let { + val phoneAccountHandle = PhoneAccountHandle( + ComponentName( + "com.android.phone", + "com.android.services.telephony.TelephonyConnectionService" + ), + it.subscriptionId.toString() + ) + putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle) } } context.startActivity(intent) diff --git a/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/model/CallModel.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/model/CallModel.kt new file mode 100644 index 00000000..77433f9b --- /dev/null +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/model/CallModel.kt @@ -0,0 +1,32 @@ +package com.bnyro.contacts.presentation.screens.calllog.model + +import android.Manifest +import android.content.Context +import android.content.Intent +import android.telecom.TelecomManager +import androidx.lifecycle.ViewModel + +class CallModel : ViewModel() { + var initialPhoneNumber: String? = null + + fun requestDefaultDialerApp(context: Context) { + val telecomManager = context.getSystemService(Context.TELECOM_SERVICE) as TelecomManager? + val isAlreadyDefaultDialer = + context.packageName.equals(telecomManager!!.defaultDialerPackage) + if (!isAlreadyDefaultDialer) { + val intent: Intent = Intent(TelecomManager.ACTION_CHANGE_DEFAULT_DIALER) + .putExtra( + TelecomManager.EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME, + context.packageName + ) + context.startActivity(intent) + } + } + + companion object { + val phonePerms = arrayOf( + Manifest.permission.CALL_PHONE, + Manifest.permission.READ_PHONE_STATE + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/bnyro/contacts/presentation/screens/dialer/DialerScreen.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/dialer/DialerScreen.kt index e978d3be..ec02e9cb 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/screens/dialer/DialerScreen.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/dialer/DialerScreen.kt @@ -1,9 +1,9 @@ package com.bnyro.contacts.presentation.screens.dialer -import android.os.Handler -import android.os.Looper -import android.telecom.Call -import android.text.format.DateUtils +import android.net.Uri +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer @@ -11,16 +11,19 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.LazyVerticalGrid +import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.rounded.VolumeUp import androidx.compose.material.icons.filled.Call import androidx.compose.material.icons.filled.CallEnd import androidx.compose.material.icons.rounded.Dialpad import androidx.compose.material.icons.rounded.MicOff -import androidx.compose.material.icons.rounded.VolumeUp +import androidx.compose.material.icons.rounded.Person import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExtendedFloatingActionButton import androidx.compose.material3.Icon @@ -29,119 +32,144 @@ import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.Text import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable -import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableIntStateOf -import androidx.compose.runtime.mutableLongStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.draw.clip +import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import coil.compose.AsyncImage import com.bnyro.contacts.R import com.bnyro.contacts.presentation.components.DialerButton import com.bnyro.contacts.presentation.components.NumberInput import com.bnyro.contacts.presentation.components.PhoneNumberOnlyDisplay -import com.bnyro.contacts.presentation.screens.contacts.model.ContactsModel import com.bnyro.contacts.presentation.screens.dialer.model.DialerModel -import com.bnyro.contacts.util.CallManager +import com.bnyro.contacts.presentation.screens.dialer.model.state.CallState -@OptIn(ExperimentalMaterial3Api::class) @Composable fun DialerScreen( - contactsModel: ContactsModel, - dialerModel: DialerModel, - onClose: () -> Unit + dialerModel: DialerModel ) { - val context = LocalContext.current - - val contactInfo = remember { - contactsModel.getContactByNumber(CallManager.callerDisplayNumber) - } - var callState by remember { - mutableIntStateOf(CallManager.currentCallState) - } - var elapsedTime by remember { - mutableLongStateOf(0L) - } - val handler = remember { Handler(Looper.getMainLooper()) } - - fun updateTime() { - elapsedTime++ - handler.postDelayed(::updateTime, 1000L) - } - - var showDialPad by remember { mutableStateOf(false) } + val callState by dialerModel.callState.collectAsState() + val callerInfo by dialerModel.callerInfo.collectAsState() - DisposableEffect(Unit) { - val listener: (Int) -> Unit = { - callState = it - - if (callState == Call.STATE_DISCONNECTED) { - handler.removeCallbacks(::updateTime) - onClose.invoke() - } - - if (callState == Call.STATE_ACTIVE) { - updateTime() - } + when (val state = callState) { + CallState.Incoming -> { + CallAlertScreen( + callerNumber = callerInfo.formattedPhoneNumber, + callerName = callerInfo.callerName, + callerPhoto = callerInfo.callerPhoto, + onAcceptCall = dialerModel.acceptCall, + onDeclineCall = dialerModel.cancelCall, + ) } - CallManager.onStateChangedListeners.add(listener) - - onDispose { - CallManager.onStateChangedListeners.remove(listener) + else -> { + InCallScreen( + callStateText = stringResource(id = state.text), + callerNumber = callerInfo.formattedPhoneNumber, + callerName = callerInfo.callerName, + callerPhoto = callerInfo.callerPhoto, + muteState = dialerModel.currentMuteState, + speakerState = dialerModel.currentSpeakerState, + onToggleMute = dialerModel::toggleMute, + onToggleSpeaker = dialerModel::toggleSpeakers, + onCancelCall = dialerModel.cancelCall, + dialpadNumber = dialerModel.dialpadNumber, + onDialpadButtonPress = dialerModel::onDialpadButtonPress, + ) } } +} + +@Composable +@OptIn(ExperimentalMaterial3Api::class) +private fun InCallScreen( + callStateText: String, + callerNumber: String, + callerName: String? = null, + callerPhoto: Uri? = null, + muteState: Boolean, + speakerState: Boolean, + onToggleMute: () -> Unit, + onToggleSpeaker: () -> Unit, + onCancelCall: () -> Unit, + dialpadNumber: String, + onDialpadButtonPress: (String) -> Unit, +) { + var showDialPad by remember { mutableStateOf(false) } Column( modifier = Modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally ) { Spacer(modifier = Modifier.height(50.dp)) - Text( - text = when (callState) { - Call.STATE_RINGING, Call.STATE_DIALING, Call.STATE_PULLING_CALL -> stringResource( - R.string.ringing + Box( + modifier = Modifier + .size(128.dp) + .background( + shape = CircleShape, + color = MaterialTheme.colorScheme.primary + ) + ) { + if (callerPhoto == null) { + Image( + modifier = Modifier + .fillMaxSize() + .clip(CircleShape), + contentScale = ContentScale.Fit, + imageVector = Icons.Rounded.Person, + contentDescription = null + ) + } else { + AsyncImage( + callerPhoto, + modifier = Modifier + .fillMaxSize() + .clip(CircleShape), + contentDescription = null, + contentScale = ContentScale.Crop ) + } + } - Call.STATE_DISCONNECTING -> stringResource(R.string.disconnecting) - Call.STATE_ACTIVE -> stringResource(R.string.in_progress) - Call.STATE_CONNECTING -> stringResource(R.string.connecting) - else -> "" - }, + Spacer(modifier = Modifier.height(10.dp)) + Text( + text = callStateText, color = MaterialTheme.colorScheme.primary, fontSize = 16.sp ) Spacer(modifier = Modifier.height(10.dp)) Text( - text = contactInfo?.displayName ?: CallManager.callerDisplayNumber, + text = callerName ?: callerNumber, fontSize = 24.sp ) Spacer(modifier = Modifier.height(5.dp)) - Text(text = DateUtils.formatElapsedTime(elapsedTime)) + Text(text = callStateText) Spacer(modifier = Modifier.weight(1f)) LazyVerticalGrid(columns = GridCells.Fixed(3), modifier = Modifier.fillMaxWidth()) { item { DialerButton( - isEnabled = dialerModel.currentMuteState, + isEnabled = muteState, icon = Icons.Rounded.MicOff, hint = stringResource(R.string.mute) ) { - dialerModel.toggleMute(context) + onToggleMute.invoke() } } item { DialerButton( - isEnabled = dialerModel.currentSpeakerState, - icon = Icons.Rounded.VolumeUp, + isEnabled = speakerState, + icon = Icons.AutoMirrored.Rounded.VolumeUp, hint = stringResource(R.string.speakers) ) { - dialerModel.toggleSpeakers(context) + onToggleSpeaker.invoke() } } item { @@ -157,21 +185,8 @@ fun DialerScreen( } Spacer(modifier = Modifier.weight(1f)) Row { - if (callState == Call.STATE_RINGING) { - ExtendedFloatingActionButton( - onClick = { CallManager.acceptCall() }, - containerColor = MaterialTheme.colorScheme.primary, - contentColor = MaterialTheme.colorScheme.onPrimary, - shape = RoundedCornerShape(50) - ) { - Icon(Icons.Default.Call, contentDescription = null) - } - - Spacer(modifier = Modifier.width(20.dp)) - } - ExtendedFloatingActionButton( - onClick = { CallManager.cancelCall() }, + onClick = onCancelCall, containerColor = MaterialTheme.colorScheme.error, contentColor = MaterialTheme.colorScheme.onError, shape = RoundedCornerShape(50) @@ -191,11 +206,75 @@ fun DialerScreen( .padding(horizontal = 8.dp), horizontalAlignment = Alignment.CenterHorizontally ) { - PhoneNumberOnlyDisplay(displayText = dialerModel.dialpadNumber) + PhoneNumberOnlyDisplay(displayText = dialpadNumber) NumberInput( - onNumberInput = dialerModel::onDialpadButtonPress + onNumberInput = onDialpadButtonPress ) } } } } + +@Composable +private fun CallAlertScreen( + callerNumber: String, + callerName: String? = null, + callerPhoto: Uri? = null, + onAcceptCall: () -> Unit, + onDeclineCall: () -> Unit +) { + Column( + modifier = Modifier.fillMaxSize(), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Spacer(modifier = Modifier.height(50.dp)) + Text( + text = stringResource(R.string.call_from), + color = MaterialTheme.colorScheme.primary, + fontSize = 16.sp + ) + if (callerPhoto != null) { + Spacer(modifier = Modifier.height(10.dp)) + AsyncImage( + callerPhoto, + modifier = Modifier + .size(256.dp) + .clip(CircleShape), + contentDescription = null, + contentScale = ContentScale.Crop + ) + } + Spacer(modifier = Modifier.height(10.dp)) + Text( + text = callerName ?: callerNumber, + fontSize = 24.sp + ) + if (callerName != null) { + Spacer(modifier = Modifier.height(5.dp)) + Text(text = callerNumber) + } + Spacer(modifier = Modifier.weight(1f)) + Row { + ExtendedFloatingActionButton( + onClick = onAcceptCall, + containerColor = MaterialTheme.colorScheme.primary, + contentColor = MaterialTheme.colorScheme.onPrimary, + shape = RoundedCornerShape(50) + ) { + Icon(Icons.Default.Call, contentDescription = null) + } + + Spacer(modifier = Modifier.width(20.dp)) + + ExtendedFloatingActionButton( + onClick = onDeclineCall, + containerColor = MaterialTheme.colorScheme.error, + contentColor = MaterialTheme.colorScheme.onError, + shape = RoundedCornerShape(50) + ) { + Icon(Icons.Default.CallEnd, contentDescription = null) + } + } + Spacer(modifier = Modifier.height(50.dp)) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/bnyro/contacts/presentation/screens/dialer/model/DialerModel.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/dialer/model/DialerModel.kt index 02f86c54..911e175a 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/screens/dialer/model/DialerModel.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/dialer/model/DialerModel.kt @@ -1,36 +1,47 @@ package com.bnyro.contacts.presentation.screens.dialer.model -import android.Manifest -import android.content.Context -import android.content.Intent +import android.app.Application import android.media.AudioManager -import android.telecom.TelecomManager import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue -import androidx.lifecycle.ViewModel +import androidx.lifecycle.AndroidViewModel +import com.bnyro.contacts.domain.model.CallerInfo +import com.bnyro.contacts.presentation.screens.dialer.model.state.CallState +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update + +class DialerModel(application: Application) : AndroidViewModel(application) { + private val audioManager = + application.applicationContext.getSystemService(AudioManager::class.java) -class DialerModel : ViewModel() { - var initialPhoneNumber: String? = null var currentMuteState by mutableStateOf(false) var currentSpeakerState by mutableStateOf(false) var playDtmfTone: (digit: Char) -> Unit = {} + var acceptCall: () -> Unit = {} + var cancelCall: () -> Unit = {} + + private val _callState = MutableStateFlow(CallState.Disconnected) + val callState = _callState.asStateFlow() + + private val _callerInfo = MutableStateFlow(CallerInfo()) + val callerInfo = _callerInfo.asStateFlow() + var dialpadNumber by mutableStateOf("") private set - fun requestDefaultDialerApp(context: Context) { - val telecomManager = context.getSystemService(Context.TELECOM_SERVICE) as TelecomManager? - val isAlreadyDefaultDialer = - context.packageName.equals(telecomManager!!.defaultDialerPackage) - if (!isAlreadyDefaultDialer) { - val intent: Intent = Intent(TelecomManager.ACTION_CHANGE_DEFAULT_DIALER) - .putExtra( - TelecomManager.EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME, - context.packageName - ) - context.startActivity(intent) + fun onState(state: CallState) { + _callState.update { + state + } + } + + fun onCallerInfoUpdate(info: CallerInfo) { + _callerInfo.update { + info } } @@ -40,22 +51,13 @@ class DialerModel : ViewModel() { } - fun toggleMute(context: Context) { - val audioManager = context.getSystemService(AudioManager::class.java)!! + fun toggleMute() { audioManager.isMicrophoneMute = !currentMuteState currentMuteState = !currentMuteState } - fun toggleSpeakers(context: Context) { - val audioManager = context.getSystemService(AudioManager::class.java)!! + fun toggleSpeakers() { audioManager.isSpeakerphoneOn = !currentSpeakerState currentSpeakerState = !currentSpeakerState } - - companion object { - val phonePerms = arrayOf( - Manifest.permission.CALL_PHONE, - Manifest.permission.READ_PHONE_STATE - ) - } } diff --git a/app/src/main/java/com/bnyro/contacts/presentation/screens/dialer/model/state/CallState.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/dialer/model/state/CallState.kt new file mode 100644 index 00000000..ce79cc4b --- /dev/null +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/dialer/model/state/CallState.kt @@ -0,0 +1,13 @@ +package com.bnyro.contacts.presentation.screens.dialer.model.state + +import androidx.annotation.StringRes +import com.bnyro.contacts.R + +sealed class CallState(@StringRes val text: Int) { + object Disconnected : CallState(R.string.call_ended) + object Incoming : CallState(R.string.ringing) + object Outgoing : CallState(R.string.dialing) + object InCall : CallState(R.string.in_progress) + object Connecting : CallState(R.string.connecting) + object Disconnecting : CallState(R.string.disconnecting) +} \ No newline at end of file diff --git a/app/src/main/java/com/bnyro/contacts/ui/activities/BaseActivity.kt b/app/src/main/java/com/bnyro/contacts/ui/activities/BaseActivity.kt index 278a5ef3..cf1a03e1 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/activities/BaseActivity.kt +++ b/app/src/main/java/com/bnyro/contacts/ui/activities/BaseActivity.kt @@ -11,8 +11,8 @@ import androidx.activity.viewModels import androidx.fragment.app.FragmentActivity import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.get +import com.bnyro.contacts.presentation.screens.calllog.model.CallModel import com.bnyro.contacts.presentation.screens.contacts.model.ContactsModel -import com.bnyro.contacts.presentation.screens.dialer.model.DialerModel import com.bnyro.contacts.presentation.screens.settings.model.ThemeModel import com.bnyro.contacts.presentation.screens.sms.model.SmsModel import com.bnyro.contacts.util.NotificationHelper @@ -24,7 +24,7 @@ abstract class BaseActivity : FragmentActivity() { ContactsModel.Factory } val smsModel by viewModels() - val dialerModel by viewModels() + val callModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) diff --git a/app/src/main/java/com/bnyro/contacts/ui/activities/CallActivity.kt b/app/src/main/java/com/bnyro/contacts/ui/activities/CallActivity.kt index d4268e53..9522074e 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/activities/CallActivity.kt +++ b/app/src/main/java/com/bnyro/contacts/ui/activities/CallActivity.kt @@ -1,19 +1,24 @@ package com.bnyro.contacts.ui.activities +import android.content.BroadcastReceiver import android.content.ComponentName import android.content.Context import android.content.Intent +import android.content.IntentFilter import android.content.ServiceConnection import android.os.Bundle import android.os.IBinder import android.view.Window import android.view.WindowManager +import androidx.activity.ComponentActivity import androidx.activity.compose.setContent +import androidx.activity.viewModels import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.material3.Scaffold import androidx.compose.ui.Modifier +import androidx.core.content.ContextCompat import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.get import com.bnyro.contacts.presentation.screens.dialer.DialerScreen @@ -21,19 +26,42 @@ import com.bnyro.contacts.presentation.screens.dialer.model.DialerModel import com.bnyro.contacts.ui.theme.ConnectYouTheme import com.bnyro.contacts.util.services.CallService -class CallActivity : BaseActivity() { +class CallActivity : ComponentActivity() { lateinit var callService: CallService + val dialerModel: DialerModel by viewModels() private val serviceConnection = object : ServiceConnection { override fun onServiceConnected(className: ComponentName, service: IBinder) { val binder = (service as CallService.LocalBinder) callService = binder.getService() + dialerModel.playDtmfTone = callService::playDtmfTone + dialerModel.acceptCall = callService::acceptCall + dialerModel.cancelCall = callService::cancelCall + + callService.onUpdateState = dialerModel::onState + callService.onCallerInfoUpdate = dialerModel::onCallerInfoUpdate + + callService.updateState() + callService.updateCallerInfo() } override fun onServiceDisconnected(p0: ComponentName?) { dialerModel.playDtmfTone = {} + dialerModel.acceptCall = {} + dialerModel.cancelCall = {} + + callService.onUpdateState = {} + callService.onCallerInfoUpdate = {} + } + } + + private val closeAlertReciever = object : BroadcastReceiver() { + override fun onReceive(context: Context?, intent: Intent?) { + if (intent?.getStringExtra(ACTION_EXTRA_KEY) == CLOSE_ACTION) { + finish() + } } } @@ -43,6 +71,12 @@ class CallActivity : BaseActivity() { window.addFlags(windowFlags) + ContextCompat.registerReceiver( + this, closeAlertReciever, IntentFilter( + CALL_ALERT_CLOSE_ACTION + ), ContextCompat.RECEIVER_NOT_EXPORTED + ) + val dialerModel: DialerModel = ViewModelProvider(this).get() setContent { @@ -53,9 +87,7 @@ class CallActivity : BaseActivity() { .fillMaxSize() .padding(pV) ) { - DialerScreen(contactsModel = contactsModel, dialerModel = dialerModel) { - this@CallActivity.finish() - } + DialerScreen(dialerModel = dialerModel) } } } @@ -70,12 +102,20 @@ class CallActivity : BaseActivity() { } } + override fun onDestroy() { + unregisterReceiver(closeAlertReciever) + super.onDestroy() + } + override fun onStop() { super.onStop() unbindService(serviceConnection) } companion object { + const val CALL_ALERT_CLOSE_ACTION = "com.bnyro.contacts.ALARM_ALERT_CLOSE_ACTION" + const val ACTION_EXTRA_KEY = "action" + const val CLOSE_ACTION = "CLOSE" private const val windowFlags = WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD or WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON or WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON } diff --git a/app/src/main/java/com/bnyro/contacts/ui/activities/MainActivity.kt b/app/src/main/java/com/bnyro/contacts/ui/activities/MainActivity.kt index 49f56c7b..08656dc4 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/activities/MainActivity.kt +++ b/app/src/main/java/com/bnyro/contacts/ui/activities/MainActivity.kt @@ -42,9 +42,9 @@ class MainActivity : BaseActivity() { smsModel.initialAddressAndBody = getInitialSmsAddressAndBody() - dialerModel.initialPhoneNumber = getInitialNumberToDial() + callModel.initialPhoneNumber = getInitialNumberToDial() - val initialTabIndex = dialerModel.initialPhoneNumber?.let { 0 } + val initialTabIndex = callModel.initialPhoneNumber?.let { 0 } ?: smsModel.initialAddressAndBody?.let { 2 } ?: Preferences.getInt(Preferences.homeTabKey, 1) setContent { diff --git a/app/src/main/java/com/bnyro/contacts/util/CallManager.kt b/app/src/main/java/com/bnyro/contacts/util/CallManager.kt deleted file mode 100644 index 20f34365..00000000 --- a/app/src/main/java/com/bnyro/contacts/util/CallManager.kt +++ /dev/null @@ -1,49 +0,0 @@ -package com.bnyro.contacts.util - -import android.telecom.Call - -object CallManager { - private var currentCall: Call? = null - - var callerDisplayNumber = "" - var currentCallState: Int = Call.STATE_RINGING - var onStateChangedListeners: MutableList<(Int) -> Unit> = mutableListOf() - - fun setCall(call: Call?) { - currentCall = call - - if (call == null) return - - callerDisplayNumber = call.details.gatewayInfo?.originalAddress?.schemeSpecificPart - ?: call.details.handle.schemeSpecificPart - } - - fun updateCallState(state: Int) { - this.currentCallState = state - onStateChangedListeners.forEach { - it.invoke(state) - } - } - - fun cancelCall() { - if (currentCall == null) return - - if (currentCallState == Call.STATE_RINGING) { - rejectCall() - } else { - disconnectCall() - } - } - - fun acceptCall() { - currentCall?.let { it.answer(it.details.videoState) } - } - - private fun rejectCall() { - currentCall?.reject(false, "") - } - - private fun disconnectCall() { - currentCall?.disconnect() - } -} \ No newline at end of file diff --git a/app/src/main/java/com/bnyro/contacts/util/receivers/CallActionReceiver.kt b/app/src/main/java/com/bnyro/contacts/util/receivers/CallActionReceiver.kt deleted file mode 100644 index 9f084b2e..00000000 --- a/app/src/main/java/com/bnyro/contacts/util/receivers/CallActionReceiver.kt +++ /dev/null @@ -1,23 +0,0 @@ -package com.bnyro.contacts.util.receivers; - -import android.content.BroadcastReceiver -import android.content.Context -import android.content.Intent -import com.bnyro.contacts.util.CallManager - -class CallActionReceiver : BroadcastReceiver() { - override fun onReceive(context: Context, intent: Intent) { - when (intent.action) { - ACCEPT_CALL -> { - CallManager.acceptCall() - } - - DECLINE_CALL -> CallManager.cancelCall() - } - } - - companion object { - const val ACCEPT_CALL = "com.bnyro.contacts.accept_call" - const val DECLINE_CALL = "com.bnyro.contacts.decline_call" - } -} \ No newline at end of file diff --git a/app/src/main/java/com/bnyro/contacts/util/services/CallService.kt b/app/src/main/java/com/bnyro/contacts/util/services/CallService.kt index c80becb9..bf41d7a7 100644 --- a/app/src/main/java/com/bnyro/contacts/util/services/CallService.kt +++ b/app/src/main/java/com/bnyro/contacts/util/services/CallService.kt @@ -3,8 +3,11 @@ package com.bnyro.contacts.util.services import android.annotation.SuppressLint import android.app.Notification import android.app.PendingIntent +import android.content.BroadcastReceiver import android.content.ContentResolver +import android.content.Context import android.content.Intent +import android.content.IntentFilter import android.graphics.Bitmap import android.net.Uri import android.os.Binder @@ -16,15 +19,16 @@ import android.view.View import android.widget.RemoteViews import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat +import androidx.core.content.ContextCompat +import androidx.core.net.toUri import com.bnyro.contacts.R +import com.bnyro.contacts.domain.model.CallerInfo +import com.bnyro.contacts.presentation.screens.dialer.model.state.CallState import com.bnyro.contacts.ui.activities.CallActivity -import com.bnyro.contacts.util.CallManager import com.bnyro.contacts.util.ContactsHelper import com.bnyro.contacts.util.NotificationHelper import com.bnyro.contacts.util.extension.stringValue -import com.bnyro.contacts.util.receivers.CallActionReceiver -import com.bnyro.contacts.util.receivers.CallActionReceiver.Companion.ACCEPT_CALL -import com.bnyro.contacts.util.receivers.CallActionReceiver.Companion.DECLINE_CALL +import com.google.i18n.phonenumbers.PhoneNumberUtil import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay @@ -36,41 +40,95 @@ class CallService : InCallService() { private val binder = LocalBinder() - private lateinit var acceptPendingIntent: PendingIntent - private lateinit var declinePendingIntent: PendingIntent - val scope = CoroutineScope(Dispatchers.Main) - var currentCall: Call? = null + private val scope = CoroutineScope(Dispatchers.Main) - override fun onCreate() { - super.onCreate() - acceptPendingIntent = createPendingIntent(ACCEPT_CALL, 0) - declinePendingIntent = createPendingIntent(DECLINE_CALL, 1) + private var currentCallState = Call.STATE_DISCONNECTED + private var callerInfo = CallerInfo() + + var onUpdateState: (CallState) -> Unit = {} + + var onCallerInfoUpdate: (CallerInfo) -> Unit = {} + + private val callActionReciever = object : BroadcastReceiver() { + override fun onReceive(context: Context?, intent: Intent?) { + when (intent?.getStringExtra(ACTION_EXTRA_KEY)) { + ACCEPT_CALL -> + acceptCall() + + DECLINE_CALL -> cancelCall() + } + } } - private fun createPendingIntent(action: String, requestCode: Int): PendingIntent { - return PendingIntent.getBroadcast( + override fun onCreate() { + ContextCompat.registerReceiver( this, - requestCode, - Intent(this, CallActionReceiver::class.java).apply { this.action = action }, - PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_MUTABLE + callActionReciever, + IntentFilter(CALL_INTENT_ACTION), + ContextCompat.RECEIVER_EXPORTED ) + super.onCreate() + } + + override fun onDestroy() { + unregisterReceiver(callActionReciever) + super.onDestroy() } private val callCallback = object : Call.Callback() { override fun onStateChanged(call: Call, state: Int) { - CallManager.updateCallState(state) + currentCallState = state + updateState(state) + if (state == Call.STATE_DISCONNECTED) { + val closeCallAlertIntent = Intent(CallActivity.CALL_ALERT_CLOSE_ACTION).apply { + putExtra(CallActivity.ACTION_EXTRA_KEY, CallActivity.CLOSE_ACTION) + `package` = packageName + } + sendBroadcast(closeCallAlertIntent) + } + } + } + + fun updateState(state: Int = currentCallState) { + when (state) { + Call.STATE_RINGING, Call.STATE_PULLING_CALL -> onUpdateState.invoke(CallState.Incoming) + Call.STATE_DIALING -> onUpdateState.invoke(CallState.Outgoing) + Call.STATE_ACTIVE -> onUpdateState.invoke(CallState.InCall) + Call.STATE_DISCONNECTED -> onUpdateState.invoke(CallState.Disconnected) + Call.STATE_CONNECTING -> onUpdateState.invoke(CallState.Connecting) + Call.STATE_DISCONNECTING -> onUpdateState.invoke(CallState.Disconnecting) + else -> {} } } + fun updateCallerInfo() { + onCallerInfoUpdate(callerInfo) + } + @SuppressLint("MissingPermission") override fun onCallAdded(call: Call) { super.onCallAdded(call) + val intent = Intent(applicationContext, CallActivity::class.java).apply { + addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK) + } + startActivity(intent) + call.registerCallback(callCallback) - CallManager.setCall(call) - currentCall = call + + val callerNumber = call.details.gatewayInfo?.originalAddress?.schemeSpecificPart + ?: call.details.handle.schemeSpecificPart + + currentCallState = call.state + updateState(call.state) + + val formattedPhoneNumber = formatPhoneNumber(callerNumber) + callerInfo = CallerInfo( + rawPhoneNumber = callerNumber, + formattedPhoneNumber = formattedPhoneNumber + ) + updateCallerInfo() scope.launch { - val callerNumber = CallManager.callerDisplayNumber val (thumbnailUri, contactName) = withContext(Dispatchers.IO) { getContactName(callerNumber) } @@ -86,10 +144,14 @@ class CallService : InCallService() { NotificationManagerCompat.from(this@CallService) .notify(CALL_NOTIFICATION_ID, notification) + callerInfo = CallerInfo( + callerName = contactName, + rawPhoneNumber = callerNumber, + callerPhoto = thumbnailUri?.toUri(), + formattedPhoneNumber = formattedPhoneNumber + ) + updateCallerInfo() } - val intent = Intent(applicationContext, CallActivity::class.java) - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK) - startActivity(intent) } private fun getContactName(phoneNumber: String): Pair { @@ -114,12 +176,19 @@ class CallService : InCallService() { return null to null } + private fun formatPhoneNumber(number: String): String { + val phoneUtil = PhoneNumberUtil.getInstance() + val phoneNumber = runCatching { phoneUtil.parse(number, null) } + .getOrElse { return number } + return phoneUtil.format(phoneNumber, PhoneNumberUtil.PhoneNumberFormat.NATIONAL) + } + override fun onCallRemoved(call: Call) { super.onCallRemoved(call) - currentCall = null NotificationManagerCompat.from(this).cancel(CALL_NOTIFICATION_ID) call.unregisterCallback(callCallback) - CallManager.setCall(null) + + currentCallState = Call.STATE_DISCONNECTED } private fun buildNotification( @@ -128,6 +197,11 @@ class CallService : InCallService() { callerName: String?, callersPhoto: Bitmap? ): Notification { + val acceptPendingIntent = + getPendingIntent(Intent(CALL_INTENT_ACTION).putExtra(ACTION_EXTRA_KEY, ACCEPT_CALL), 1) + val declinePendingIntent = + getPendingIntent(Intent(CALL_INTENT_ACTION).putExtra(ACTION_EXTRA_KEY, DECLINE_CALL), 2) + val collapsedView = RemoteViews(packageName, R.layout.call_notification).apply { setTextViewText( R.id.notification_caller_name, @@ -167,6 +241,9 @@ class CallService : InCallService() { .build() } + val currentCall: Call? + get() = calls.firstOrNull() + fun playDtmfTone(digit: Char) { scope.launch { currentCall?.playDtmfTone(digit) @@ -175,6 +252,28 @@ class CallService : InCallService() { } } + fun cancelCall() { + if (currentCall == null) return + + if (currentCallState == Call.STATE_RINGING) { + rejectCall() + } else { + disconnectCall() + } + } + + fun acceptCall() { + currentCall?.let { it.answer(it.details.videoState) } + } + + private fun rejectCall() { + currentCall?.reject(false, "") + } + + private fun disconnectCall() { + currentCall?.disconnect() + } + override fun onBind(intent: Intent): IBinder? { if (intent.action == CUSTOM_BIND_ACTION) { return binder; @@ -186,7 +285,19 @@ class CallService : InCallService() { fun getService() = this@CallService } + private fun getPendingIntent(intent: Intent, requestCode: Int): PendingIntent = + PendingIntent.getBroadcast( + this, + requestCode, + intent, + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE + ) + companion object { + const val CALL_INTENT_ACTION = "com.bnyro.contacts.CALL_ACTION" + const val ACTION_EXTRA_KEY = "call_action" + const val ACCEPT_CALL = "ACCEPT" + const val DECLINE_CALL = "DECLINE" const val CALL_NOTIFICATION_ID = 10 const val CUSTOM_BIND_ACTION = "custom_bind" } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e48288e6..490f0ad1 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -145,4 +145,7 @@ Accept Unknown Number Dialpad + Call Ended + Dialing... + Call From \ No newline at end of file From 72239536295a5ec9b8f34ba6a2b38b6a1e4a6d02 Mon Sep 17 00:00:00 2001 From: Suhas Dissanayake Date: Sat, 27 Apr 2024 18:59:11 +0530 Subject: [PATCH 026/159] feat: handle tel uri scheme --- app/src/main/AndroidManifest.xml | 1 + app/src/main/java/com/bnyro/contacts/App.kt | 4 ++ .../repositories/CallLogRepository.kt} | 27 +++++++--- .../contacts/navigation/HomeNavContainer.kt | 3 -- .../bnyro/contacts/navigation/HomeNavHost.kt | 11 ++-- .../bnyro/contacts/navigation/NavContainer.kt | 5 +- .../com/bnyro/contacts/navigation/NavHost.kt | 3 -- .../bnyro/contacts/navigation/NavRoutes.kt | 15 +++++- .../screens/calllog/CallLogsScreen.kt | 22 +++----- .../screens/calllog/model/CallModel.kt | 29 +++++++++-- .../contacts/ui/activities/BaseActivity.kt | 2 - .../contacts/ui/activities/MainActivity.kt | 50 +++++++++++++++---- 12 files changed, 118 insertions(+), 54 deletions(-) rename app/src/main/java/com/bnyro/contacts/{util/CallLogHelper.kt => domain/repositories/CallLogRepository.kt} (58%) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 93b1fe04..a6351e9f 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -59,6 +59,7 @@ diff --git a/app/src/main/java/com/bnyro/contacts/App.kt b/app/src/main/java/com/bnyro/contacts/App.kt index 355a5816..5ce775c8 100644 --- a/app/src/main/java/com/bnyro/contacts/App.kt +++ b/app/src/main/java/com/bnyro/contacts/App.kt @@ -2,6 +2,7 @@ package com.bnyro.contacts import android.app.Application import com.bnyro.contacts.data.database.DatabaseHolder +import com.bnyro.contacts.domain.repositories.CallLogRepository import com.bnyro.contacts.domain.repositories.DeviceContactsRepository import com.bnyro.contacts.domain.repositories.DeviceSmsRepo import com.bnyro.contacts.domain.repositories.LocalContactsRepository @@ -19,6 +20,9 @@ class App : Application() { val localContactsRepository by lazy { LocalContactsRepository(this) } + val callLogRepository by lazy { + CallLogRepository(this) + } lateinit var smsRepo: SmsRepository diff --git a/app/src/main/java/com/bnyro/contacts/util/CallLogHelper.kt b/app/src/main/java/com/bnyro/contacts/domain/repositories/CallLogRepository.kt similarity index 58% rename from app/src/main/java/com/bnyro/contacts/util/CallLogHelper.kt rename to app/src/main/java/com/bnyro/contacts/domain/repositories/CallLogRepository.kt index 2d652679..9e5ca77e 100644 --- a/app/src/main/java/com/bnyro/contacts/util/CallLogHelper.kt +++ b/app/src/main/java/com/bnyro/contacts/domain/repositories/CallLogRepository.kt @@ -1,18 +1,23 @@ -package com.bnyro.contacts.util +package com.bnyro.contacts.domain.repositories import android.Manifest import android.content.Context import android.provider.CallLog import com.bnyro.contacts.domain.model.CallLogEntry +import com.bnyro.contacts.util.PermissionHelper import com.bnyro.contacts.util.extension.intValue import com.bnyro.contacts.util.extension.longValue import com.bnyro.contacts.util.extension.stringValue import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext -object CallLogHelper { - suspend fun getCallLog(context: Context): List = withContext(Dispatchers.IO) { - if (!PermissionHelper.checkPermissions(context, arrayOf(Manifest.permission.READ_CALL_LOG))) return@withContext emptyList() +class CallLogRepository(private val context: Context) { + suspend fun getCallLog(): List = withContext(Dispatchers.IO) { + if (!PermissionHelper.checkPermissions( + context, + arrayOf(Manifest.permission.READ_CALL_LOG) + ) + ) return@withContext emptyList() val callLog = mutableListOf() @@ -34,11 +39,19 @@ object CallLogHelper { return@withContext callLog } - suspend fun deleteAll(context: Context, callLog: List) = withContext(Dispatchers.IO) { - if (!PermissionHelper.checkPermissions(context, arrayOf(Manifest.permission.WRITE_CALL_LOG))) return@withContext + suspend fun deleteAll(callLog: List) = withContext(Dispatchers.IO) { + if (!PermissionHelper.checkPermissions( + context, + arrayOf(Manifest.permission.WRITE_CALL_LOG) + ) + ) return@withContext callLog.distinctBy { it.phoneNumber }.forEach { entry -> - context.contentResolver.delete(CallLog.Calls.CONTENT_URI, "NUMBER=${entry.phoneNumber}", null) + context.contentResolver.delete( + CallLog.Calls.CONTENT_URI, + "NUMBER=${entry.phoneNumber}", + null + ) } } } \ No newline at end of file diff --git a/app/src/main/java/com/bnyro/contacts/navigation/HomeNavContainer.kt b/app/src/main/java/com/bnyro/contacts/navigation/HomeNavContainer.kt index 2dfc74f1..842e290e 100644 --- a/app/src/main/java/com/bnyro/contacts/navigation/HomeNavContainer.kt +++ b/app/src/main/java/com/bnyro/contacts/navigation/HomeNavContainer.kt @@ -23,7 +23,6 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.navigation.NavController import androidx.navigation.compose.rememberNavController -import com.bnyro.contacts.presentation.screens.calllog.model.CallModel import com.bnyro.contacts.presentation.screens.contacts.model.ContactsModel import com.bnyro.contacts.presentation.screens.settings.model.ThemeModel import com.bnyro.contacts.presentation.screens.sms.model.SmsModel @@ -34,7 +33,6 @@ fun HomeNavContainer( onNavigate: (String) -> Unit, smsModel: SmsModel, contactsModel: ContactsModel, - callModel: CallModel, themeModel: ThemeModel ) { val navController = rememberNavController() @@ -108,7 +106,6 @@ fun HomeNavContainer( onNavigate = onNavigate, smsModel = smsModel, contactsModel = contactsModel, - callModel = callModel, themeModel = themeModel ) } diff --git a/app/src/main/java/com/bnyro/contacts/navigation/HomeNavHost.kt b/app/src/main/java/com/bnyro/contacts/navigation/HomeNavHost.kt index 2ae40ae5..e63c2072 100644 --- a/app/src/main/java/com/bnyro/contacts/navigation/HomeNavHost.kt +++ b/app/src/main/java/com/bnyro/contacts/navigation/HomeNavHost.kt @@ -9,7 +9,6 @@ import androidx.navigation.NavHostController import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import com.bnyro.contacts.presentation.screens.calllog.CallLogsScreen -import com.bnyro.contacts.presentation.screens.calllog.model.CallModel import com.bnyro.contacts.presentation.screens.contacts.ContactsPage import com.bnyro.contacts.presentation.screens.contacts.model.ContactsModel import com.bnyro.contacts.presentation.screens.settings.model.ThemeModel @@ -24,7 +23,6 @@ fun HomeNavHost( modifier: Modifier = Modifier, smsModel: SmsModel, contactsModel: ContactsModel, - callModel: CallModel, themeModel: ThemeModel ) { val viewModelStoreOwner: ViewModelStoreOwner = LocalViewModelStoreOwner.current!! @@ -38,10 +36,11 @@ fun HomeNavHost( }) } } - composable(HomeRoutes.Phone.route) { - CompositionLocalProvider(LocalViewModelStoreOwner provides viewModelStoreOwner) { - CallLogsScreen(contactsModel, callModel, themeModel) - } + composable( + HomeRoutes.Phone.route, + deepLinks = HomeRoutes.Phone.deepLinks + ) { + CallLogsScreen(contactsModel, themeModel) } composable(HomeRoutes.Messages.route) { CompositionLocalProvider(LocalViewModelStoreOwner provides viewModelStoreOwner) { diff --git a/app/src/main/java/com/bnyro/contacts/navigation/NavContainer.kt b/app/src/main/java/com/bnyro/contacts/navigation/NavContainer.kt index 9665de01..41472879 100644 --- a/app/src/main/java/com/bnyro/contacts/navigation/NavContainer.kt +++ b/app/src/main/java/com/bnyro/contacts/navigation/NavContainer.kt @@ -5,19 +5,17 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.compose.rememberNavController -import com.bnyro.contacts.presentation.screens.calllog.model.CallModel import com.bnyro.contacts.presentation.screens.contacts.model.ContactsModel import com.bnyro.contacts.presentation.screens.settings.model.ThemeModel import com.bnyro.contacts.presentation.screens.sms.model.SmsModel @Composable fun NavContainer( - initialTab: HomeRoutes + initialTab: HomeRoutes, ) { val navController = rememberNavController() val smsModel: SmsModel = viewModel() val contactsModel: ContactsModel = viewModel(factory = ContactsModel.Factory) - val callModel: CallModel = viewModel() val themeModel: ThemeModel = viewModel() AppNavHost( navController, @@ -26,7 +24,6 @@ fun NavContainer( .fillMaxSize(), smsModel = smsModel, contactsModel = contactsModel, - callModel = callModel, themeModel = themeModel ) } diff --git a/app/src/main/java/com/bnyro/contacts/navigation/NavHost.kt b/app/src/main/java/com/bnyro/contacts/navigation/NavHost.kt index 71326470..609efc1e 100644 --- a/app/src/main/java/com/bnyro/contacts/navigation/NavHost.kt +++ b/app/src/main/java/com/bnyro/contacts/navigation/NavHost.kt @@ -15,7 +15,6 @@ import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.navArgument import com.bnyro.contacts.presentation.screens.about.AboutScreen -import com.bnyro.contacts.presentation.screens.calllog.model.CallModel import com.bnyro.contacts.presentation.screens.contacts.model.ContactsModel import com.bnyro.contacts.presentation.screens.settings.SettingsScreen import com.bnyro.contacts.presentation.screens.settings.model.ThemeModel @@ -29,7 +28,6 @@ fun AppNavHost( initialTab: HomeRoutes, smsModel: SmsModel, contactsModel: ContactsModel, - callModel: CallModel, themeModel: ThemeModel ) { val viewModelStoreOwner: ViewModelStoreOwner = LocalViewModelStoreOwner.current!! @@ -53,7 +51,6 @@ fun AppNavHost( }, smsModel = smsModel, contactsModel = contactsModel, - callModel = callModel, themeModel = themeModel ) } diff --git a/app/src/main/java/com/bnyro/contacts/navigation/NavRoutes.kt b/app/src/main/java/com/bnyro/contacts/navigation/NavRoutes.kt index c3e0d1ed..d1c82cea 100644 --- a/app/src/main/java/com/bnyro/contacts/navigation/NavRoutes.kt +++ b/app/src/main/java/com/bnyro/contacts/navigation/NavRoutes.kt @@ -6,6 +6,8 @@ import androidx.compose.material.icons.rounded.Message import androidx.compose.material.icons.rounded.Person import androidx.compose.material.icons.rounded.Phone import androidx.compose.ui.graphics.vector.ImageVector +import androidx.core.net.toUri +import androidx.navigation.navDeepLink import com.bnyro.contacts.R sealed class NavRoutes( @@ -22,7 +24,18 @@ sealed class HomeRoutes( @StringRes val stringRes: Int, val icon: ImageVector ) { - object Phone : HomeRoutes("phone", R.string.dial, Icons.Rounded.Phone) + object Phone : HomeRoutes("phone", R.string.dial, Icons.Rounded.Phone) { + val phoneNumber = "phoneNumber" + val deepLink = "connectyou://dial/{$phoneNumber}" + val navAction = "com.bnyro.contacts.DIAL" + val deepLinks = listOf(navDeepLink { + uriPattern = deepLink + action = navAction + }) + + fun getDeepLink(number: String) = "connectyou://dial/$number".toUri() + } + object Contacts : HomeRoutes("contacts", R.string.contacts, Icons.Rounded.Person) object Messages : HomeRoutes("messages", R.string.messages, Icons.Rounded.Message) diff --git a/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/CallLogsScreen.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/CallLogsScreen.kt index 60257ce1..ad1030ff 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/CallLogsScreen.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/CallLogsScreen.kt @@ -37,8 +37,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp +import androidx.lifecycle.viewmodel.compose.viewModel import com.bnyro.contacts.R -import com.bnyro.contacts.domain.model.CallLogEntry import com.bnyro.contacts.presentation.components.NothingHere import com.bnyro.contacts.presentation.components.NumberInput import com.bnyro.contacts.presentation.components.PhoneNumberDisplay @@ -47,7 +47,6 @@ import com.bnyro.contacts.presentation.screens.calllog.model.CallModel import com.bnyro.contacts.presentation.screens.contacts.model.ContactsModel import com.bnyro.contacts.presentation.screens.editor.components.ContactIconPlaceholder import com.bnyro.contacts.presentation.screens.settings.model.ThemeModel -import com.bnyro.contacts.util.CallLogHelper import com.bnyro.contacts.util.PermissionHelper import com.bnyro.contacts.util.SmsUtil import com.bnyro.contacts.util.extension.removeLastChar @@ -56,20 +55,18 @@ import com.bnyro.contacts.util.extension.removeLastChar @Composable fun CallLogsScreen( contactsModel: ContactsModel, - dialerModel: CallModel, themeModel: ThemeModel ) { + val callModel: CallModel = viewModel() val context = LocalContext.current var showNumberPicker by remember { - mutableStateOf(false) + mutableStateOf(callModel.initialPhoneNumber != null) } var numberToCall by remember { - mutableStateOf(dialerModel.initialPhoneNumber.orEmpty()) - } - var callLog by remember { - mutableStateOf(emptyList()) + mutableStateOf(callModel.initialPhoneNumber.orEmpty()) } + val subscriptions = remember { SmsUtil.getSubscriptions(context) } @@ -96,10 +93,7 @@ fun CallLogsScreen( context.startActivity(intent) } - LaunchedEffect(Unit) { - callLog = CallLogHelper.getCallLog(context) - } - + val callLog = callModel.callLogs Scaffold( floatingActionButton = { FloatingActionButton( @@ -189,7 +183,7 @@ fun CallLogsScreen( }, subscriptions = subscriptions, onSubscriptionIndexChange = { - chosenSubInfo = subscriptions?.get(it) + chosenSubInfo = subscriptions.get(it) } ) } @@ -197,6 +191,6 @@ fun CallLogsScreen( } LaunchedEffect(Unit) { - dialerModel.requestDefaultDialerApp(context) + callModel.requestDefaultDialerApp(context) } } diff --git a/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/model/CallModel.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/model/CallModel.kt index 77433f9b..83aeee99 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/model/CallModel.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/model/CallModel.kt @@ -1,13 +1,36 @@ package com.bnyro.contacts.presentation.screens.calllog.model import android.Manifest +import android.app.Application import android.content.Context import android.content.Intent import android.telecom.TelecomManager -import androidx.lifecycle.ViewModel +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.neverEqualPolicy +import androidx.compose.runtime.setValue +import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.SavedStateHandle +import androidx.lifecycle.viewModelScope +import com.bnyro.contacts.App +import com.bnyro.contacts.domain.model.CallLogEntry +import com.bnyro.contacts.navigation.HomeRoutes +import kotlinx.coroutines.launch -class CallModel : ViewModel() { - var initialPhoneNumber: String? = null +class CallModel(application: Application, savedStateHandle: SavedStateHandle) : + AndroidViewModel(application) { + val callLogRepository = (application as App).callLogRepository + + val initialPhoneNumber = savedStateHandle.get(HomeRoutes.Phone.phoneNumber) + + var callLogs by mutableStateOf>(emptyList(), policy = neverEqualPolicy()) + private set + + init { + viewModelScope.launch { + callLogs = callLogRepository.getCallLog() + } + } fun requestDefaultDialerApp(context: Context) { val telecomManager = context.getSystemService(Context.TELECOM_SERVICE) as TelecomManager? diff --git a/app/src/main/java/com/bnyro/contacts/ui/activities/BaseActivity.kt b/app/src/main/java/com/bnyro/contacts/ui/activities/BaseActivity.kt index cf1a03e1..00a115ff 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/activities/BaseActivity.kt +++ b/app/src/main/java/com/bnyro/contacts/ui/activities/BaseActivity.kt @@ -11,7 +11,6 @@ import androidx.activity.viewModels import androidx.fragment.app.FragmentActivity import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.get -import com.bnyro.contacts.presentation.screens.calllog.model.CallModel import com.bnyro.contacts.presentation.screens.contacts.model.ContactsModel import com.bnyro.contacts.presentation.screens.settings.model.ThemeModel import com.bnyro.contacts.presentation.screens.sms.model.SmsModel @@ -24,7 +23,6 @@ abstract class BaseActivity : FragmentActivity() { ContactsModel.Factory } val smsModel by viewModels() - val callModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) diff --git a/app/src/main/java/com/bnyro/contacts/ui/activities/MainActivity.kt b/app/src/main/java/com/bnyro/contacts/ui/activities/MainActivity.kt index 08656dc4..91f9a256 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/activities/MainActivity.kt +++ b/app/src/main/java/com/bnyro/contacts/ui/activities/MainActivity.kt @@ -1,5 +1,7 @@ package com.bnyro.contacts.ui.activities +import android.app.PendingIntent +import android.app.TaskStackBuilder import android.content.Intent import android.net.Uri import android.os.Build @@ -42,10 +44,7 @@ class MainActivity : BaseActivity() { smsModel.initialAddressAndBody = getInitialSmsAddressAndBody() - callModel.initialPhoneNumber = getInitialNumberToDial() - - val initialTabIndex = callModel.initialPhoneNumber?.let { 0 } - ?: smsModel.initialAddressAndBody?.let { 2 } + val initialTabIndex = smsModel.initialAddressAndBody?.let { 2 } ?: Preferences.getInt(Preferences.homeTabKey, 1) setContent { ConnectYouTheme(themeModel.themeMode) { @@ -77,6 +76,42 @@ class MainActivity : BaseActivity() { } } } + processIntent(intent) + } + + override fun onNewIntent(intent: Intent?) { + processIntent(intent) + super.onNewIntent(intent) + } + + private fun processIntent(intent: Intent?) { + var number: String? = null + if (intent?.action == Intent.ACTION_INSERT_OR_EDIT) { + number = intent.getStringExtra(Intents.Insert.PHONE) + } else if (intent?.action == Intent.ACTION_DIAL || intent?.action == Intent.ACTION_VIEW) { + if (intent.data?.scheme == "tel") { + number = intent.data?.schemeSpecificPart + } + } + if (number != null) openDialPad(number) + } + + private fun openDialPad(number: String) { + val deepLinkIntent = Intent( + HomeRoutes.Phone.navAction, + HomeRoutes.Phone.getDeepLink(number), + this, + MainActivity::class.java + ) + + val deepLinkPendingIntent: PendingIntent? = TaskStackBuilder.create(this).run { + addNextIntentWithParentStack(deepLinkIntent) + getPendingIntent( + 10, + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE + ) + } + deepLinkPendingIntent?.send() } private fun getInsertContactData(): ContactData? { @@ -118,13 +153,6 @@ class MainActivity : BaseActivity() { return ContactsHelper.normalizePhoneNumber(address) to body } - private fun getInitialNumberToDial(): String? { - if (intent?.action != Intent.ACTION_DIAL) return null - - return intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER) - .takeIf { !it.isNullOrBlank() } - } - private fun getSharedVcfUri(): Uri? { if (intent?.type !in BackupHelper.vCardMimeTypes) return null From 4ce8f8285ae77b57b429658becf4069dc199fd03 Mon Sep 17 00:00:00 2001 From: Suhas Dissanayake Date: Sat, 27 Apr 2024 22:40:43 +0530 Subject: [PATCH 027/159] feat: dtmf tones for dialpad --- .../screens/calllog/CallLogsScreen.kt | 59 +++----------- .../screens/calllog/model/CallModel.kt | 79 ++++++++++++++++++- 2 files changed, 87 insertions(+), 51 deletions(-) diff --git a/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/CallLogsScreen.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/CallLogsScreen.kt index ad1030ff..3b997aa9 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/CallLogsScreen.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/CallLogsScreen.kt @@ -1,10 +1,5 @@ package com.bnyro.contacts.presentation.screens.calllog -import android.content.ComponentName -import android.content.Intent -import android.net.Uri -import android.telecom.PhoneAccountHandle -import android.telecom.TelecomManager import android.text.format.DateUtils import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column @@ -47,9 +42,7 @@ import com.bnyro.contacts.presentation.screens.calllog.model.CallModel import com.bnyro.contacts.presentation.screens.contacts.model.ContactsModel import com.bnyro.contacts.presentation.screens.editor.components.ContactIconPlaceholder import com.bnyro.contacts.presentation.screens.settings.model.ThemeModel -import com.bnyro.contacts.util.PermissionHelper -import com.bnyro.contacts.util.SmsUtil -import com.bnyro.contacts.util.extension.removeLastChar + @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -63,35 +56,6 @@ fun CallLogsScreen( var showNumberPicker by remember { mutableStateOf(callModel.initialPhoneNumber != null) } - var numberToCall by remember { - mutableStateOf(callModel.initialPhoneNumber.orEmpty()) - } - - val subscriptions = remember { - SmsUtil.getSubscriptions(context) - } - - var chosenSubInfo = remember { - subscriptions.firstOrNull() - } - - fun callNumber(number: String) { - if (!PermissionHelper.checkPermissions(context, CallModel.phonePerms)) return - - val intent = Intent(Intent.ACTION_CALL, Uri.parse("tel:$number")).apply { - chosenSubInfo?.let { - val phoneAccountHandle = PhoneAccountHandle( - ComponentName( - "com.android.phone", - "com.android.services.telephony.TelephonyConnectionService" - ), - it.subscriptionId.toString() - ) - putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle) - } - } - context.startActivity(intent) - } val callLog = callModel.callLogs Scaffold( @@ -151,7 +115,7 @@ fun CallLogsScreen( contact?.displayName ?: it.phoneNumber ) ) { - callNumber(it.phoneNumber) + callModel.callNumber(it.phoneNumber) } } } @@ -163,6 +127,7 @@ fun CallLogsScreen( if (showNumberPicker) { val state = rememberModalBottomSheetState(skipPartiallyExpanded = true) + ModalBottomSheet(onDismissRequest = { showNumberPicker = false }, sheetState = state) { Column( modifier = Modifier @@ -170,21 +135,15 @@ fun CallLogsScreen( .padding(horizontal = 8.dp), horizontalAlignment = Alignment.CenterHorizontally ) { - PhoneNumberDisplay(displayText = numberToCall) + PhoneNumberDisplay(displayText = callModel.numberToCall) NumberInput( - onNumberInput = { - numberToCall += it - }, - onDelete = { - numberToCall = numberToCall.removeLastChar() - }, + onNumberInput = callModel::onNumberInput, + onDelete = callModel::onBackSpace, onDial = { - callNumber(numberToCall) + callModel.callNumber() }, - subscriptions = subscriptions, - onSubscriptionIndexChange = { - chosenSubInfo = subscriptions.get(it) - } + subscriptions = callModel.subscriptions, + onSubscriptionIndexChange = callModel::onSubscriptionIndexChange ) } } diff --git a/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/model/CallModel.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/model/CallModel.kt index 83aeee99..d95d00b6 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/model/CallModel.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/model/CallModel.kt @@ -2,8 +2,13 @@ package com.bnyro.contacts.presentation.screens.calllog.model import android.Manifest import android.app.Application +import android.content.ComponentName import android.content.Context import android.content.Intent +import android.media.AudioManager +import android.media.ToneGenerator +import android.net.Uri +import android.telecom.PhoneAccountHandle import android.telecom.TelecomManager import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -15,23 +20,73 @@ import androidx.lifecycle.viewModelScope import com.bnyro.contacts.App import com.bnyro.contacts.domain.model.CallLogEntry import com.bnyro.contacts.navigation.HomeRoutes +import com.bnyro.contacts.util.PermissionHelper +import com.bnyro.contacts.util.SmsUtil +import com.bnyro.contacts.util.extension.removeLastChar import kotlinx.coroutines.launch -class CallModel(application: Application, savedStateHandle: SavedStateHandle) : +class CallModel(private val application: Application, savedStateHandle: SavedStateHandle) : AndroidViewModel(application) { val callLogRepository = (application as App).callLogRepository val initialPhoneNumber = savedStateHandle.get(HomeRoutes.Phone.phoneNumber) + var numberToCall: String by mutableStateOf(initialPhoneNumber.orEmpty()) + private set + var callLogs by mutableStateOf>(emptyList(), policy = neverEqualPolicy()) private set + private val toneGenerator = ToneGenerator( + AudioManager.STREAM_SYSTEM, + 100 + ) + + val subscriptions = + SmsUtil.getSubscriptions(application.applicationContext) + + var chosenSubInfo = + subscriptions.firstOrNull() + + init { viewModelScope.launch { callLogs = callLogRepository.getCallLog() } } + fun onSubscriptionIndexChange(index: Int) { + chosenSubInfo = subscriptions.get(index) + } + + fun onNumberInput(digit: String) { + numberToCall += digit + playToneForDigit(digit) + } + + fun onBackSpace() { + numberToCall = numberToCall.removeLastChar() + } + + fun callNumber(number: String = numberToCall) { + if (!PermissionHelper.checkPermissions(application.applicationContext, phonePerms)) return + + val intent = Intent(Intent.ACTION_CALL, Uri.parse("tel:$number")).apply { + flags = Intent.FLAG_ACTIVITY_NEW_TASK + chosenSubInfo?.let { + val phoneAccountHandle = PhoneAccountHandle( + ComponentName( + "com.android.phone", + "com.android.services.telephony.TelephonyConnectionService" + ), + it.subscriptionId.toString() + ) + putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle) + } + } + application.applicationContext.startActivity(intent) + } + fun requestDefaultDialerApp(context: Context) { val telecomManager = context.getSystemService(Context.TELECOM_SERVICE) as TelecomManager? val isAlreadyDefaultDialer = @@ -46,6 +101,28 @@ class CallModel(application: Application, savedStateHandle: SavedStateHandle) : } } + fun playToneForDigit(digit: String) { + val numericDigit = when (digit) { + "0" -> ToneGenerator.TONE_DTMF_0 + "1" -> ToneGenerator.TONE_DTMF_1 + "2" -> ToneGenerator.TONE_DTMF_2 + "3" -> ToneGenerator.TONE_DTMF_3 + "4" -> ToneGenerator.TONE_DTMF_4 + "5" -> ToneGenerator.TONE_DTMF_5 + "6" -> ToneGenerator.TONE_DTMF_6 + "7" -> ToneGenerator.TONE_DTMF_7 + "8" -> ToneGenerator.TONE_DTMF_8 + "9" -> ToneGenerator.TONE_DTMF_9 + "#" -> ToneGenerator.TONE_DTMF_P + "*" -> ToneGenerator.TONE_DTMF_S + else -> error("Invalid digit: $digit") + } + + val durationMs = 100 + toneGenerator.stopTone() + toneGenerator.startTone(numericDigit, durationMs) + } + companion object { val phonePerms = arrayOf( Manifest.permission.CALL_PHONE, From 9091cba57d3e16d9a0d1e47c25d57b1c7aa72324 Mon Sep 17 00:00:00 2001 From: Suhas Dissanayake Date: Sat, 27 Apr 2024 23:07:13 +0530 Subject: [PATCH 028/159] feat: proximity sensor for calls --- .../contacts/ui/activities/CallActivity.kt | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/app/src/main/java/com/bnyro/contacts/ui/activities/CallActivity.kt b/app/src/main/java/com/bnyro/contacts/ui/activities/CallActivity.kt index 9522074e..bf83618a 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/activities/CallActivity.kt +++ b/app/src/main/java/com/bnyro/contacts/ui/activities/CallActivity.kt @@ -8,6 +8,7 @@ import android.content.IntentFilter import android.content.ServiceConnection import android.os.Bundle import android.os.IBinder +import android.os.PowerManager import android.view.Window import android.view.WindowManager import androidx.activity.ComponentActivity @@ -31,6 +32,15 @@ class CallActivity : ComponentActivity() { lateinit var callService: CallService val dialerModel: DialerModel by viewModels() + val powerManager by lazy { getSystemService(PowerManager::class.java) } + + val wakeLock by lazy { + powerManager.newWakeLock( + PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK, + this::class.simpleName + ) + } + private val serviceConnection = object : ServiceConnection { override fun onServiceConnected(className: ComponentName, service: IBinder) { val binder = (service as CallService.LocalBinder) @@ -69,6 +79,10 @@ class CallActivity : ComponentActivity() { super.onCreate(savedInstanceState) requestWindowFeature(Window.FEATURE_NO_TITLE) + if (!wakeLock.isHeld) { + wakeLock.acquire(10 * 60 * 1000L /*10 minutes*/) + } + window.addFlags(windowFlags) ContextCompat.registerReceiver( @@ -107,6 +121,18 @@ class CallActivity : ComponentActivity() { super.onDestroy() } + override fun onResume() { + if (!wakeLock.isHeld) { + wakeLock.acquire(10 * 60 * 1000L /*10 minutes*/) + } + super.onResume() + } + + override fun onPause() { + wakeLock.release() + super.onPause() + } + override fun onStop() { super.onStop() unbindService(serviceConnection) From 86ff047071e9992adf790eaefc6a9aa31df28d41 Mon Sep 17 00:00:00 2001 From: Suhas Dissanayake Date: Sun, 28 Apr 2024 17:07:01 +0530 Subject: [PATCH 029/159] style: fix call screen design --- .../screens/dialer/DialerScreen.kt | 189 +++++++++++++----- app/src/main/res/drawable/ic_person.xml | 56 ++++++ app/src/main/res/values/strings.xml | 1 + 3 files changed, 194 insertions(+), 52 deletions(-) create mode 100644 app/src/main/res/drawable/ic_person.xml diff --git a/app/src/main/java/com/bnyro/contacts/presentation/screens/dialer/DialerScreen.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/dialer/DialerScreen.kt index ec02e9cb..fc1579a3 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/screens/dialer/DialerScreen.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/dialer/DialerScreen.kt @@ -1,8 +1,8 @@ package com.bnyro.contacts.presentation.screens.dialer -import android.net.Uri import androidx.compose.foundation.Image import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -12,7 +12,6 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.LazyVerticalGrid import androidx.compose.foundation.shape.CircleShape @@ -23,9 +22,9 @@ import androidx.compose.material.icons.filled.Call import androidx.compose.material.icons.filled.CallEnd import androidx.compose.material.icons.rounded.Dialpad import androidx.compose.material.icons.rounded.MicOff -import androidx.compose.material.icons.rounded.Person import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.ExtendedFloatingActionButton +import androidx.compose.material3.FloatingActionButton +import androidx.compose.material3.FloatingActionButtonDefaults import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.ModalBottomSheet @@ -40,8 +39,14 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import coil.compose.AsyncImage @@ -94,7 +99,7 @@ private fun InCallScreen( callStateText: String, callerNumber: String, callerName: String? = null, - callerPhoto: Uri? = null, + callerPhoto: Any? = null, muteState: Boolean, speakerState: Boolean, onToggleMute: () -> Unit, @@ -106,16 +111,18 @@ private fun InCallScreen( var showDialPad by remember { mutableStateOf(false) } Column( - modifier = Modifier.fillMaxSize(), - horizontalAlignment = Alignment.CenterHorizontally + modifier = Modifier + .fillMaxSize() + .background(MaterialTheme.colorScheme.surfaceContainerLow), + horizontalAlignment = Alignment.CenterHorizontally, ) { Spacer(modifier = Modifier.height(50.dp)) Box( modifier = Modifier - .size(128.dp) + .size(150.dp) .background( shape = CircleShape, - color = MaterialTheme.colorScheme.primary + color = MaterialTheme.colorScheme.surfaceVariant ) ) { if (callerPhoto == null) { @@ -124,8 +131,9 @@ private fun InCallScreen( .fillMaxSize() .clip(CircleShape), contentScale = ContentScale.Fit, - imageVector = Icons.Rounded.Person, - contentDescription = null + painter = painterResource(id = R.drawable.ic_person), + contentDescription = null, + colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onSurfaceVariant) ) } else { AsyncImage( @@ -139,7 +147,7 @@ private fun InCallScreen( } } - Spacer(modifier = Modifier.height(10.dp)) + Spacer(modifier = Modifier.height(40.dp)) Text( text = callStateText, color = MaterialTheme.colorScheme.primary, @@ -148,12 +156,26 @@ private fun InCallScreen( Spacer(modifier = Modifier.height(10.dp)) Text( text = callerName ?: callerNumber, + color = MaterialTheme.colorScheme.secondary, + fontWeight = FontWeight.Medium, fontSize = 24.sp ) - Spacer(modifier = Modifier.height(5.dp)) - Text(text = callStateText) + if (callerName != null) { + Spacer(modifier = Modifier.height(10.dp)) + Text( + text = callerNumber, + color = MaterialTheme.colorScheme.secondary, + fontFamily = FontFamily.Monospace, + fontSize = 24.sp + ) + } Spacer(modifier = Modifier.weight(1f)) - LazyVerticalGrid(columns = GridCells.Fixed(3), modifier = Modifier.fillMaxWidth()) { + LazyVerticalGrid( + columns = GridCells.Fixed(3), + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 32.dp) + ) { item { DialerButton( isEnabled = muteState, @@ -176,23 +198,23 @@ private fun InCallScreen( DialerButton( isEnabled = showDialPad, icon = Icons.Rounded.Dialpad, - hint = stringResource(R.string.dial_pad) + hint = stringResource(R.string.key_pad) ) { showDialPad = true } } } - Spacer(modifier = Modifier.weight(1f)) - Row { - ExtendedFloatingActionButton( - onClick = onCancelCall, - containerColor = MaterialTheme.colorScheme.error, - contentColor = MaterialTheme.colorScheme.onError, - shape = RoundedCornerShape(50) - ) { - Icon(Icons.Default.CallEnd, contentDescription = null) - } + FloatingActionButton( + onClick = onCancelCall, + containerColor = MaterialTheme.colorScheme.error, + contentColor = MaterialTheme.colorScheme.onError, + shape = RoundedCornerShape(50), + elevation = FloatingActionButtonDefaults.elevation( + 0.dp, 0.dp, 0.dp, 0.dp + ) + ) { + Icon(Icons.Default.CallEnd, contentDescription = null) } Spacer(modifier = Modifier.height(50.dp)) } @@ -219,62 +241,125 @@ private fun InCallScreen( private fun CallAlertScreen( callerNumber: String, callerName: String? = null, - callerPhoto: Uri? = null, + callerPhoto: Any? = null, onAcceptCall: () -> Unit, onDeclineCall: () -> Unit ) { Column( - modifier = Modifier.fillMaxSize(), + modifier = Modifier + .fillMaxSize() + .background(MaterialTheme.colorScheme.surfaceContainerLow), horizontalAlignment = Alignment.CenterHorizontally ) { Spacer(modifier = Modifier.height(50.dp)) Text( text = stringResource(R.string.call_from), - color = MaterialTheme.colorScheme.primary, - fontSize = 16.sp + color = MaterialTheme.colorScheme.outline, + fontSize = 24.sp ) - if (callerPhoto != null) { - Spacer(modifier = Modifier.height(10.dp)) - AsyncImage( - callerPhoto, - modifier = Modifier - .size(256.dp) - .clip(CircleShape), - contentDescription = null, - contentScale = ContentScale.Crop - ) + Spacer(modifier = Modifier.height(40.dp)) + Box( + modifier = Modifier + .size(250.dp) + .background( + shape = CircleShape, + color = MaterialTheme.colorScheme.surfaceVariant + ) + ) { + if (callerPhoto == null) { + Image( + modifier = Modifier + .fillMaxSize() + .clip(CircleShape), + contentScale = ContentScale.Fit, + painter = painterResource(id = R.drawable.ic_person), + contentDescription = null, + colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onSurfaceVariant) + ) + } else { + AsyncImage( + callerPhoto, + modifier = Modifier + .fillMaxSize() + .clip(CircleShape), + contentDescription = null, + contentScale = ContentScale.Crop + ) + } } - Spacer(modifier = Modifier.height(10.dp)) + Spacer(modifier = Modifier.height(40.dp)) Text( text = callerName ?: callerNumber, + color = MaterialTheme.colorScheme.secondary, + fontWeight = FontWeight.Medium, fontSize = 24.sp ) if (callerName != null) { - Spacer(modifier = Modifier.height(5.dp)) - Text(text = callerNumber) + Spacer(modifier = Modifier.height(10.dp)) + Text( + text = callerNumber, + color = MaterialTheme.colorScheme.secondary, + fontFamily = FontFamily.Monospace, + fontSize = 24.sp + ) } Spacer(modifier = Modifier.weight(1f)) - Row { - ExtendedFloatingActionButton( + Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceEvenly) { + FloatingActionButton( onClick = onAcceptCall, - containerColor = MaterialTheme.colorScheme.primary, - contentColor = MaterialTheme.colorScheme.onPrimary, - shape = RoundedCornerShape(50) + containerColor = Color(0xFF348540), + contentColor = Color.White, + shape = RoundedCornerShape(50), + elevation = FloatingActionButtonDefaults.elevation( + 0.dp, 0.dp, 0.dp, 0.dp + ) ) { Icon(Icons.Default.Call, contentDescription = null) } - Spacer(modifier = Modifier.width(20.dp)) - - ExtendedFloatingActionButton( + FloatingActionButton( onClick = onDeclineCall, containerColor = MaterialTheme.colorScheme.error, contentColor = MaterialTheme.colorScheme.onError, - shape = RoundedCornerShape(50) + shape = RoundedCornerShape(50), + elevation = FloatingActionButtonDefaults.elevation( + 0.dp, 0.dp, 0.dp, 0.dp + ) ) { Icon(Icons.Default.CallEnd, contentDescription = null) } } Spacer(modifier = Modifier.height(50.dp)) } +} + +@Preview +@Composable +private fun InCallScreenPreview() { + InCallScreen( + callStateText = "Connecting..", + callerNumber = "1234567890", + callerName = "Test Caller", + callerPhoto = null, + muteState = false, + speakerState = false, + onToggleMute = {}, + onToggleSpeaker = {}, + onCancelCall = {}, + dialpadNumber = "", + onDialpadButtonPress = {} + ) +} + +@Preview +@Composable +private fun CallAlertScreenPreview() { + CallAlertScreen( + callerNumber = "1234567890", + callerName = "Test Caller", + callerPhoto = null, + onAcceptCall = {}, + onDeclineCall = {} + ) + } \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_person.xml b/app/src/main/res/drawable/ic_person.xml new file mode 100644 index 00000000..48497443 --- /dev/null +++ b/app/src/main/res/drawable/ic_person.xml @@ -0,0 +1,56 @@ + + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 490f0ad1..94e7044d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -148,4 +148,5 @@ Call Ended Dialing... Call From + Keypad \ No newline at end of file From 8d3932483633fef017e49d6f7647b22b532d4fdb Mon Sep 17 00:00:00 2001 From: Suhas Dissanayake Date: Sun, 12 May 2024 15:45:42 +0530 Subject: [PATCH 030/159] feat: show search suggestions when dialing --- app/src/main/java/com/bnyro/contacts/App.kt | 5 +- .../contacts/domain/model/BasicContactData.kt | 15 ++ .../repositories/PhoneLookupRepository.kt | 128 ++++++++++++++++++ .../screens/calllog/CallLogsScreen.kt | 10 +- .../calllog}/components/NumberInput.kt | 59 +++++++- .../screens/calllog/model/CallModel.kt | 22 +++ .../screens/dialer/DialerScreen.kt | 4 +- .../com/bnyro/contacts/util/ContactsHelper.kt | 87 ++++++++++-- .../contacts/util/services/CallService.kt | 44 ++---- 9 files changed, 315 insertions(+), 59 deletions(-) create mode 100644 app/src/main/java/com/bnyro/contacts/domain/model/BasicContactData.kt create mode 100644 app/src/main/java/com/bnyro/contacts/domain/repositories/PhoneLookupRepository.kt rename app/src/main/java/com/bnyro/contacts/presentation/{ => screens/calllog}/components/NumberInput.kt (81%) diff --git a/app/src/main/java/com/bnyro/contacts/App.kt b/app/src/main/java/com/bnyro/contacts/App.kt index 5ce775c8..40418c58 100644 --- a/app/src/main/java/com/bnyro/contacts/App.kt +++ b/app/src/main/java/com/bnyro/contacts/App.kt @@ -7,6 +7,7 @@ import com.bnyro.contacts.domain.repositories.DeviceContactsRepository import com.bnyro.contacts.domain.repositories.DeviceSmsRepo import com.bnyro.contacts.domain.repositories.LocalContactsRepository import com.bnyro.contacts.domain.repositories.LocalSmsRepo +import com.bnyro.contacts.domain.repositories.PhoneLookupRepository import com.bnyro.contacts.domain.repositories.SmsRepository import com.bnyro.contacts.util.NotificationHelper import com.bnyro.contacts.util.Preferences @@ -23,7 +24,9 @@ class App : Application() { val callLogRepository by lazy { CallLogRepository(this) } - + val phoneLookupRepository by lazy { + PhoneLookupRepository(this) + } lateinit var smsRepo: SmsRepository fun initSmsRepo() { diff --git a/app/src/main/java/com/bnyro/contacts/domain/model/BasicContactData.kt b/app/src/main/java/com/bnyro/contacts/domain/model/BasicContactData.kt new file mode 100644 index 00000000..4c981241 --- /dev/null +++ b/app/src/main/java/com/bnyro/contacts/domain/model/BasicContactData.kt @@ -0,0 +1,15 @@ +package com.bnyro.contacts.domain.model + +import android.net.Uri + +/** + * Data class containing contact name, number and thumbnail + * @property number phone number without any formatting + * @property name contact display name + * @property thumbnail contact thumbnail [Uri] + */ +data class BasicContactData( + val number: String, + val name: String? = null, + val thumbnail: Uri? = null +) diff --git a/app/src/main/java/com/bnyro/contacts/domain/repositories/PhoneLookupRepository.kt b/app/src/main/java/com/bnyro/contacts/domain/repositories/PhoneLookupRepository.kt new file mode 100644 index 00000000..5d5fefe4 --- /dev/null +++ b/app/src/main/java/com/bnyro/contacts/domain/repositories/PhoneLookupRepository.kt @@ -0,0 +1,128 @@ +package com.bnyro.contacts.domain.repositories + +import android.content.Context +import android.net.Uri +import android.provider.ContactsContract.CommonDataKinds.Phone +import android.provider.ContactsContract.PhoneLookup +import androidx.core.net.toUri +import com.bnyro.contacts.domain.model.BasicContactData +import com.bnyro.contacts.util.extension.stringValue +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext + +class PhoneLookupRepository(private val context: Context) { + /** + * Get all contacts with a number equal to or containing a given number. + * @param number The number to search for + * @return A list containing simple contact data + */ + suspend fun getContactsWithNumber(number: String): List = + withContext(Dispatchers.IO) { + val contacts = mutableListOf() + val uri = Phone.CONTENT_URI + val projection = arrayOf( + Phone.PHOTO_THUMBNAIL_URI, + Phone.DISPLAY_NAME, + Phone.NUMBER + ) + val selection = "${Phone.NUMBER} LIKE ?" + val selectionArgs = arrayOf("%$number%") + + val cursor = + context.contentResolver.query(uri, projection, selection, selectionArgs, null) + + cursor?.use { + while (it.moveToNext()) { + val name = it.stringValue(Phone.DISPLAY_NAME) + val phoneNumber = it.stringValue(Phone.NUMBER) + val photoUri = it.stringValue(Phone.PHOTO_THUMBNAIL_URI)?.toUri() + + if (phoneNumber != null) { + contacts.add( + BasicContactData( + number = phoneNumber, + name = name, + thumbnail = photoUri + ) + ) + } + } + } + + contacts + } + + /** + * Get all contacts with a name containing the given query + * @param nameQuery The name to search for + * @return A list containing simple contact data + */ + suspend fun getContactsWithName(nameQuery: String): List = + withContext(Dispatchers.IO) { + val contacts = mutableListOf() + val uri = Phone.CONTENT_URI + val projection = arrayOf( + Phone.PHOTO_THUMBNAIL_URI, + Phone.DISPLAY_NAME, + Phone.NUMBER + ) + val selection = "${Phone.DISPLAY_NAME} LIKE ?" + val selectionArgs = arrayOf("%$nameQuery%") + + val cursor = + context.contentResolver.query(uri, projection, selection, selectionArgs, null) + + cursor?.use { + while (it.moveToNext()) { + val name = it.stringValue(Phone.DISPLAY_NAME) + val phoneNumber = it.stringValue(Phone.NUMBER) + val photoUri = it.stringValue(Phone.PHOTO_THUMBNAIL_URI)?.toUri() + + if (phoneNumber != null) { + contacts.add( + BasicContactData( + number = phoneNumber, + name = name, + thumbnail = photoUri + ) + ) + } + } + } + + contacts + } + + /**Get caller id from a number + * @param phoneNumber The number of the caller + * @return A simple contact data containing name phone and thumbnail + */ + suspend fun getContactByNumber(phoneNumber: String): BasicContactData = + withContext(Dispatchers.IO) { + val uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(phoneNumber)) + val query = + context.contentResolver.query( + uri, + arrayOf(PhoneLookup.DISPLAY_NAME, PhoneLookup.PHOTO_THUMBNAIL_URI), + null, + null, + null + ) + + query?.use { cursor -> + if (cursor.moveToFirst()) { + BasicContactData( + number = phoneNumber, + name = cursor.stringValue( + PhoneLookup.DISPLAY_NAME + ), + thumbnail = cursor.stringValue(PhoneLookup.PHOTO_THUMBNAIL_URI)?.toUri() + ) + + } + } + BasicContactData( + number = phoneNumber + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/CallLogsScreen.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/CallLogsScreen.kt index 3b997aa9..728d24aa 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/CallLogsScreen.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/CallLogsScreen.kt @@ -35,9 +35,9 @@ import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel import com.bnyro.contacts.R import com.bnyro.contacts.presentation.components.NothingHere -import com.bnyro.contacts.presentation.components.NumberInput -import com.bnyro.contacts.presentation.components.PhoneNumberDisplay import com.bnyro.contacts.presentation.features.ConfirmationDialog +import com.bnyro.contacts.presentation.screens.calllog.components.NumberInput +import com.bnyro.contacts.presentation.screens.calllog.components.PhoneNumberDisplay import com.bnyro.contacts.presentation.screens.calllog.model.CallModel import com.bnyro.contacts.presentation.screens.contacts.model.ContactsModel import com.bnyro.contacts.presentation.screens.editor.components.ContactIconPlaceholder @@ -135,7 +135,11 @@ fun CallLogsScreen( .padding(horizontal = 8.dp), horizontalAlignment = Alignment.CenterHorizontally ) { - PhoneNumberDisplay(displayText = callModel.numberToCall) + PhoneNumberDisplay( + displayText = callModel.numberToCall, + contacts = callModel.contacts, + onClickContact = callModel::setPhoneNumberContact + ) NumberInput( onNumberInput = callModel::onNumberInput, onDelete = callModel::onBackSpace, diff --git a/app/src/main/java/com/bnyro/contacts/presentation/components/NumberInput.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/components/NumberInput.kt similarity index 81% rename from app/src/main/java/com/bnyro/contacts/presentation/components/NumberInput.kt rename to app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/components/NumberInput.kt index a69f642b..4005ea8e 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/components/NumberInput.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/components/NumberInput.kt @@ -1,10 +1,11 @@ -package com.bnyro.contacts.presentation.components +package com.bnyro.contacts.presentation.screens.calllog.components import android.annotation.SuppressLint import android.telephony.SubscriptionInfo import android.view.SoundEffectConstants import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background +import androidx.compose.foundation.clickable import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.horizontalScroll import androidx.compose.foundation.layout.Arrangement @@ -16,10 +17,13 @@ import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.aspectRatio +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape @@ -27,6 +31,7 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.rounded.Backspace import androidx.compose.material.icons.rounded.Call import androidx.compose.material3.Icon +import androidx.compose.material3.ListItem import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedButton import androidx.compose.material3.Text @@ -42,12 +47,15 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.hapticfeedback.HapticFeedbackType +import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalHapticFeedback import androidx.compose.ui.platform.LocalView import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp +import coil.compose.AsyncImage import com.bnyro.contacts.R +import com.bnyro.contacts.domain.model.BasicContactData val keypadNumbers = arrayOf( arrayOf("1", "2", "3"), @@ -221,7 +229,11 @@ fun RowScope.NumpadButton( } @Composable -fun ColumnScope.PhoneNumberDisplay(displayText: String) { +fun ColumnScope.PhoneNumberDisplay( + displayText: String, + contacts: List, + onClickContact: (BasicContactData) -> Unit +) { Column( modifier = Modifier .fillMaxWidth() @@ -234,7 +246,9 @@ fun ColumnScope.PhoneNumberDisplay(displayText: String) { .weight(1f), verticalArrangement = Arrangement.spacedBy(8.dp, alignment = Alignment.Bottom) ) { - // TODO: Contact Suggestions + items(contacts, key = { it.number }) { contact -> + ContactSuggestionItem(onClickContact, contact) + } } val scroll = rememberScrollState() Row( @@ -259,6 +273,45 @@ fun ColumnScope.PhoneNumberDisplay(displayText: String) { } } +@Composable +private fun ContactSuggestionItem( + onClickContact: (BasicContactData) -> Unit, + contact: BasicContactData +) { + ListItem( + modifier = Modifier.clickable { onClickContact.invoke(contact) }, + headlineContent = { Text(text = contact.number) }, leadingContent = { + Box( + modifier = Modifier + .size(48.dp) + .clip(CircleShape) + .background(MaterialTheme.colorScheme.surfaceVariant), + contentAlignment = Alignment.Center + ) { + if (contact.thumbnail != null) { + AsyncImage( + model = contact.thumbnail, + contentDescription = null, + modifier = Modifier.fillMaxSize(), + alignment = Alignment.Center, + contentScale = ContentScale.Crop + ) + } else { + Icon( + modifier = Modifier.size(48.dp), + imageVector = Icons.Rounded.Call, + contentDescription = null, + tint = MaterialTheme.colorScheme.onSurfaceVariant + ) + } + } + }, supportingContent = { + if (contact.name != null) { + Text(text = contact.name) + } + }) +} + @Composable fun PhoneNumberOnlyDisplay(displayText: String) { Column( diff --git a/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/model/CallModel.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/model/CallModel.kt index d95d00b6..bcd1bf68 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/model/CallModel.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/model/CallModel.kt @@ -18,6 +18,7 @@ import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.viewModelScope import com.bnyro.contacts.App +import com.bnyro.contacts.domain.model.BasicContactData import com.bnyro.contacts.domain.model.CallLogEntry import com.bnyro.contacts.navigation.HomeRoutes import com.bnyro.contacts.util.PermissionHelper @@ -28,6 +29,7 @@ import kotlinx.coroutines.launch class CallModel(private val application: Application, savedStateHandle: SavedStateHandle) : AndroidViewModel(application) { val callLogRepository = (application as App).callLogRepository + val phoneLookupRepository = (application as App).phoneLookupRepository val initialPhoneNumber = savedStateHandle.get(HomeRoutes.Phone.phoneNumber) @@ -37,6 +39,9 @@ class CallModel(private val application: Application, savedStateHandle: SavedSta var callLogs by mutableStateOf>(emptyList(), policy = neverEqualPolicy()) private set + var contacts by mutableStateOf>(emptyList(), policy = neverEqualPolicy()) + private set + private val toneGenerator = ToneGenerator( AudioManager.STREAM_SYSTEM, 100 @@ -62,10 +67,21 @@ class CallModel(private val application: Application, savedStateHandle: SavedSta fun onNumberInput(digit: String) { numberToCall += digit playToneForDigit(digit) + if (numberToCall.length > 3) { + searchPhoneNumber(numberToCall) + } + } + + fun setPhoneNumberContact(contact: BasicContactData) { + numberToCall = contact.number + contacts = listOf(contact) } fun onBackSpace() { numberToCall = numberToCall.removeLastChar() + if (numberToCall.length > 3) { + searchPhoneNumber(numberToCall) + } } fun callNumber(number: String = numberToCall) { @@ -123,6 +139,12 @@ class CallModel(private val application: Application, savedStateHandle: SavedSta toneGenerator.startTone(numericDigit, durationMs) } + private fun searchPhoneNumber(number: String) { + viewModelScope.launch { + contacts = phoneLookupRepository.getContactsWithNumber(number) + } + } + companion object { val phonePerms = arrayOf( Manifest.permission.CALL_PHONE, diff --git a/app/src/main/java/com/bnyro/contacts/presentation/screens/dialer/DialerScreen.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/dialer/DialerScreen.kt index fc1579a3..103d3cea 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/screens/dialer/DialerScreen.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/dialer/DialerScreen.kt @@ -52,8 +52,8 @@ import androidx.compose.ui.unit.sp import coil.compose.AsyncImage import com.bnyro.contacts.R import com.bnyro.contacts.presentation.components.DialerButton -import com.bnyro.contacts.presentation.components.NumberInput -import com.bnyro.contacts.presentation.components.PhoneNumberOnlyDisplay +import com.bnyro.contacts.presentation.screens.calllog.components.NumberInput +import com.bnyro.contacts.presentation.screens.calllog.components.PhoneNumberOnlyDisplay import com.bnyro.contacts.presentation.screens.dialer.model.DialerModel import com.bnyro.contacts.presentation.screens.dialer.model.state.CallState diff --git a/app/src/main/java/com/bnyro/contacts/util/ContactsHelper.kt b/app/src/main/java/com/bnyro/contacts/util/ContactsHelper.kt index d40a9c72..c667340a 100644 --- a/app/src/main/java/com/bnyro/contacts/util/ContactsHelper.kt +++ b/app/src/main/java/com/bnyro/contacts/util/ContactsHelper.kt @@ -24,20 +24,56 @@ import ezvcard.parameter.TelephoneType object ContactsHelper { val emailTypes = listOf( - TranslatedType(ContactsContract.CommonDataKinds.Email.TYPE_HOME, R.string.home, EmailType.HOME), - TranslatedType(ContactsContract.CommonDataKinds.Email.TYPE_WORK, R.string.work, EmailType.WORK), - TranslatedType(ContactsContract.CommonDataKinds.Email.TYPE_MOBILE, R.string.mobile, EmailType.PREF), + TranslatedType( + ContactsContract.CommonDataKinds.Email.TYPE_HOME, + R.string.home, + EmailType.HOME + ), + TranslatedType( + ContactsContract.CommonDataKinds.Email.TYPE_WORK, + R.string.work, + EmailType.WORK + ), + TranslatedType( + ContactsContract.CommonDataKinds.Email.TYPE_MOBILE, + R.string.mobile, + EmailType.PREF + ), TranslatedType(ContactsContract.CommonDataKinds.Email.TYPE_CUSTOM, R.string.custom), TranslatedType(ContactsContract.CommonDataKinds.Email.TYPE_OTHER, R.string.other) ) val phoneNumberTypes = listOf( - TranslatedType(ContactsContract.CommonDataKinds.Phone.TYPE_HOME, R.string.home, TelephoneType.HOME), - TranslatedType(ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE, R.string.mobile, TelephoneType.CELL), - TranslatedType(ContactsContract.CommonDataKinds.Phone.TYPE_WORK, R.string.work, TelephoneType.WORK), - TranslatedType(ContactsContract.CommonDataKinds.Phone.TYPE_CAR, R.string.car, TelephoneType.CAR), - TranslatedType(ContactsContract.CommonDataKinds.Phone.TYPE_FAX_HOME, R.string.fax_home, TelephoneType.FAX), - TranslatedType(ContactsContract.CommonDataKinds.Phone.TYPE_FAX_WORK, R.string.fax_work, TelephoneType.FAX), + TranslatedType( + ContactsContract.CommonDataKinds.Phone.TYPE_HOME, + R.string.home, + TelephoneType.HOME + ), + TranslatedType( + ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE, + R.string.mobile, + TelephoneType.CELL + ), + TranslatedType( + ContactsContract.CommonDataKinds.Phone.TYPE_WORK, + R.string.work, + TelephoneType.WORK + ), + TranslatedType( + ContactsContract.CommonDataKinds.Phone.TYPE_CAR, + R.string.car, + TelephoneType.CAR + ), + TranslatedType( + ContactsContract.CommonDataKinds.Phone.TYPE_FAX_HOME, + R.string.fax_home, + TelephoneType.FAX + ), + TranslatedType( + ContactsContract.CommonDataKinds.Phone.TYPE_FAX_WORK, + R.string.fax_work, + TelephoneType.FAX + ), TranslatedType( ContactsContract.CommonDataKinds.Phone.TYPE_ASSISTANT, R.string.assistant @@ -122,9 +158,20 @@ object ContactsHelper { } fun matches(contactData: ContactData, query: String): Boolean { - val contactInfoStrings = listOf(contactData.numbers, contactData.emails, contactData.addresses, contactData.notes, contactData.websites, contactData.events) + val contactInfoStrings = listOf( + contactData.numbers, + contactData.emails, + contactData.addresses, + contactData.notes, + contactData.websites, + contactData.events + ) .flatten() - .map { (value, _) -> value } + listOf(contactData.organization, contactData.nickName, contactData.displayName) + .map { (value, _) -> value } + listOf( + contactData.organization, + contactData.nickName, + contactData.displayName + ) return contactInfoStrings.filterNotNull().any { str -> str.lowercase().contains(query) @@ -138,15 +185,25 @@ object ContactsHelper { } fun isContactEmpty(contactData: ContactData): Boolean { - val stringProperties = listOf(contactData.firstName, contactData.surName, contactData.nickName, contactData.organization) - val listProperties = listOf(contactData.numbers, contactData.emails, contactData.events, contactData.addresses, contactData.notes) + val stringProperties = listOf( + contactData.firstName, + contactData.surName, + contactData.nickName, + contactData.organization + ) + val listProperties = listOf( + contactData.numbers, + contactData.emails, + contactData.events, + contactData.addresses, + contactData.notes + ) return stringProperties.none { !it.isNullOrBlank() } && listProperties.flatten().isEmpty() } - fun getContactPhotoThumbnail(context: Context, uri: String): Bitmap? { + fun getContactPhotoThumbnail(context: Context, photoThumbnailUri: Uri): Bitmap? { val contentResolver = context.contentResolver - val photoThumbnailUri = Uri.parse(uri) val assetFileDescriptor = contentResolver.openAssetFileDescriptor(photoThumbnailUri, "r") ?: return null val fileDescriptor = assetFileDescriptor.fileDescriptor diff --git a/app/src/main/java/com/bnyro/contacts/util/services/CallService.kt b/app/src/main/java/com/bnyro/contacts/util/services/CallService.kt index bf41d7a7..2b9118aa 100644 --- a/app/src/main/java/com/bnyro/contacts/util/services/CallService.kt +++ b/app/src/main/java/com/bnyro/contacts/util/services/CallService.kt @@ -4,15 +4,12 @@ import android.annotation.SuppressLint import android.app.Notification import android.app.PendingIntent import android.content.BroadcastReceiver -import android.content.ContentResolver import android.content.Context import android.content.Intent import android.content.IntentFilter import android.graphics.Bitmap -import android.net.Uri import android.os.Binder import android.os.IBinder -import android.provider.ContactsContract.PhoneLookup import android.telecom.Call import android.telecom.InCallService import android.view.View @@ -20,14 +17,13 @@ import android.widget.RemoteViews import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat import androidx.core.content.ContextCompat -import androidx.core.net.toUri +import com.bnyro.contacts.App import com.bnyro.contacts.R import com.bnyro.contacts.domain.model.CallerInfo import com.bnyro.contacts.presentation.screens.dialer.model.state.CallState import com.bnyro.contacts.ui.activities.CallActivity import com.bnyro.contacts.util.ContactsHelper import com.bnyro.contacts.util.NotificationHelper -import com.bnyro.contacts.util.extension.stringValue import com.google.i18n.phonenumbers.PhoneNumberUtil import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -129,53 +125,31 @@ class CallService : InCallService() { updateCallerInfo() scope.launch { - val (thumbnailUri, contactName) = withContext(Dispatchers.IO) { - getContactName(callerNumber) - } - val contactPhoto = if (thumbnailUri != null) { + val phoneLookupRepository = + (this@CallService.applicationContext as App).phoneLookupRepository + val contactData = phoneLookupRepository.getContactByNumber(callerNumber) + val contactPhoto = if (contactData.thumbnail != null) { withContext(Dispatchers.IO) { - ContactsHelper.getContactPhotoThumbnail(this@CallService, thumbnailUri) + ContactsHelper.getContactPhotoThumbnail(this@CallService, contactData.thumbnail) } } else { null } - val notification = buildNotification(call, callerNumber, contactName, contactPhoto) + val notification = buildNotification(call, callerNumber, contactData.name, contactPhoto) NotificationManagerCompat.from(this@CallService) .notify(CALL_NOTIFICATION_ID, notification) callerInfo = CallerInfo( - callerName = contactName, + callerName = contactData.name, rawPhoneNumber = callerNumber, - callerPhoto = thumbnailUri?.toUri(), + callerPhoto = contactData.thumbnail, formattedPhoneNumber = formattedPhoneNumber ) updateCallerInfo() } } - private fun getContactName(phoneNumber: String): Pair { - val cr: ContentResolver = this.contentResolver - val uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(phoneNumber)) - val query = - cr.query( - uri, - arrayOf(PhoneLookup.DISPLAY_NAME, PhoneLookup.PHOTO_THUMBNAIL_URI), - null, - null, - null - ) - - query?.use { cursor -> - if (cursor.moveToFirst()) { - return cursor.stringValue(PhoneLookup.PHOTO_THUMBNAIL_URI) to cursor.stringValue( - PhoneLookup.DISPLAY_NAME - ) - } - } - return null to null - } - private fun formatPhoneNumber(number: String): String { val phoneUtil = PhoneNumberUtil.getInstance() val phoneNumber = runCatching { phoneUtil.parse(number, null) } From b79e0c300f17a825b7e88674f4608ca3ed5e8aa6 Mon Sep 17 00:00:00 2001 From: Suhas Dissanayake Date: Sun, 12 May 2024 17:03:57 +0530 Subject: [PATCH 031/159] feat: search contacts names using dialpad --- .../screens/calllog/CallLogsScreen.kt | 4 +- .../screens/calllog/components/NumberInput.kt | 6 +- .../screens/calllog/model/CallModel.kt | 56 ++++++++++++++++--- .../contacts/util/extension/DigitsToWords.kt | 38 +++++++++++++ 4 files changed, 92 insertions(+), 12 deletions(-) create mode 100644 app/src/main/java/com/bnyro/contacts/util/extension/DigitsToWords.kt diff --git a/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/CallLogsScreen.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/CallLogsScreen.kt index 728d24aa..20090b16 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/CallLogsScreen.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/CallLogsScreen.kt @@ -23,6 +23,7 @@ import androidx.compose.material3.Text import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -135,9 +136,10 @@ fun CallLogsScreen( .padding(horizontal = 8.dp), horizontalAlignment = Alignment.CenterHorizontally ) { + val contacts by callModel.contacts.collectAsState() PhoneNumberDisplay( displayText = callModel.numberToCall, - contacts = callModel.contacts, + contacts = contacts, onClickContact = callModel::setPhoneNumberContact ) NumberInput( diff --git a/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/components/NumberInput.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/components/NumberInput.kt index 4005ea8e..db324197 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/components/NumberInput.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/components/NumberInput.kt @@ -50,6 +50,7 @@ import androidx.compose.ui.hapticfeedback.HapticFeedbackType import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalHapticFeedback import androidx.compose.ui.platform.LocalView +import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp @@ -298,8 +299,9 @@ private fun ContactSuggestionItem( ) } else { Icon( - modifier = Modifier.size(48.dp), - imageVector = Icons.Rounded.Call, + modifier = Modifier + .fillMaxSize(), + painter = painterResource(id = R.drawable.ic_person), contentDescription = null, tint = MaterialTheme.colorScheme.onSurfaceVariant ) diff --git a/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/model/CallModel.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/model/CallModel.kt index bcd1bf68..14dfa9d9 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/model/CallModel.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/model/CallModel.kt @@ -23,8 +23,14 @@ import com.bnyro.contacts.domain.model.CallLogEntry import com.bnyro.contacts.navigation.HomeRoutes import com.bnyro.contacts.util.PermissionHelper import com.bnyro.contacts.util.SmsUtil +import com.bnyro.contacts.util.extension.letterCombinations import com.bnyro.contacts.util.extension.removeLastChar +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext class CallModel(private val application: Application, savedStateHandle: SavedStateHandle) : AndroidViewModel(application) { @@ -39,8 +45,10 @@ class CallModel(private val application: Application, savedStateHandle: SavedSta var callLogs by mutableStateOf>(emptyList(), policy = neverEqualPolicy()) private set - var contacts by mutableStateOf>(emptyList(), policy = neverEqualPolicy()) - private set + private val contactsSet = mutableSetOf() + + private val _contacts = MutableStateFlow>(emptyList()) + val contacts = _contacts.asStateFlow() private val toneGenerator = ToneGenerator( AudioManager.STREAM_SYSTEM, @@ -67,20 +75,26 @@ class CallModel(private val application: Application, savedStateHandle: SavedSta fun onNumberInput(digit: String) { numberToCall += digit playToneForDigit(digit) - if (numberToCall.length > 3) { - searchPhoneNumber(numberToCall) + if (numberToCall.length > 2) { + searchPhoneNumberOrName(numberToCall) } } fun setPhoneNumberContact(contact: BasicContactData) { numberToCall = contact.number - contacts = listOf(contact) + _contacts.update { + listOf(contact) + } } fun onBackSpace() { numberToCall = numberToCall.removeLastChar() - if (numberToCall.length > 3) { - searchPhoneNumber(numberToCall) + if (numberToCall.length > 2) { + searchPhoneNumberOrName(numberToCall) + } else { + _contacts.update { + emptyList() + } } } @@ -139,12 +153,36 @@ class CallModel(private val application: Application, savedStateHandle: SavedSta toneGenerator.startTone(numericDigit, durationMs) } - private fun searchPhoneNumber(number: String) { + private fun searchPhoneNumberOrName(number: String) { + contactsSet.clear() viewModelScope.launch { - contacts = phoneLookupRepository.getContactsWithNumber(number) + contactsSet.addAll(phoneLookupRepository.getContactsWithNumber(number)) + _contacts.update { + contactsSet.toList() + } + } + if (number.length < 5) { + // search by name only if number is less than 5 digits to avoid performance issues + viewModelScope.launch { + contactsSet.addAll(getContactsByDigitPattern(number)) + _contacts.update { + contactsSet.toList() + } + } } } + private suspend fun getContactsByDigitPattern(number: String): List = + withContext(Dispatchers.IO) { + val contacts = mutableListOf() + val namePatterns = letterCombinations(number) + namePatterns.forEach { + val contact = phoneLookupRepository.getContactsWithName(it) + contacts += contact + } + contacts + } + companion object { val phonePerms = arrayOf( Manifest.permission.CALL_PHONE, diff --git a/app/src/main/java/com/bnyro/contacts/util/extension/DigitsToWords.kt b/app/src/main/java/com/bnyro/contacts/util/extension/DigitsToWords.kt new file mode 100644 index 00000000..81aa981d --- /dev/null +++ b/app/src/main/java/com/bnyro/contacts/util/extension/DigitsToWords.kt @@ -0,0 +1,38 @@ +package com.bnyro.contacts.util.extension + +val digitsToWordsMap = mapOf( + '2' to arrayOf('A', 'B', 'C'), + '3' to arrayOf('D', 'E', 'F'), + '4' to arrayOf('G', 'H', 'I'), + '5' to arrayOf('J', 'K', 'L'), + '6' to arrayOf('M', 'N', 'O'), + '7' to arrayOf('P', 'Q', 'R', 'S'), + '8' to arrayOf('T', 'U', 'V'), + '9' to arrayOf('W', 'X', 'Y', 'Z') +) + +fun letterCombinations(digits: String): List { + if (digits.isEmpty()) return emptyList() + if (digits.contains(Regex("[01+#*]"))) return emptyList() + val arrays = digits.map { digitsToWordsMap[it]!! } + return getPossibleStrings(arrays) +} + +private fun getPossibleStrings(arrays: List>): List { + if (arrays.isEmpty()) return emptyList() + val result = mutableListOf() + + // Create a tree of depth [arrays.size] and return the result from the leaf node + fun backtrack(index: Int, currentString: String) { + if (index == arrays.size) { + result.add(currentString) + return + } + for (char in arrays[index]) { + backtrack(index + 1, currentString + char) + } + } + + backtrack(0, "") + return result +} \ No newline at end of file From bbc969ae7fbfbd25d46da9675c460ac1ae1a81d7 Mon Sep 17 00:00:00 2001 From: Suhas Dissanayake Date: Sun, 12 May 2024 17:30:52 +0530 Subject: [PATCH 032/159] style: add dialpad subcontent --- .../screens/calllog/components/NumberInput.kt | 101 ++++++++++++++---- 1 file changed, 83 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/components/NumberInput.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/components/NumberInput.kt index db324197..173b6e43 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/components/NumberInput.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/components/NumberInput.kt @@ -28,8 +28,9 @@ import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.rounded.Backspace +import androidx.compose.material.icons.automirrored.rounded.Backspace import androidx.compose.material.icons.rounded.Call +import androidx.compose.material.icons.rounded.Voicemail import androidx.compose.material3.Icon import androidx.compose.material3.ListItem import androidx.compose.material3.MaterialTheme @@ -46,6 +47,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.hapticfeedback.HapticFeedbackType import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalHapticFeedback @@ -58,11 +60,11 @@ import coil.compose.AsyncImage import com.bnyro.contacts.R import com.bnyro.contacts.domain.model.BasicContactData -val keypadNumbers = arrayOf( - arrayOf("1", "2", "3"), - arrayOf("4", "5", "6"), - arrayOf("7", "8", "9"), - arrayOf("*", "0", "#") +val keypadNumbers: Array>> = arrayOf( + arrayOf("1" to Icons.Rounded.Voicemail, "2" to "ABC", "3" to "DEF"), + arrayOf("4" to "GHI", "5" to "JKL", "6" to "MNO"), + arrayOf("7" to "PQRS", "8" to "TUV", "9" to "WXYZ"), + arrayOf("*" to Unit, "0" to "+", "#" to Unit) ) @SuppressLint("NewApi") @@ -87,12 +89,45 @@ fun NumberInput( horizontalArrangement = Arrangement.spacedBy(buttonSpacing) ) { col.forEach { - NumpadButton( - text = it, - onClick = { - onNumberInput(it) + val subcontent = it.second + if (subcontent is String) { + NumpadButtonWithSubcontent( + aspectRatio = 1.5f, + text = it.first, + onClick = { + onNumberInput(it.first) + } + ) { + Text( + text = subcontent, + color = MaterialTheme.colorScheme.onSurface, + style = MaterialTheme.typography.bodyMedium + ) } - ) + } else if (subcontent is ImageVector) { + NumpadButtonWithSubcontent( + aspectRatio = 1.5f, + text = it.first, + onClick = { + onNumberInput(it.first) + } + ) { + Icon( + modifier = Modifier.size(16.dp), + imageVector = subcontent, + contentDescription = null, + tint = MaterialTheme.colorScheme.onSurface + ) + } + } else { + NumpadButton( + aspectRatio = 1.5f, + text = it.first, + onClick = { + onNumberInput(it.first) + } + ) + } } } } @@ -116,7 +151,7 @@ fun NumberInput( backgroundColor = MaterialTheme.colorScheme.tertiaryContainer ) { Icon( - Icons.Rounded.Backspace, + Icons.AutoMirrored.Rounded.Backspace, contentDescription = stringResource(R.string.delete), tint = MaterialTheme.colorScheme.onTertiaryContainer ) @@ -163,9 +198,9 @@ fun NumberInput( ) { col.forEach { NumpadButton( - text = it, + text = it.first, onClick = { - onNumberInput(it) + onNumberInput(it.first) } ) } @@ -179,7 +214,7 @@ fun RowScope.NumpadButton( text: String, textColor: Color = MaterialTheme.colorScheme.onSurface, backgroundColor: Color = MaterialTheme.colorScheme.surfaceColorAtElevation(10.dp), - aspectRatio: Float = 1f, + aspectRatio: Float = 2f, onClick: () -> Unit, onLongClick: () -> Unit = { } ) { @@ -197,11 +232,41 @@ fun RowScope.NumpadButton( } } +@Composable +fun RowScope.NumpadButtonWithSubcontent( + text: String, + textColor: Color = MaterialTheme.colorScheme.onSurface, + backgroundColor: Color = MaterialTheme.colorScheme.surfaceColorAtElevation(10.dp), + aspectRatio: Float = 2f, + onClick: () -> Unit, + onLongClick: () -> Unit = { }, + content: @Composable (() -> Unit), +) { + NumpadButton( + backgroundColor = backgroundColor, + aspectRatio = aspectRatio, + onClick = onClick, + onLongClick = onLongClick + ) { + Column( + modifier = Modifier.padding(vertical = 4.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text( + text = text, + color = textColor, + style = MaterialTheme.typography.displaySmall + ) + content() + } + } +} + @OptIn(ExperimentalFoundationApi::class) @Composable fun RowScope.NumpadButton( backgroundColor: Color = MaterialTheme.colorScheme.surfaceVariant, - aspectRatio: Float = 1f, + aspectRatio: Float = 2f, onClick: () -> Unit, onLongClick: () -> Unit = { }, content: @Composable (BoxScope.() -> Unit) @@ -223,8 +288,8 @@ fun RowScope.NumpadButton( } ) .background(backgroundColor) - .aspectRatio(2f) - .weight(aspectRatio), + .aspectRatio(aspectRatio) + .weight(1f), content = content ) } From 59aaad5958566fed5b63324bfd7ef3474c69c353 Mon Sep 17 00:00:00 2001 From: Suhas Dissanayake Date: Sun, 12 May 2024 20:25:50 +0530 Subject: [PATCH 033/159] feat: set custom ringtones for contacts (closes #305) --- .../contacts/domain/model/ContactData.kt | 4 +- .../repositories/DeviceContactsRepository.kt | 87 +++++++++++++------ .../screens/contact/SingleContactScreen.kt | 37 ++++++++ .../screens/contacts/model/ContactsModel.kt | 6 ++ .../contacts/util/RingtonePickContract.kt | 26 ++++++ app/src/main/res/values/strings.xml | 2 + 6 files changed, 134 insertions(+), 28 deletions(-) create mode 100644 app/src/main/java/com/bnyro/contacts/util/RingtonePickContract.kt diff --git a/app/src/main/java/com/bnyro/contacts/domain/model/ContactData.kt b/app/src/main/java/com/bnyro/contacts/domain/model/ContactData.kt index 92a8a6fa..3813548b 100644 --- a/app/src/main/java/com/bnyro/contacts/domain/model/ContactData.kt +++ b/app/src/main/java/com/bnyro/contacts/domain/model/ContactData.kt @@ -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( @@ -24,7 +25,8 @@ data class ContactData( var events: List = listOf(), var notes: List = listOf(), var groups: List = listOf(), - var websites: List = listOf() + var websites: List = listOf(), + var ringTone: Uri? = null ) { val accountIdentifier get() = "$accountType|$accountName" fun getNameBySortOrder(sortOrder: SortOrder): String? { diff --git a/app/src/main/java/com/bnyro/contacts/domain/repositories/DeviceContactsRepository.kt b/app/src/main/java/com/bnyro/contacts/domain/repositories/DeviceContactsRepository.kt index 885b2aa5..b5cff7fe 100644 --- a/app/src/main/java/com/bnyro/contacts/domain/repositories/DeviceContactsRepository.kt +++ b/app/src/main/java/com/bnyro/contacts/domain/repositories/DeviceContactsRepository.kt @@ -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 @@ -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) @@ -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( @@ -317,22 +323,28 @@ class DeviceContactsRepository(private val context: Context) : ContactsRepositor it.rowId.toString() ) }.toTypedArray(), - *ContactsHelper.contactAttributesTypes.filterIsInstance().map { attribute -> - attribute.get(contact)?.let { - getInsertAction(attribute.androidContentType, attribute.androidValueColumn, it) - } - }.toTypedArray(), - *ContactsHelper.contactAttributesTypes.filterIsInstance().map { attribute -> - attribute.get(contact).map { - getInsertAction( - attribute.androidContentType, - attribute.androidValueColumn, - it.value, - attribute.androidTypeColumn, - it.type - ) - } - }.flatten().toTypedArray() + *ContactsHelper.contactAttributesTypes.filterIsInstance() + .map { attribute -> + attribute.get(contact)?.let { + getInsertAction( + attribute.androidContentType, + attribute.androidValueColumn, + it + ) + } + }.toTypedArray(), + *ContactsHelper.contactAttributesTypes.filterIsInstance() + .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) @@ -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 + ) + ) } } @@ -398,13 +414,19 @@ class DeviceContactsRepository(private val context: Context) : ContactsRepositor fun getAccountTypes(): List { 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) @@ -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 } diff --git a/app/src/main/java/com/bnyro/contacts/presentation/screens/contact/SingleContactScreen.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/contact/SingleContactScreen.kt index 71c0cdc7..2008adb1 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/screens/contact/SingleContactScreen.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/contact/SingleContactScreen.kt @@ -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 @@ -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 @@ -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 @@ -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 = { @@ -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() + } + ) + } + } } ) } diff --git a/app/src/main/java/com/bnyro/contacts/presentation/screens/contacts/model/ContactsModel.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/contacts/model/ContactsModel.kt index ee6b97a9..f7c13627 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/screens/contacts/model/ContactsModel.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/contacts/model/ContactsModel.kt @@ -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 { diff --git a/app/src/main/java/com/bnyro/contacts/util/RingtonePickContract.kt b/app/src/main/java/com/bnyro/contacts/util/RingtonePickContract.kt new file mode 100644 index 00000000..1b9332d1 --- /dev/null +++ b/app/src/main/java/com/bnyro/contacts/util/RingtonePickContract.kt @@ -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() { + 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) + } +} \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 94e7044d..f118fa41 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -149,4 +149,6 @@ Dialing... Call From Keypad + Select custom ringtone + Change ringtone \ No newline at end of file From 0bea79e6547a77f76c8dd717b62bd2185d068d82 Mon Sep 17 00:00:00 2001 From: Suhas Dissanayake Date: Sun, 12 May 2024 20:30:49 +0530 Subject: [PATCH 034/159] fix: contact photo and name not loading due to missing return statement --- .../contacts/domain/repositories/PhoneLookupRepository.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/src/main/java/com/bnyro/contacts/domain/repositories/PhoneLookupRepository.kt b/app/src/main/java/com/bnyro/contacts/domain/repositories/PhoneLookupRepository.kt index 5d5fefe4..d25a1e4a 100644 --- a/app/src/main/java/com/bnyro/contacts/domain/repositories/PhoneLookupRepository.kt +++ b/app/src/main/java/com/bnyro/contacts/domain/repositories/PhoneLookupRepository.kt @@ -111,14 +111,13 @@ class PhoneLookupRepository(private val context: Context) { query?.use { cursor -> if (cursor.moveToFirst()) { - BasicContactData( + return@withContext BasicContactData( number = phoneNumber, name = cursor.stringValue( PhoneLookup.DISPLAY_NAME ), thumbnail = cursor.stringValue(PhoneLookup.PHOTO_THUMBNAIL_URI)?.toUri() ) - } } BasicContactData( From 92da10988c8e82c71086edbd98b858d952dcffec Mon Sep 17 00:00:00 2001 From: Suhas Dissanayake Date: Sun, 12 May 2024 23:35:33 +0530 Subject: [PATCH 035/159] feat: block numbers (closes #323) --- .../screens/calllog/CallLogsScreen.kt | 197 +++++++++++++++++- .../presentation/screens/sms/SmsListScreen.kt | 135 +++++++++++- .../screens/sms/components/SmsThreadItem.kt | 14 +- .../com/bnyro/contacts/util/IntentHelper.kt | 50 ++++- app/src/main/res/values/strings.xml | 6 + 5 files changed, 387 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/CallLogsScreen.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/CallLogsScreen.kt index 20090b16..bad95fa6 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/CallLogsScreen.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/CallLogsScreen.kt @@ -1,25 +1,46 @@ package com.bnyro.contacts.presentation.screens.calllog +import android.annotation.SuppressLint +import android.os.Build +import android.provider.BlockedNumberContract import android.text.format.DateUtils +import android.view.SoundEffectConstants +import androidx.annotation.StringRes +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.background import androidx.compose.foundation.clickable +import androidx.compose.foundation.combinedClickable +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.systemBars import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Dialpad +import androidx.compose.material.icons.rounded.FrontHand +import androidx.compose.material.icons.rounded.Handshake +import androidx.compose.material.icons.rounded.MoreVert +import androidx.compose.material3.DropdownMenu +import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.ElevatedCard import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.Scaffold import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect @@ -30,11 +51,18 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalView +import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel import com.bnyro.contacts.R +import com.bnyro.contacts.domain.model.CallLogEntry +import com.bnyro.contacts.presentation.components.ClickableIcon import com.bnyro.contacts.presentation.components.NothingHere import com.bnyro.contacts.presentation.features.ConfirmationDialog import com.bnyro.contacts.presentation.screens.calllog.components.NumberInput @@ -43,9 +71,10 @@ import com.bnyro.contacts.presentation.screens.calllog.model.CallModel import com.bnyro.contacts.presentation.screens.contacts.model.ContactsModel import com.bnyro.contacts.presentation.screens.editor.components.ContactIconPlaceholder import com.bnyro.contacts.presentation.screens.settings.model.ThemeModel +import com.bnyro.contacts.util.IntentHelper -@OptIn(ExperimentalMaterial3Api::class) +@OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class) @Composable fun CallLogsScreen( contactsModel: ContactsModel, @@ -58,6 +87,10 @@ fun CallLogsScreen( mutableStateOf(callModel.initialPhoneNumber != null) } + var selectedCallLog by remember { + mutableStateOf(null) + } + val callLog = callModel.callLogs Scaffold( floatingActionButton = { @@ -66,6 +99,39 @@ fun CallLogsScreen( ) { Icon(Icons.Default.Dialpad, contentDescription = null) } + }, topBar = { + TopAppBar(title = { + Text(text = stringResource(id = R.string.recent_calls)) + }, actions = { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + 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.block_numbers) + ) + }, + onClick = { + IntentHelper.openBlockedNumberManager(context) + } + ) + } + } + } + }) } ) { pV -> if (callLog.isNotEmpty()) { @@ -79,12 +145,19 @@ fun CallLogsScreen( var showCallDialog by remember { mutableStateOf(false) } + val shape = RoundedCornerShape(20.dp) ElevatedCard( modifier = Modifier .fillMaxWidth() + .clip(shape) .padding(horizontal = 10.dp, vertical = 5.dp) - .clickable { if (it.phoneNumber.isNotBlank()) showCallDialog = true } + .combinedClickable(onClick = { + if (it.phoneNumber.isNotBlank()) showCallDialog = true + }, onLongClick = { + selectedCallLog = it + }), + shape = shape ) { Row( modifier = Modifier.padding(vertical = 10.dp, horizontal = 16.dp), @@ -155,7 +228,127 @@ fun CallLogsScreen( } } + selectedCallLog?.let { + CallLogOptionsSheet( + onDismissRequest = { selectedCallLog = null }, + log = it + ) + } + LaunchedEffect(Unit) { callModel.requestDefaultDialerApp(context) } } + +@SuppressLint("NewApi") +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun CallLogOptionsSheet( + onDismissRequest: () -> Unit, + log: CallLogEntry +) { + val songSettingsSheetState = rememberModalBottomSheetState( + skipPartiallyExpanded = true + ) + val context = LocalContext.current + var isBlocked by remember { + mutableStateOf(null) + } + LaunchedEffect(Unit) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + if (!BlockedNumberContract.canCurrentUserBlockNumbers(context)) return@LaunchedEffect + isBlocked = BlockedNumberContract.isBlocked(context, log.phoneNumber) + } + } + ModalBottomSheet( + onDismissRequest = onDismissRequest, + sheetState = songSettingsSheetState, + windowInsets = WindowInsets.systemBars, + dragHandle = null + ) { + Row( + Modifier + .fillMaxWidth() + .padding(horizontal = 8.dp, vertical = 16.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Box( + modifier = Modifier + .size(64.dp) + .padding(8.dp) + .clip(CircleShape) + .background(MaterialTheme.colorScheme.surfaceVariant) + ) { + Icon( + modifier = Modifier.fillMaxSize(), + painter = painterResource(id = R.drawable.ic_person), + contentDescription = null, + tint = MaterialTheme.colorScheme.onSurfaceVariant + ) + } + Column( + Modifier + .weight(1f) + .padding(8.dp) + ) { + Text( + log.phoneNumber, + style = MaterialTheme.typography.titleMedium, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + Text( + DateUtils.getRelativeTimeSpanString(log.time)?.toString() + .orEmpty(), + style = MaterialTheme.typography.titleSmall, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + } + } + Column( + Modifier + .padding(horizontal = 8.dp, vertical = 16.dp) + ) { + isBlocked?.let { isBlocked -> + if (isBlocked) { + SheetSettingItem( + icon = Icons.Rounded.Handshake, + description = R.string.unblock_number, + onClick = { + BlockedNumberContract.unblock(context, log.phoneNumber) + onDismissRequest() + } + ) + } else { + SheetSettingItem( + icon = Icons.Rounded.FrontHand, + description = R.string.block_number, + onClick = { + IntentHelper.blockNumberOrAddress(context, log.phoneNumber) + onDismissRequest() + } + ) + } + } + } + } +} + +@Composable +fun SheetSettingItem(icon: ImageVector, @StringRes description: Int, onClick: () -> Unit) { + val view = LocalView.current + Row( + Modifier + .fillMaxWidth() + .padding(8.dp) + .clickable { + view.playSoundEffect(SoundEffectConstants.CLICK) + onClick() + } + ) { + Icon(imageVector = icon, contentDescription = null) + Spacer(Modifier.width(16.dp)) + Text(text = stringResource(id = description)) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/SmsListScreen.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/SmsListScreen.kt index efcea8e1..32467ed3 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/SmsListScreen.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/SmsListScreen.kt @@ -1,19 +1,36 @@ package com.bnyro.contacts.presentation.screens.sms +import android.annotation.SuppressLint +import android.os.Build +import android.provider.BlockedNumberContract +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.systemBars import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.shape.CircleShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Edit import androidx.compose.material.icons.filled.Search +import androidx.compose.material.icons.rounded.FrontHand +import androidx.compose.material.icons.rounded.Handshake import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar +import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState @@ -21,10 +38,18 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.asImageBitmap import androidx.compose.ui.input.nestedscroll.NestedScrollConnection import androidx.compose.ui.input.nestedscroll.nestedScroll +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp import com.bnyro.contacts.R import com.bnyro.contacts.domain.model.ContactData import com.bnyro.contacts.domain.model.SmsThread @@ -33,10 +58,12 @@ import com.bnyro.contacts.presentation.components.ClickableIcon import com.bnyro.contacts.presentation.components.NothingHere import com.bnyro.contacts.presentation.components.TopBarMoreMenu import com.bnyro.contacts.presentation.features.NumberPickerDialog +import com.bnyro.contacts.presentation.screens.calllog.SheetSettingItem import com.bnyro.contacts.presentation.screens.contacts.model.ContactsModel import com.bnyro.contacts.presentation.screens.sms.components.SmsSearchScreen import com.bnyro.contacts.presentation.screens.sms.components.SmsThreadItem import com.bnyro.contacts.presentation.screens.sms.model.SmsModel +import com.bnyro.contacts.util.IntentHelper @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -54,6 +81,9 @@ fun SmsListScreen( mutableStateOf(false) } + var selectedThread by remember { + mutableStateOf(null) + } LaunchedEffect(Unit) { smsModel.initialAddressAndBody?.let { onClickMessage(it.first, null) @@ -123,7 +153,9 @@ fun SmsListScreen( } ) { items(threadList) { thread -> - SmsThreadItem(smsModel, thread, onClick = onClickMessage) + SmsThreadItem(smsModel, thread, onClick = onClickMessage, onLongClick = { + selectedThread = thread + }) } } if (showSearch) { @@ -143,4 +175,105 @@ fun SmsListScreen( ) } } + selectedThread?.let { thread -> + SMSThreadOptionsSheet(onDismissRequest = { selectedThread = null }, thread = thread) + } } + +@SuppressLint("NewApi") +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun SMSThreadOptionsSheet( + onDismissRequest: () -> Unit, + thread: SmsThread +) { + val songSettingsSheetState = rememberModalBottomSheetState( + skipPartiallyExpanded = true + ) + val context = LocalContext.current + var isBlocked by remember { + mutableStateOf(null) + } + LaunchedEffect(Unit) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + if (!BlockedNumberContract.canCurrentUserBlockNumbers(context)) return@LaunchedEffect + isBlocked = BlockedNumberContract.isBlocked(context, thread.address) + } + } + ModalBottomSheet( + onDismissRequest = onDismissRequest, + sheetState = songSettingsSheetState, + windowInsets = WindowInsets.systemBars, + dragHandle = null + ) { + Row( + Modifier + .fillMaxWidth() + .padding(horizontal = 8.dp, vertical = 16.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Box( + modifier = Modifier + .size(64.dp) + .padding(8.dp) + .clip(CircleShape) + .background(MaterialTheme.colorScheme.surfaceVariant) + ) { + if (thread.contactData?.thumbnail != null) { + Image( + modifier = Modifier + .fillMaxSize(), + bitmap = thread.contactData.thumbnail!!.asImageBitmap(), + contentDescription = null, + contentScale = ContentScale.Crop + ) + } else { + Icon( + modifier = Modifier.fillMaxSize(), + painter = painterResource(id = R.drawable.ic_person), + contentDescription = null, + tint = MaterialTheme.colorScheme.onSurfaceVariant + ) + } + } + Column( + Modifier + .weight(1f) + .padding(8.dp) + ) { + Text( + thread.contactData?.displayName ?: thread.address, + style = MaterialTheme.typography.titleLarge, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + } + } + Column( + Modifier + .padding(horizontal = 8.dp, vertical = 16.dp) + ) { + isBlocked?.let { isBlocked -> + if (isBlocked) { + SheetSettingItem( + icon = Icons.Rounded.Handshake, + description = R.string.unblock_number, + onClick = { + BlockedNumberContract.unblock(context, thread.address) + onDismissRequest() + } + ) + } else { + SheetSettingItem( + icon = Icons.Rounded.FrontHand, + description = R.string.block_number, + onClick = { + IntentHelper.blockNumberOrAddress(context, thread.address) + onDismissRequest() + } + ) + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/components/SmsThreadItem.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/components/SmsThreadItem.kt index 30e667dd..dc37f370 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/components/SmsThreadItem.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/components/SmsThreadItem.kt @@ -1,8 +1,9 @@ package com.bnyro.contacts.presentation.screens.sms.components +import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.Image import androidx.compose.foundation.background -import androidx.compose.foundation.clickable +import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -46,12 +47,13 @@ import com.bnyro.contacts.domain.model.SmsThread import com.bnyro.contacts.presentation.features.ConfirmationDialog import com.bnyro.contacts.presentation.screens.sms.model.SmsModel -@OptIn(ExperimentalMaterial3Api::class) +@OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class) @Composable fun SmsThreadItem( smsModel: SmsModel, thread: SmsThread, - onClick: (address: String, contactData: ContactData?) -> Unit + onClick: (address: String, contactData: ContactData?) -> Unit, + onLongClick: (thread: SmsThread) -> Unit = {} ) { val context = LocalContext.current @@ -79,9 +81,11 @@ fun SmsThreadItem( modifier = Modifier .fillMaxWidth() .clip(shape) - .clickable { + .combinedClickable(onClick = { onClick.invoke(thread.address, thread.contactData) - }, + }, onLongClick = { + onLongClick.invoke(thread) + }), shape = shape ) { Row( diff --git a/app/src/main/java/com/bnyro/contacts/util/IntentHelper.kt b/app/src/main/java/com/bnyro/contacts/util/IntentHelper.kt index f1bb5028..b0f0549d 100644 --- a/app/src/main/java/com/bnyro/contacts/util/IntentHelper.kt +++ b/app/src/main/java/com/bnyro/contacts/util/IntentHelper.kt @@ -1,10 +1,17 @@ package com.bnyro.contacts.util import android.content.ContentUris +import android.content.ContentValues import android.content.Context import android.content.Intent import android.net.Uri +import android.os.Build +import android.provider.BlockedNumberContract +import android.provider.BlockedNumberContract.BlockedNumbers import android.provider.ContactsContract +import android.telecom.TelecomManager +import android.widget.Toast +import androidx.annotation.RequiresApi import androidx.core.net.toUri import com.bnyro.contacts.R import com.bnyro.contacts.domain.enums.IntentActionType @@ -14,6 +21,7 @@ import com.bnyro.contacts.domain.model.ContactData import com.bnyro.contacts.domain.model.TranslatedType import com.bnyro.contacts.domain.model.ValueWithType + object IntentHelper { fun launchAction(context: Context, type: IntentActionType, argument: String) { runCatching { @@ -84,19 +92,47 @@ object IntentHelper { ) ContactsHelper.contactAttributesTypes.forEach { attribute -> - if (attribute is StringAttribute) { - attribute.set(data, intent.getStringExtra(attribute.insertKey)) - } else if (attribute is ListAttribute) { - val values = attribute.insertKeys.mapNotNull { insertKey -> - extractIntentValue(intent, insertKey.first, insertKey.second) - } - attribute.set(data, values) + if (attribute is StringAttribute) { + attribute.set(data, intent.getStringExtra(attribute.insertKey)) + } else if (attribute is ListAttribute) { + val values = attribute.insertKeys.mapNotNull { insertKey -> + extractIntentValue(intent, insertKey.first, insertKey.second) } + attribute.set(data, values) } + } return data } + @RequiresApi(Build.VERSION_CODES.N) + fun openBlockedNumberManager(context: Context) { + val telecomManager = context.getSystemService(TelecomManager::class.java) + val intent = telecomManager.createManageBlockedNumbersIntent() + context.startActivity(intent) + } + + @RequiresApi(Build.VERSION_CODES.N) + fun blockNumberOrAddress(context: Context, number: String) { + if (!BlockedNumberContract.canCurrentUserBlockNumbers(context)) { + Toast.makeText( + context, + context.getString(R.string.blocking_number_is_not_supported), + Toast.LENGTH_LONG + ).show() + return + } + val values = ContentValues().apply { + put(BlockedNumbers.COLUMN_ORIGINAL_NUMBER, number) + } + context.contentResolver.insert(BlockedNumbers.CONTENT_URI, values) + Toast.makeText( + context, + context.getString(R.string.number_blocked), + Toast.LENGTH_SHORT + ).show() + } + private fun extractIntentValue( intent: Intent, key: String, diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f118fa41..3cd3eeba 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -151,4 +151,10 @@ Keypad Select custom ringtone Change ringtone + Recent calls + Block numbers + Blocking number is not supported, Connect You is not the default SMS/Phone app + Number blocked + Block number + Unblock number \ No newline at end of file From cfa1cf307eafe65a5d2818e2594f441613c237cb Mon Sep 17 00:00:00 2001 From: Suhas Dissanayake Date: Tue, 14 May 2024 11:34:04 +0530 Subject: [PATCH 036/159] style: add 'no sms' icon --- .idea/misc.xml | 46 +++++++++++++++++++ .../presentation/components/BlobIconBox.kt | 39 ++++++++++++++++ .../presentation/screens/sms/SmsListScreen.kt | 6 +-- app/src/main/res/drawable/blob.xml | 10 ++++ app/src/main/res/drawable/ic_no_sms.xml | 29 ++++++++++++ 5 files changed, 126 insertions(+), 4 deletions(-) create mode 100644 app/src/main/java/com/bnyro/contacts/presentation/components/BlobIconBox.kt create mode 100755 app/src/main/res/drawable/blob.xml create mode 100644 app/src/main/res/drawable/ic_no_sms.xml diff --git a/.idea/misc.xml b/.idea/misc.xml index 2188b52b..2998c5e1 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,3 +1,4 @@ + @@ -5,6 +6,51 @@ + + + + diff --git a/app/src/main/java/com/bnyro/contacts/presentation/components/BlobIconBox.kt b/app/src/main/java/com/bnyro/contacts/presentation/components/BlobIconBox.kt new file mode 100644 index 00000000..a444833f --- /dev/null +++ b/app/src/main/java/com/bnyro/contacts/presentation/components/BlobIconBox.kt @@ -0,0 +1,39 @@ +package com.bnyro.contacts.presentation.components + +import androidx.annotation.DrawableRes +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.size +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha +import androidx.compose.ui.graphics.ColorFilter +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.unit.dp +import com.bnyro.contacts.R + +@Composable +fun BlobIconBox(@DrawableRes icon: Int) { + Box( + contentAlignment = Alignment.Center, + modifier = Modifier + .fillMaxSize() + .alpha(0.3f) + ) { + Image( + modifier = Modifier.size(350.dp), + painter = painterResource(id = R.drawable.blob), + contentDescription = null, + colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.secondaryContainer) + ) + Image( + modifier = Modifier.size(250.dp), + painter = painterResource(id = icon), + contentDescription = null, + colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onSecondaryContainer) + ) + } +} diff --git a/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/SmsListScreen.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/SmsListScreen.kt index 32467ed3..c3fd5920 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/SmsListScreen.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/SmsListScreen.kt @@ -54,8 +54,8 @@ import com.bnyro.contacts.R import com.bnyro.contacts.domain.model.ContactData import com.bnyro.contacts.domain.model.SmsThread import com.bnyro.contacts.navigation.NavRoutes +import com.bnyro.contacts.presentation.components.BlobIconBox import com.bnyro.contacts.presentation.components.ClickableIcon -import com.bnyro.contacts.presentation.components.NothingHere import com.bnyro.contacts.presentation.components.TopBarMoreMenu import com.bnyro.contacts.presentation.features.NumberPickerDialog import com.bnyro.contacts.presentation.screens.calllog.SheetSettingItem @@ -162,9 +162,7 @@ fun SmsListScreen( SmsSearchScreen(smsModel, threadList, { showSearch = false }, onClickMessage) } } else { - Column(Modifier.padding(pv)) { - NothingHere() - } + BlobIconBox(icon = R.drawable.ic_no_sms) } if (showNumberPicker) { diff --git a/app/src/main/res/drawable/blob.xml b/app/src/main/res/drawable/blob.xml new file mode 100755 index 00000000..3bd4d837 --- /dev/null +++ b/app/src/main/res/drawable/blob.xml @@ -0,0 +1,10 @@ + + + + diff --git a/app/src/main/res/drawable/ic_no_sms.xml b/app/src/main/res/drawable/ic_no_sms.xml new file mode 100644 index 00000000..425cccb0 --- /dev/null +++ b/app/src/main/res/drawable/ic_no_sms.xml @@ -0,0 +1,29 @@ + + + + + From 0b5f75f36a33f323444491963c90e476f8563c47 Mon Sep 17 00:00:00 2001 From: Suhas Dissanayake Date: Tue, 14 May 2024 14:18:06 +0530 Subject: [PATCH 037/159] style: change contact photo placeholder in sms list --- .../screens/sms/components/SmsThreadItem.kt | 28 +++++++------------ .../java/com/bnyro/contacts/ui/theme/Theme.kt | 20 ++++++++----- 2 files changed, 23 insertions(+), 25 deletions(-) diff --git a/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/components/SmsThreadItem.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/components/SmsThreadItem.kt index dc37f370..0dfb491d 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/components/SmsThreadItem.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/components/SmsThreadItem.kt @@ -16,16 +16,14 @@ import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Person import androidx.compose.material3.ElevatedCard import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.SwipeToDismissBox import androidx.compose.material3.SwipeToDismissBoxValue import androidx.compose.material3.Text import androidx.compose.material3.rememberSwipeToDismissBoxState -import androidx.compose.material3.surfaceColorAtElevation import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -34,10 +32,10 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.asImageBitmap import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp @@ -46,6 +44,7 @@ import com.bnyro.contacts.domain.model.ContactData import com.bnyro.contacts.domain.model.SmsThread import com.bnyro.contacts.presentation.features.ConfirmationDialog import com.bnyro.contacts.presentation.screens.sms.model.SmsModel +import com.bnyro.contacts.ui.theme.LocalDarkTheme @OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class) @Composable @@ -95,37 +94,30 @@ fun SmsThreadItem( ), verticalAlignment = Alignment.CenterVertically ) { + val darkTheme = LocalDarkTheme.current Box( modifier = Modifier .size(58.dp) .clip(CircleShape) .background( - MaterialTheme.colorScheme.primary, - CircleShape + if (darkTheme) MaterialTheme.colorScheme.onSurfaceVariant else MaterialTheme.colorScheme.surfaceVariant ), contentAlignment = Alignment.Center ) { if (thread.contactData?.thumbnail != null) { Image( modifier = Modifier - .fillMaxSize() - .clip(CircleShape), + .fillMaxSize(), bitmap = thread.contactData.thumbnail!!.asImageBitmap(), contentDescription = null, contentScale = ContentScale.Crop ) } else { - Image( - modifier = Modifier - .padding(vertical = 12.dp) - .fillMaxSize(), - imageVector = Icons.Default.Person, + Icon( + modifier = Modifier.fillMaxSize(), + painter = painterResource(id = R.drawable.ic_person), contentDescription = null, - colorFilter = ColorFilter.tint( - MaterialTheme.colorScheme.surfaceColorAtElevation( - 10.dp - ) - ) + tint = if (darkTheme) MaterialTheme.colorScheme.surfaceVariant else MaterialTheme.colorScheme.onSurfaceVariant ) } } diff --git a/app/src/main/java/com/bnyro/contacts/ui/theme/Theme.kt b/app/src/main/java/com/bnyro/contacts/ui/theme/Theme.kt index 352dea4b..80dc12eb 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/theme/Theme.kt +++ b/app/src/main/java/com/bnyro/contacts/ui/theme/Theme.kt @@ -10,7 +10,9 @@ import androidx.compose.material3.dynamicLightColorScheme import androidx.compose.material3.lightColorScheme import androidx.compose.material3.surfaceColorAtElevation import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.SideEffect +import androidx.compose.runtime.compositionLocalOf import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.platform.LocalContext @@ -67,7 +69,8 @@ fun ConnectYouTheme( SideEffect { val activity = view.context as Activity if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - activity.window.navigationBarColor = colorScheme.surfaceColorAtElevation(10.dp).toArgb() + activity.window.navigationBarColor = + colorScheme.surfaceColorAtElevation(10.dp).toArgb() activity.window.statusBarColor = colorScheme.background.toArgb() WindowCompat.getInsetsController( activity.window, @@ -80,10 +83,13 @@ fun ConnectYouTheme( } } } - - MaterialTheme( - colorScheme = colorScheme, - typography = Typography, - content = content - ) + CompositionLocalProvider(LocalDarkTheme provides (darkTheme || amoledDark)) { + MaterialTheme( + colorScheme = colorScheme, + typography = Typography, + content = content + ) + } } + +val LocalDarkTheme = compositionLocalOf { false } \ No newline at end of file From efe6755c6ca1a54e7804296c8e2d89409dc392d8 Mon Sep 17 00:00:00 2001 From: Suhas Dissanayake Date: Tue, 14 May 2024 14:53:52 +0530 Subject: [PATCH 038/159] style: add call status icons --- .../screens/calllog/CallLogsScreen.kt | 32 ++++++++++++++++--- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/CallLogsScreen.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/CallLogsScreen.kt index bad95fa6..63f64e17 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/CallLogsScreen.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/CallLogsScreen.kt @@ -3,6 +3,7 @@ package com.bnyro.contacts.presentation.screens.calllog import android.annotation.SuppressLint import android.os.Build import android.provider.BlockedNumberContract +import android.provider.CallLog import android.text.format.DateUtils import android.view.SoundEffectConstants import androidx.annotation.StringRes @@ -26,7 +27,12 @@ import androidx.compose.foundation.lazy.items import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.rounded.CallMade +import androidx.compose.material.icons.automirrored.rounded.CallMissed +import androidx.compose.material.icons.automirrored.rounded.CallReceived import androidx.compose.material.icons.filled.Dialpad +import androidx.compose.material.icons.rounded.Block +import androidx.compose.material.icons.rounded.Close import androidx.compose.material.icons.rounded.FrontHand import androidx.compose.material.icons.rounded.Handshake import androidx.compose.material.icons.rounded.MoreVert @@ -69,7 +75,6 @@ import com.bnyro.contacts.presentation.screens.calllog.components.NumberInput import com.bnyro.contacts.presentation.screens.calllog.components.PhoneNumberDisplay import com.bnyro.contacts.presentation.screens.calllog.model.CallModel import com.bnyro.contacts.presentation.screens.contacts.model.ContactsModel -import com.bnyro.contacts.presentation.screens.editor.components.ContactIconPlaceholder import com.bnyro.contacts.presentation.screens.settings.model.ThemeModel import com.bnyro.contacts.util.IntentHelper @@ -163,10 +168,27 @@ fun CallLogsScreen( modifier = Modifier.padding(vertical = 10.dp, horizontal = 16.dp), verticalAlignment = Alignment.CenterVertically ) { - ContactIconPlaceholder( - themeModel = themeModel, - firstChar = contact?.displayName?.firstOrNull() - ) + Box( + modifier = Modifier + .size(48.dp) + .clip(CircleShape) + .background(color = MaterialTheme.colorScheme.surfaceVariant), + contentAlignment = Alignment.Center + ) { + val icon: ImageVector = when (it.type) { + CallLog.Calls.INCOMING_TYPE -> Icons.AutoMirrored.Rounded.CallReceived + CallLog.Calls.OUTGOING_TYPE -> Icons.AutoMirrored.Rounded.CallMade + CallLog.Calls.MISSED_TYPE -> Icons.AutoMirrored.Rounded.CallMissed + CallLog.Calls.REJECTED_TYPE -> Icons.Rounded.Close + else -> Icons.Rounded.Block + } + Icon( + modifier = Modifier.size(24.dp), + imageVector = icon, + contentDescription = null, + tint = MaterialTheme.colorScheme.onSurfaceVariant + ) + } Spacer(modifier = Modifier.width(20.dp)) From 7d8e3b60ee93f535c58292ec66f8f75bcaa72c3e Mon Sep 17 00:00:00 2001 From: Suhas Dissanayake Date: Tue, 14 May 2024 15:52:05 +0530 Subject: [PATCH 039/159] feat: group call logs by date --- .../contacts/domain/model/CallLogEntry.kt | 17 ++- .../screens/calllog/CallLogsScreen.kt | 126 ++++++++++-------- .../screens/calllog/model/CallModel.kt | 10 +- 3 files changed, 91 insertions(+), 62 deletions(-) diff --git a/app/src/main/java/com/bnyro/contacts/domain/model/CallLogEntry.kt b/app/src/main/java/com/bnyro/contacts/domain/model/CallLogEntry.kt index 668b6b85..12ceb674 100644 --- a/app/src/main/java/com/bnyro/contacts/domain/model/CallLogEntry.kt +++ b/app/src/main/java/com/bnyro/contacts/domain/model/CallLogEntry.kt @@ -1,8 +1,23 @@ package com.bnyro.contacts.domain.model +import java.text.SimpleDateFormat +import java.util.Date + data class CallLogEntry( val phoneNumber: String, val type: Int, val time: Long, val duration: Long -) +) { + val dateString: String + val timeString: String + + init { + val date = Date(time) + val dateFormat = SimpleDateFormat.getDateInstance(SimpleDateFormat.FULL) + dateString = dateFormat.format(date) + + val timeFormat = SimpleDateFormat.getTimeInstance(SimpleDateFormat.SHORT) + timeString = timeFormat.format(date) + } +} diff --git a/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/CallLogsScreen.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/CallLogsScreen.kt index 63f64e17..a939afd4 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/CallLogsScreen.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/CallLogsScreen.kt @@ -68,6 +68,7 @@ import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel import com.bnyro.contacts.R import com.bnyro.contacts.domain.model.CallLogEntry +import com.bnyro.contacts.presentation.components.CharacterHeader import com.bnyro.contacts.presentation.components.ClickableIcon import com.bnyro.contacts.presentation.components.NothingHere import com.bnyro.contacts.presentation.features.ConfirmationDialog @@ -96,7 +97,7 @@ fun CallLogsScreen( mutableStateOf(null) } - val callLog = callModel.callLogs + val groupedLogs = callModel.callLogs Scaffold( floatingActionButton = { FloatingActionButton( @@ -139,79 +140,86 @@ fun CallLogsScreen( }) } ) { pV -> - if (callLog.isNotEmpty()) { + if (groupedLogs.isNotEmpty()) { LazyColumn( modifier = Modifier.padding(pV) ) { - items(callLog) { - val contact = remember { - contactsModel.getContactByNumber(it.phoneNumber) + groupedLogs.entries.forEach { (time, callLog) -> + stickyHeader { + CharacterHeader(text = time) } - var showCallDialog by remember { - mutableStateOf(false) - } - val shape = RoundedCornerShape(20.dp) + items(callLog) { + val contact = remember { + contactsModel.getContactByNumber(it.phoneNumber) + } + var showCallDialog by remember { + mutableStateOf(false) + } + val shape = RoundedCornerShape(20.dp) - ElevatedCard( - modifier = Modifier - .fillMaxWidth() - .clip(shape) - .padding(horizontal = 10.dp, vertical = 5.dp) - .combinedClickable(onClick = { - if (it.phoneNumber.isNotBlank()) showCallDialog = true - }, onLongClick = { - selectedCallLog = it - }), - shape = shape - ) { - Row( - modifier = Modifier.padding(vertical = 10.dp, horizontal = 16.dp), - verticalAlignment = Alignment.CenterVertically + ElevatedCard( + modifier = Modifier + .fillMaxWidth() + .clip(shape) + .padding(horizontal = 10.dp, vertical = 5.dp) + .combinedClickable(onClick = { + if (it.phoneNumber.isNotBlank()) showCallDialog = true + }, onLongClick = { + selectedCallLog = it + }), + shape = shape ) { - Box( - modifier = Modifier - .size(48.dp) - .clip(CircleShape) - .background(color = MaterialTheme.colorScheme.surfaceVariant), - contentAlignment = Alignment.Center + Row( + modifier = Modifier.padding(vertical = 10.dp, horizontal = 16.dp), + verticalAlignment = Alignment.CenterVertically ) { - val icon: ImageVector = when (it.type) { - CallLog.Calls.INCOMING_TYPE -> Icons.AutoMirrored.Rounded.CallReceived - CallLog.Calls.OUTGOING_TYPE -> Icons.AutoMirrored.Rounded.CallMade - CallLog.Calls.MISSED_TYPE -> Icons.AutoMirrored.Rounded.CallMissed - CallLog.Calls.REJECTED_TYPE -> Icons.Rounded.Close - else -> Icons.Rounded.Block + Box( + modifier = Modifier + .size(48.dp) + .clip(CircleShape) + .background(color = MaterialTheme.colorScheme.surfaceVariant), + contentAlignment = Alignment.Center + ) { + val icon: ImageVector = when (it.type) { + CallLog.Calls.INCOMING_TYPE -> Icons.AutoMirrored.Rounded.CallReceived + CallLog.Calls.OUTGOING_TYPE -> Icons.AutoMirrored.Rounded.CallMade + CallLog.Calls.MISSED_TYPE -> Icons.AutoMirrored.Rounded.CallMissed + CallLog.Calls.REJECTED_TYPE -> Icons.Rounded.Close + else -> Icons.Rounded.Block + } + Icon( + modifier = Modifier.size(24.dp), + imageVector = icon, + contentDescription = null, + tint = MaterialTheme.colorScheme.onSurfaceVariant + ) } - Icon( - modifier = Modifier.size(24.dp), - imageVector = icon, - contentDescription = null, - tint = MaterialTheme.colorScheme.onSurfaceVariant - ) - } - Spacer(modifier = Modifier.width(20.dp)) + Spacer(modifier = Modifier.width(20.dp)) - Column { - Text(text = contact?.displayName ?: it.phoneNumber, maxLines = 1) + Column(Modifier.weight(1f)) { + Text( + text = contact?.displayName ?: it.phoneNumber, + maxLines = 1 + ) + } Text( - text = DateUtils.getRelativeTimeSpanString(it.time)?.toString() - .orEmpty() + text = it.timeString ) } } - } - if (showCallDialog) { - ConfirmationDialog( - onDismissRequest = { showCallDialog = false }, - title = stringResource(id = R.string.dial), - text = stringResource( - id = R.string.confirm_start_call, - contact?.displayName ?: it.phoneNumber - ) - ) { - callModel.callNumber(it.phoneNumber) + if (showCallDialog) { + ConfirmationDialog( + onDismissRequest = { showCallDialog = false }, + title = stringResource(id = R.string.dial), + text = stringResource( + id = R.string.confirm_start_call, + contact?.displayName ?: it.phoneNumber + ) + ) { + callModel.callNumber(it.phoneNumber) + } } } } diff --git a/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/model/CallModel.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/model/CallModel.kt index 14dfa9d9..a8b9e4a8 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/model/CallModel.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/model/CallModel.kt @@ -42,7 +42,10 @@ class CallModel(private val application: Application, savedStateHandle: SavedSta var numberToCall: String by mutableStateOf(initialPhoneNumber.orEmpty()) private set - var callLogs by mutableStateOf>(emptyList(), policy = neverEqualPolicy()) + var callLogs by mutableStateOf>>( + mapOf(), + policy = neverEqualPolicy() + ) private set private val contactsSet = mutableSetOf() @@ -64,7 +67,10 @@ class CallModel(private val application: Application, savedStateHandle: SavedSta init { viewModelScope.launch { - callLogs = callLogRepository.getCallLog() + val logs = callLogRepository.getCallLog() + callLogs = logs.groupBy { + it.dateString + } } } From 9841dd0d5e49f4e6c830353b96312a94d1e91a4b Mon Sep 17 00:00:00 2001 From: Suhas Dissanayake Date: Tue, 14 May 2024 16:12:17 +0530 Subject: [PATCH 040/159] feat: show sim number in call logs --- .../contacts/domain/model/CallLogEntry.kt | 3 +- .../domain/repositories/CallLogRepository.kt | 9 +++- .../screens/calllog/CallLogsScreen.kt | 42 +++++++++++++------ 3 files changed, 40 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/com/bnyro/contacts/domain/model/CallLogEntry.kt b/app/src/main/java/com/bnyro/contacts/domain/model/CallLogEntry.kt index 12ceb674..03e888d9 100644 --- a/app/src/main/java/com/bnyro/contacts/domain/model/CallLogEntry.kt +++ b/app/src/main/java/com/bnyro/contacts/domain/model/CallLogEntry.kt @@ -7,7 +7,8 @@ data class CallLogEntry( val phoneNumber: String, val type: Int, val time: Long, - val duration: Long + val duration: Long, + val subscriptionId: String? ) { val dateString: String val timeString: String diff --git a/app/src/main/java/com/bnyro/contacts/domain/repositories/CallLogRepository.kt b/app/src/main/java/com/bnyro/contacts/domain/repositories/CallLogRepository.kt index 9e5ca77e..751517e9 100644 --- a/app/src/main/java/com/bnyro/contacts/domain/repositories/CallLogRepository.kt +++ b/app/src/main/java/com/bnyro/contacts/domain/repositories/CallLogRepository.kt @@ -30,8 +30,15 @@ class CallLogRepository(private val context: Context) { val callType = cursor.intValue(CallLog.Calls.TYPE)!! val callDate = cursor.longValue(CallLog.Calls.DATE)!! val callDuration = cursor.longValue(CallLog.Calls.DURATION)!! + val subscriptionId = cursor.stringValue(CallLog.Calls.PHONE_ACCOUNT_ID) callLog.add( - CallLogEntry(phoneNumber.orEmpty(), callType, callDate, callDuration) + CallLogEntry( + phoneNumber.orEmpty(), + callType, + callDate, + callDuration, + subscriptionId + ) ) } } diff --git a/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/CallLogsScreen.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/CallLogsScreen.kt index a939afd4..15cf63ab 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/CallLogsScreen.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/CallLogsScreen.kt @@ -25,6 +25,7 @@ import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.CutCornerShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.rounded.CallMade @@ -144,13 +145,13 @@ fun CallLogsScreen( LazyColumn( modifier = Modifier.padding(pV) ) { - groupedLogs.entries.forEach { (time, callLog) -> + groupedLogs.entries.forEach { (time, callLogs) -> stickyHeader { CharacterHeader(text = time) } - items(callLog) { + items(callLogs) { callLog -> val contact = remember { - contactsModel.getContactByNumber(it.phoneNumber) + contactsModel.getContactByNumber(callLog.phoneNumber) } var showCallDialog by remember { mutableStateOf(false) @@ -163,9 +164,9 @@ fun CallLogsScreen( .clip(shape) .padding(horizontal = 10.dp, vertical = 5.dp) .combinedClickable(onClick = { - if (it.phoneNumber.isNotBlank()) showCallDialog = true + if (callLog.phoneNumber.isNotBlank()) showCallDialog = true }, onLongClick = { - selectedCallLog = it + selectedCallLog = callLog }), shape = shape ) { @@ -180,7 +181,7 @@ fun CallLogsScreen( .background(color = MaterialTheme.colorScheme.surfaceVariant), contentAlignment = Alignment.Center ) { - val icon: ImageVector = when (it.type) { + val icon: ImageVector = when (callLog.type) { CallLog.Calls.INCOMING_TYPE -> Icons.AutoMirrored.Rounded.CallReceived CallLog.Calls.OUTGOING_TYPE -> Icons.AutoMirrored.Rounded.CallMade CallLog.Calls.MISSED_TYPE -> Icons.AutoMirrored.Rounded.CallMissed @@ -199,13 +200,30 @@ fun CallLogsScreen( Column(Modifier.weight(1f)) { Text( - text = contact?.displayName ?: it.phoneNumber, + text = contact?.displayName ?: callLog.phoneNumber, maxLines = 1 ) } - Text( - text = it.timeString - ) + Column(horizontalAlignment = Alignment.End) { + callLog.subscriptionId?.let { + Box( + modifier = Modifier + .clip(CutCornerShape(10, 35, 10, 10)) + .background(MaterialTheme.colorScheme.tertiaryContainer), + contentAlignment = Alignment.Center + ) { + Text( + modifier = Modifier.padding(horizontal = 4.dp), + text = it, + maxLines = 1, + color = MaterialTheme.colorScheme.onTertiaryContainer + ) + } + } + Text( + text = callLog.timeString + ) + } } } @@ -215,10 +233,10 @@ fun CallLogsScreen( title = stringResource(id = R.string.dial), text = stringResource( id = R.string.confirm_start_call, - contact?.displayName ?: it.phoneNumber + contact?.displayName ?: callLog.phoneNumber ) ) { - callModel.callNumber(it.phoneNumber) + callModel.callNumber(callLog.phoneNumber) } } } From 80a56fead4ea9da5bbe800c4702defed1ad1a6c7 Mon Sep 17 00:00:00 2001 From: Suhas Dissanayake Date: Tue, 14 May 2024 20:56:52 +0530 Subject: [PATCH 041/159] chore: update dependencies --- app/build.gradle.kts | 47 +++++++++++-------- app/src/main/AndroidManifest.xml | 1 - .../contacts/ui/activities/MainActivity.kt | 2 +- build.gradle.kts | 10 ++-- gradle/wrapper/gradle-wrapper.properties | 6 +-- 5 files changed, 35 insertions(+), 31 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 29e4173e..82e7168d 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,7 +1,9 @@ plugins { id("com.android.application") - id("org.jetbrains.kotlin.android") - id("org.jetbrains.kotlin.kapt") + kotlin("android") + id("com.google.devtools.ksp") + id("kotlin-parcelize") + id("org.jetbrains.kotlin.plugin.serialization") version "1.9.23" } android { @@ -24,10 +26,8 @@ android { useSupportLibrary = true } - kapt { - arguments { - arg("room.schemaLocation", "$projectDir/schemas") - } + ksp { + arg("room.schemaLocation", "$projectDir/schemas") } } @@ -56,7 +56,7 @@ android { compose = true } composeOptions { - kotlinCompilerExtensionVersion = "1.4.2" + kotlinCompilerExtensionVersion = "1.5.11" } packagingOptions { resources { @@ -75,12 +75,15 @@ dependencies { implementation("androidx.work:work-runtime-ktx:2.9.0") // Compose & UI - implementation("androidx.activity:activity-compose:1.8.2") - implementation("androidx.compose.ui:ui:$compose_version") - implementation("androidx.compose.ui:ui-tooling-preview:$compose_version") - implementation("androidx.compose.material3:material3:1.2.1") - implementation("androidx.compose.material:material-icons-extended:1.6.5") + implementation("androidx.activity:activity-compose:1.9.0") + implementation(platform("androidx.compose:compose-bom:2024.05.00")) + implementation("androidx.compose.ui:ui") + implementation("androidx.compose.ui:ui-graphics") + implementation("androidx.compose.ui:ui-tooling-preview") + implementation("androidx.compose.material3:material3") + implementation("androidx.compose.material:material-icons-extended:1.6.7") implementation("com.github.nanihadesuka:LazyColumnScrollbar:1.9.0") + implementation("androidx.navigation:navigation-compose:2.8.0-alpha08") // VCard implementation("com.googlecode.ez-vcard:ez-vcard:0.11.3") @@ -88,10 +91,10 @@ dependencies { // Image parsing implementation("androidx.exifinterface:exifinterface:1.3.7") - implementation("io.coil-kt:coil-compose:2.4.0") + implementation("io.coil-kt:coil-compose:2.6.0") // Phone number formatting - implementation("com.googlecode.libphonenumber:libphonenumber:8.2.0") + implementation("com.googlecode.libphonenumber:libphonenumber:8.13.35") // Biometrics implementation("androidx.biometric:biometric-ktx:1.2.0-alpha05") @@ -101,15 +104,19 @@ dependencies { implementation("com.halilibo.compose-richtext:richtext-commonmark:0.17.0") // Room database - implementation("androidx.room:room-ktx:2.6.1") - implementation("androidx.navigation:navigation-compose:2.7.7") - kapt("androidx.room:room-compiler:2.6.1") + val roomVersion = "2.6.1" + + implementation("androidx.room:room-runtime:$roomVersion") + implementation("androidx.room:room-ktx:$roomVersion") + annotationProcessor("androidx.room:room-compiler:$roomVersion") + ksp("androidx.room:room-compiler:$roomVersion") // Testing testImplementation("junit:junit:4.13.2") androidTestImplementation("androidx.test.ext:junit:1.1.5") androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") - androidTestImplementation("androidx.compose.ui:ui-test-junit4:$compose_version") - debugImplementation("androidx.compose.ui:ui-tooling:$compose_version") - debugImplementation("androidx.compose.ui:ui-test-manifest:$compose_version") + androidTestImplementation(platform("androidx.compose:compose-bom:2024.05.00")) + androidTestImplementation("androidx.compose.ui:ui-test-junit4") + debugImplementation("androidx.compose.ui:ui-tooling") + debugImplementation("androidx.compose.ui:ui-test-manifest") } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a6351e9f..8423160e 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -38,7 +38,6 @@ - Date: Wed, 15 May 2024 11:13:40 +0530 Subject: [PATCH 042/159] refactor: use type safe navigation --- app/build.gradle.kts | 2 + .../contacts/navigation/HomeNavContainer.kt | 12 +-- .../bnyro/contacts/navigation/HomeNavHost.kt | 17 ++-- .../com/bnyro/contacts/navigation/NavHost.kt | 17 ++-- .../bnyro/contacts/navigation/NavRoutes.kt | 78 ++++++++++++------- .../contacts/ui/activities/MainActivity.kt | 2 +- 6 files changed, 74 insertions(+), 54 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 82e7168d..638757b6 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -111,6 +111,8 @@ dependencies { annotationProcessor("androidx.room:room-compiler:$roomVersion") ksp("androidx.room:room-compiler:$roomVersion") + implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3") + // Testing testImplementation("junit:junit:4.13.2") androidTestImplementation("androidx.test.ext:junit:1.1.5") diff --git a/app/src/main/java/com/bnyro/contacts/navigation/HomeNavContainer.kt b/app/src/main/java/com/bnyro/contacts/navigation/HomeNavContainer.kt index 842e290e..5d403168 100644 --- a/app/src/main/java/com/bnyro/contacts/navigation/HomeNavContainer.kt +++ b/app/src/main/java/com/bnyro/contacts/navigation/HomeNavContainer.kt @@ -22,6 +22,7 @@ import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.navigation.NavController +import androidx.navigation.NavDestination.Companion.hasRoute import androidx.navigation.compose.rememberNavController import com.bnyro.contacts.presentation.screens.contacts.model.ContactsModel import com.bnyro.contacts.presentation.screens.settings.model.ThemeModel @@ -30,7 +31,7 @@ import com.bnyro.contacts.presentation.screens.sms.model.SmsModel @Composable fun HomeNavContainer( initialTab: HomeRoutes, - onNavigate: (String) -> Unit, + onNavigate: (NavRoutes) -> Unit, smsModel: SmsModel, contactsModel: ContactsModel, themeModel: ThemeModel @@ -44,8 +45,8 @@ fun HomeNavContainer( // listen for destination changes (e.g. back presses) DisposableEffect(Unit) { val listener = NavController.OnDestinationChangedListener { _, destination, _ -> - HomeRoutes.all.firstOrNull { it.route == destination.route } - ?.also { selectedRoute = it } + HomeRoutes.all.firstOrNull { destination.hasRoute(it.route::class) } + ?.also { selectedRoute = it.route } } navController.addOnDestinationChangedListener(listener) @@ -69,7 +70,7 @@ fun HomeNavContainer( icon = { Icon(it.icon, null) }, - selected = it == selectedRoute, + selected = it.route == selectedRoute, onClick = { navController.popBackStack() navController.navigate(it.route) @@ -88,7 +89,8 @@ fun HomeNavContainer( if (orientation == Configuration.ORIENTATION_LANDSCAPE) { NavigationRail { HomeRoutes.all.forEach { - NavigationRailItem(selected = it == selectedRoute, + NavigationRailItem( + selected = it.route == selectedRoute, onClick = { navController.popBackStack() navController.navigate(it.route) diff --git a/app/src/main/java/com/bnyro/contacts/navigation/HomeNavHost.kt b/app/src/main/java/com/bnyro/contacts/navigation/HomeNavHost.kt index e63c2072..995ca005 100644 --- a/app/src/main/java/com/bnyro/contacts/navigation/HomeNavHost.kt +++ b/app/src/main/java/com/bnyro/contacts/navigation/HomeNavHost.kt @@ -18,7 +18,7 @@ import com.bnyro.contacts.presentation.screens.sms.model.SmsModel @Composable fun HomeNavHost( navController: NavHostController, - onNavigate: (String) -> Unit, + onNavigate: (NavRoutes) -> Unit, startTab: HomeRoutes, modifier: Modifier = Modifier, smsModel: SmsModel, @@ -27,33 +27,32 @@ fun HomeNavHost( ) { val viewModelStoreOwner: ViewModelStoreOwner = LocalViewModelStoreOwner.current!! - NavHost(navController, startDestination = startTab.route, modifier = modifier) { - composable(HomeRoutes.Contacts.route) { + NavHost(navController, startDestination = startTab, modifier = modifier) { + composable { CompositionLocalProvider(LocalViewModelStoreOwner provides viewModelStoreOwner) { ContactsPage(null, onNavigate = { - onNavigate.invoke(it.route) + onNavigate.invoke(it) }) } } - composable( - HomeRoutes.Phone.route, + composable( deepLinks = HomeRoutes.Phone.deepLinks ) { CallLogsScreen(contactsModel, themeModel) } - composable(HomeRoutes.Messages.route) { + composable { CompositionLocalProvider(LocalViewModelStoreOwner provides viewModelStoreOwner) { SmsListScreen( smsModel = smsModel, contactsModel = contactsModel, scrollConnection = null, onNavigate = { - onNavigate.invoke(it.route) + onNavigate.invoke(it) }, onClickMessage = { address, contactData -> smsModel.currentContactData = contactData - onNavigate.invoke("${NavRoutes.MessageThread.route}/$address") + onNavigate.invoke(NavRoutes.MessageThread(address)) } ) } diff --git a/app/src/main/java/com/bnyro/contacts/navigation/NavHost.kt b/app/src/main/java/com/bnyro/contacts/navigation/NavHost.kt index 609efc1e..3af9a5cb 100644 --- a/app/src/main/java/com/bnyro/contacts/navigation/NavHost.kt +++ b/app/src/main/java/com/bnyro/contacts/navigation/NavHost.kt @@ -10,10 +10,9 @@ import androidx.compose.ui.Modifier import androidx.lifecycle.ViewModelStoreOwner import androidx.lifecycle.viewmodel.compose.LocalViewModelStoreOwner import androidx.navigation.NavHostController -import androidx.navigation.NavType import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable -import androidx.navigation.navArgument +import androidx.navigation.toRoute import com.bnyro.contacts.presentation.screens.about.AboutScreen import com.bnyro.contacts.presentation.screens.contacts.model.ContactsModel import com.bnyro.contacts.presentation.screens.settings.SettingsScreen @@ -32,8 +31,8 @@ fun AppNavHost( ) { val viewModelStoreOwner: ViewModelStoreOwner = LocalViewModelStoreOwner.current!! - NavHost(navController, startDestination = NavRoutes.Home.route, modifier = modifier) { - composable(NavRoutes.Home.route, + NavHost(navController, startDestination = NavRoutes.Home, modifier = modifier) { + composable( enterTransition = { slideIntoContainer( AnimatedContentTransitionScope.SlideDirection.Down, @@ -54,7 +53,7 @@ fun AppNavHost( themeModel = themeModel ) } - composable(NavRoutes.About.route, + composable( enterTransition = { slideIntoContainer( AnimatedContentTransitionScope.SlideDirection.Up, @@ -69,7 +68,7 @@ fun AppNavHost( navController.popBackStack() } } - composable(NavRoutes.Settings.route, + composable( enterTransition = { slideIntoContainer( AnimatedContentTransitionScope.SlideDirection.Up, @@ -84,9 +83,7 @@ fun AppNavHost( navController.popBackStack() } } - composable( - "${NavRoutes.MessageThread.route}/{address}", - listOf(navArgument("address") { type = NavType.StringType }), + composable( enterTransition = { slideIntoContainer( AnimatedContentTransitionScope.SlideDirection.Up, @@ -98,7 +95,7 @@ fun AppNavHost( targetOffset = { it / 4 }) + fadeOut() } ) { - val address = it.arguments?.getString("address") + val address = it.toRoute().address CompositionLocalProvider(LocalViewModelStoreOwner provides viewModelStoreOwner) { SmsThreadScreen( smsModel = smsModel, diff --git a/app/src/main/java/com/bnyro/contacts/navigation/NavRoutes.kt b/app/src/main/java/com/bnyro/contacts/navigation/NavRoutes.kt index d1c82cea..e1c98cfb 100644 --- a/app/src/main/java/com/bnyro/contacts/navigation/NavRoutes.kt +++ b/app/src/main/java/com/bnyro/contacts/navigation/NavRoutes.kt @@ -2,48 +2,68 @@ package com.bnyro.contacts.navigation import androidx.annotation.StringRes import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.rounded.Message +import androidx.compose.material.icons.automirrored.rounded.Message import androidx.compose.material.icons.rounded.Person import androidx.compose.material.icons.rounded.Phone import androidx.compose.ui.graphics.vector.ImageVector import androidx.core.net.toUri import androidx.navigation.navDeepLink import com.bnyro.contacts.R +import kotlinx.serialization.Serializable -sealed class NavRoutes( - val route: String -) { - object Home : NavRoutes("home") - object About : NavRoutes("about") - object Settings : NavRoutes("settings") - object MessageThread : NavRoutes("message_thread") +@Serializable +sealed class NavRoutes { + @Serializable + data object Home : NavRoutes() + + @Serializable + data object About : NavRoutes() + + @Serializable + data object Settings : NavRoutes() + + @Serializable + data class MessageThread( + val address: String? + ) : NavRoutes() } -sealed class HomeRoutes( - val route: String, - @StringRes val stringRes: Int, - val icon: ImageVector -) { - object Phone : HomeRoutes("phone", R.string.dial, Icons.Rounded.Phone) { - val phoneNumber = "phoneNumber" - val deepLink = "connectyou://dial/{$phoneNumber}" - val navAction = "com.bnyro.contacts.DIAL" - val deepLinks = listOf(navDeepLink { - uriPattern = deepLink - action = navAction - }) - - fun getDeepLink(number: String) = "connectyou://dial/$number".toUri() +@Serializable +sealed class HomeRoutes() { + @Serializable + data class Phone( + val phoneNumber: String? = null + ) : HomeRoutes() { + companion object { + const val phoneNumber = "phoneNumber" + const val deepLink = "connectyou://dial/{$phoneNumber}" + const val navAction = "com.bnyro.contacts.DIAL" + val deepLinks = listOf(navDeepLink { + uriPattern = deepLink + action = navAction + }) + + fun getDeepLink(number: String) = "connectyou://dial/$number".toUri() + } } - object Contacts : HomeRoutes("contacts", R.string.contacts, Icons.Rounded.Person) - object Messages : HomeRoutes("messages", R.string.messages, Icons.Rounded.Message) + @Serializable + data object Contacts : HomeRoutes() + + @Serializable + data object Messages : HomeRoutes() companion object { val all = listOf( - Phone, - Contacts, - Messages + HomeNavBarItem(Phone(), R.string.dial, Icons.Rounded.Phone), + HomeNavBarItem(Contacts, R.string.contacts, Icons.Rounded.Person), + HomeNavBarItem(Messages, R.string.messages, Icons.AutoMirrored.Rounded.Message) ) } -} \ No newline at end of file +} + +data class HomeNavBarItem( + val route: HomeRoutes, + @StringRes val stringRes: Int, + val icon: ImageVector +) \ No newline at end of file diff --git a/app/src/main/java/com/bnyro/contacts/ui/activities/MainActivity.kt b/app/src/main/java/com/bnyro/contacts/ui/activities/MainActivity.kt index e0064895..019509ff 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/activities/MainActivity.kt +++ b/app/src/main/java/com/bnyro/contacts/ui/activities/MainActivity.kt @@ -55,7 +55,7 @@ class MainActivity : BaseActivity() { } if (authSuccess) { - NavContainer(HomeRoutes.all[initialTabIndex.coerceIn(0, 2)]) + NavContainer(HomeRoutes.all[initialTabIndex.coerceIn(0, 2)].route) getInsertOrEditNumber()?.let { AddToContactDialog(it) } From 627ca77236d8f7edc55f5a27b1ce29b000f5507b Mon Sep 17 00:00:00 2001 From: Suhas Dissanayake Date: Wed, 15 May 2024 13:26:04 +0530 Subject: [PATCH 043/159] fix: message button not working --- .../com/bnyro/contacts/navigation/NavHost.kt | 6 ++- .../bnyro/contacts/navigation/NavRoutes.kt | 43 +++++++++++++--- .../presentation/screens/sms/SmsListScreen.kt | 5 -- .../screens/sms/model/SmsModel.kt | 5 -- .../contacts/ui/activities/MainActivity.kt | 49 +++++++++++++++---- 5 files changed, 80 insertions(+), 28 deletions(-) diff --git a/app/src/main/java/com/bnyro/contacts/navigation/NavHost.kt b/app/src/main/java/com/bnyro/contacts/navigation/NavHost.kt index 3af9a5cb..3a168bdd 100644 --- a/app/src/main/java/com/bnyro/contacts/navigation/NavHost.kt +++ b/app/src/main/java/com/bnyro/contacts/navigation/NavHost.kt @@ -84,6 +84,7 @@ fun AppNavHost( } } composable( + deepLinks = NavRoutes.MessageThread.deepLinks, enterTransition = { slideIntoContainer( AnimatedContentTransitionScope.SlideDirection.Up, @@ -95,13 +96,14 @@ fun AppNavHost( targetOffset = { it / 4 }) + fadeOut() } ) { - val address = it.toRoute().address + val thread = it.toRoute() CompositionLocalProvider(LocalViewModelStoreOwner provides viewModelStoreOwner) { SmsThreadScreen( smsModel = smsModel, contactsModel = contactsModel, contactsData = remember { smsModel.currentContactData }, - address = address.orEmpty() + address = thread.address, + initialText = thread.body.orEmpty() ) { navController.popBackStack() } diff --git a/app/src/main/java/com/bnyro/contacts/navigation/NavRoutes.kt b/app/src/main/java/com/bnyro/contacts/navigation/NavRoutes.kt index e1c98cfb..eb4745e4 100644 --- a/app/src/main/java/com/bnyro/contacts/navigation/NavRoutes.kt +++ b/app/src/main/java/com/bnyro/contacts/navigation/NavRoutes.kt @@ -1,5 +1,6 @@ package com.bnyro.contacts.navigation +import android.net.Uri import androidx.annotation.StringRes import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.rounded.Message @@ -10,6 +11,7 @@ import androidx.core.net.toUri import androidx.navigation.navDeepLink import com.bnyro.contacts.R import kotlinx.serialization.Serializable +import java.net.URLEncoder @Serializable sealed class NavRoutes { @@ -24,8 +26,38 @@ sealed class NavRoutes { @Serializable data class MessageThread( - val address: String? - ) : NavRoutes() + val address: String, + val body: String? = null + ) : NavRoutes() { + companion object { + const val address = "address" + const val body = "body" + private const val basePath = "connectyou://message" + const val navAction = "com.bnyro.contacts.SMS" + val deepLinks = listOf(navDeepLink(basePath) { + action = navAction + }) + + + fun getDeepLink(number: String, bodyText: String? = null): Uri { + return if (bodyText == null) { + "connectyou://message/${ + URLEncoder.encode( + number, + "UTF-8" + ) + }".toUri() + } else { + "connectyou://message/${ + URLEncoder.encode( + number, + "UTF-8" + ) + }?body=${URLEncoder.encode(bodyText, "UTF-8")}".toUri() + } + } + } + } } @Serializable @@ -36,14 +68,13 @@ sealed class HomeRoutes() { ) : HomeRoutes() { companion object { const val phoneNumber = "phoneNumber" - const val deepLink = "connectyou://dial/{$phoneNumber}" + private const val basePath = "connectyou://dial" const val navAction = "com.bnyro.contacts.DIAL" - val deepLinks = listOf(navDeepLink { - uriPattern = deepLink + val deepLinks = listOf(navDeepLink(basePath) { action = navAction }) - fun getDeepLink(number: String) = "connectyou://dial/$number".toUri() + fun getDeepLink(number: String) = "connectyou://dial?phoneNumber=$number".toUri() } } diff --git a/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/SmsListScreen.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/SmsListScreen.kt index c3fd5920..d4953fc9 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/SmsListScreen.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/SmsListScreen.kt @@ -84,11 +84,6 @@ fun SmsListScreen( var selectedThread by remember { mutableStateOf(null) } - LaunchedEffect(Unit) { - smsModel.initialAddressAndBody?.let { - onClickMessage(it.first, null) - } - } Scaffold( topBar = { TopAppBar( diff --git a/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/model/SmsModel.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/model/SmsModel.kt index f4afd8ca..68efc332 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/model/SmsModel.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/model/SmsModel.kt @@ -4,9 +4,6 @@ import android.annotation.SuppressLint import android.app.Application import android.content.Context import android.telephony.SubscriptionInfo -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.setValue import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.viewModelScope import com.bnyro.contacts.App @@ -27,8 +24,6 @@ class SmsModel(application: Application) : AndroidViewModel(application) { var currentContactData: ContactData? = null - var initialAddressAndBody by mutableStateOf?>(null) - var currentSubscription: SubscriptionInfo? = null private fun updateSmsList() { diff --git a/app/src/main/java/com/bnyro/contacts/ui/activities/MainActivity.kt b/app/src/main/java/com/bnyro/contacts/ui/activities/MainActivity.kt index 019509ff..c217cb0d 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/activities/MainActivity.kt +++ b/app/src/main/java/com/bnyro/contacts/ui/activities/MainActivity.kt @@ -18,6 +18,7 @@ import androidx.compose.ui.platform.LocalContext import com.bnyro.contacts.domain.model.ContactData import com.bnyro.contacts.navigation.HomeRoutes import com.bnyro.contacts.navigation.NavContainer +import com.bnyro.contacts.navigation.NavRoutes import com.bnyro.contacts.presentation.features.AddToContactDialog import com.bnyro.contacts.presentation.features.ConfirmImportContactsDialog import com.bnyro.contacts.ui.theme.ConnectYouTheme @@ -42,10 +43,7 @@ class MainActivity : BaseActivity() { contactsModel.initialContactId = getInitialContactId() contactsModel.initialContactData = getInsertContactData() - smsModel.initialAddressAndBody = getInitialSmsAddressAndBody() - - val initialTabIndex = smsModel.initialAddressAndBody?.let { 2 } - ?: Preferences.getInt(Preferences.homeTabKey, 1) + val initialTabIndex = Preferences.getInt(Preferences.homeTabKey, 1) setContent { ConnectYouTheme(themeModel.themeMode) { val context = LocalContext.current @@ -84,16 +82,31 @@ class MainActivity : BaseActivity() { super.onNewIntent(intent) } - private fun processIntent(intent: Intent?) { + private fun processIntent(intent: Intent) { + val initialSmsAddressAndBody = getInitialSmsAddressAndBody() + if (initialSmsAddressAndBody != null) { + openSMSThread(initialSmsAddressAndBody.first, initialSmsAddressAndBody.second) + } + var number: String? = null - if (intent?.action == Intent.ACTION_INSERT_OR_EDIT) { + if (intent.action == Intent.ACTION_INSERT_OR_EDIT) { number = intent.getStringExtra(Intents.Insert.PHONE) - } else if (intent?.action == Intent.ACTION_DIAL || intent?.action == Intent.ACTION_VIEW) { + } else if (intent.action == Intent.ACTION_DIAL || intent.action == Intent.ACTION_VIEW) { if (intent.data?.scheme == "tel") { number = intent.data?.schemeSpecificPart } } if (number != null) openDialPad(number) + + var address: String? = null + if (intent.action == Intent.ACTION_INSERT_OR_EDIT) { + address = intent.getStringExtra(Intents.Insert.PHONE) + } else if (intent.action == Intent.ACTION_DIAL || intent.action == Intent.ACTION_VIEW) { + if (intent.data?.scheme == "sms") { + address = intent.data?.schemeSpecificPart + } + } + if (address != null) openSMSThread(address, null) } private fun openDialPad(number: String) { @@ -114,6 +127,24 @@ class MainActivity : BaseActivity() { deepLinkPendingIntent?.send() } + private fun openSMSThread(address: String, body: String?) { + val deepLinkIntent = Intent( + NavRoutes.MessageThread.navAction, + NavRoutes.MessageThread.getDeepLink(address, body), + this, + MainActivity::class.java + ) + + val deepLinkPendingIntent: PendingIntent? = TaskStackBuilder.create(this).run { + addNextIntentWithParentStack(deepLinkIntent) + getPendingIntent( + 11, + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE + ) + } + deepLinkPendingIntent?.send() + } + private fun getInsertContactData(): ContactData? { return when { intent?.action == Intent.ACTION_INSERT -> { @@ -142,9 +173,7 @@ class MainActivity : BaseActivity() { private fun getInitialSmsAddressAndBody(): Pair? { if (intent?.action !in smsSendIntents || intent?.type in BackupHelper.vCardMimeTypes) return null - val address = intent?.dataString - ?.split(":") - ?.lastOrNull() + val address = intent?.data?.schemeSpecificPart // the number is url encoded and hence must be decoded first ?.let { URLDecoder.decode(it, "UTF-8") } ?: return null From d279737e9d474cae6862ac875532fff154023c50 Mon Sep 17 00:00:00 2001 From: Suhas Dissanayake Date: Wed, 15 May 2024 18:56:22 +0530 Subject: [PATCH 044/159] style: make colorful contact icons match with the material you theme --- .../components/ContactIconPlaceholder.kt | 19 +++++++----------- .../com/bnyro/contacts/util/ColorUtils.kt | 20 ++++++++++++++++++- .../contacts/util/extension/ContentColor.kt | 17 ---------------- 3 files changed, 26 insertions(+), 30 deletions(-) delete mode 100644 app/src/main/java/com/bnyro/contacts/util/extension/ContentColor.kt diff --git a/app/src/main/java/com/bnyro/contacts/presentation/screens/editor/components/ContactIconPlaceholder.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/editor/components/ContactIconPlaceholder.kt index 67d61e95..7890b90c 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/screens/editor/components/ContactIconPlaceholder.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/editor/components/ContactIconPlaceholder.kt @@ -10,26 +10,21 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import com.bnyro.contacts.presentation.screens.settings.model.ThemeModel import com.bnyro.contacts.util.ColorUtils -import com.bnyro.contacts.util.extension.contentColor @Composable fun ContactIconPlaceholder( themeModel: ThemeModel, firstChar: Char? ) { - val backgroundColor = if (themeModel.colorfulIcons) { - remember { Color(ColorUtils.getRandomColor()) } - } else { - MaterialTheme.colorScheme.primary - } - val contentColor = when { - !themeModel.colorfulIcons -> MaterialTheme.colorScheme.onPrimary - else -> backgroundColor.contentColor() + val container = MaterialTheme.colorScheme.secondaryContainer + val onContainer = MaterialTheme.colorScheme.onSecondaryContainer + val color = when (themeModel.colorfulIcons) { + true -> remember { ColorUtils.getRandomMaterialColorPair(container, onContainer) } + false -> container to onContainer } Box( @@ -37,13 +32,13 @@ fun ContactIconPlaceholder( .size(42.dp) .background( shape = CircleShape, - color = backgroundColor + color = color.first ) ) { Text( modifier = Modifier.align(Alignment.Center), text = firstChar?.toString() ?: "?", - color = contentColor, + color = color.second, fontWeight = FontWeight.Bold ) } diff --git a/app/src/main/java/com/bnyro/contacts/util/ColorUtils.kt b/app/src/main/java/com/bnyro/contacts/util/ColorUtils.kt index 2875b2ed..217bf367 100644 --- a/app/src/main/java/com/bnyro/contacts/util/ColorUtils.kt +++ b/app/src/main/java/com/bnyro/contacts/util/ColorUtils.kt @@ -1,5 +1,23 @@ package com.bnyro.contacts.util +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.toArgb +import kotlin.random.Random + + object ColorUtils { - fun getRandomColor(): Int = (Math.random() * 16777215).toInt() or (0xFF shl 24) + private val random = Random(System.currentTimeMillis()) + + fun getRandomMaterialColorPair(container: Color, content: Color): Pair { + val hue = random.nextFloat() * 360 + val color1 = getMaterialColorWithHue(container, hue) + val color2 = getMaterialColorWithHue(content, hue) + return Pair(color1, color2) + } + + private fun getMaterialColorWithHue(original: Color, hue: Float): Color { + val hsv = FloatArray(3) + android.graphics.Color.colorToHSV(original.toArgb(), hsv) + return Color.hsv(hue, hsv[1], hsv[2]) + } } diff --git a/app/src/main/java/com/bnyro/contacts/util/extension/ContentColor.kt b/app/src/main/java/com/bnyro/contacts/util/extension/ContentColor.kt deleted file mode 100644 index ef375629..00000000 --- a/app/src/main/java/com/bnyro/contacts/util/extension/ContentColor.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.bnyro.contacts.util.extension - -import androidx.compose.material3.MaterialTheme -import androidx.compose.runtime.Composable -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.luminance - -@Composable -fun Color.contentColor(): Color { - val isDarkTheme = MaterialTheme.colorScheme.background.luminance() < 0.5 - return when { - luminance().let { if (!isDarkTheme) it else 1 - it } > 0.5 -> { - MaterialTheme.typography.bodyLarge.color - } - else -> MaterialTheme.colorScheme.background - } -} From fed6b85e4ab60dfd8c7da80db80ab285eeb78f9c Mon Sep 17 00:00:00 2001 From: Suhas Dissanayake Date: Fri, 17 May 2024 12:53:07 +0530 Subject: [PATCH 045/159] style: improved contact picker dialog --- .../domain/model/ContactSingleDataItem.kt | 8 + .../features/NumberPickerDialog.kt | 318 +++++++++++++----- .../presentation/screens/sms/SmsListScreen.kt | 4 + app/src/main/res/values/strings.xml | 2 + 4 files changed, 253 insertions(+), 79 deletions(-) create mode 100644 app/src/main/java/com/bnyro/contacts/domain/model/ContactSingleDataItem.kt diff --git a/app/src/main/java/com/bnyro/contacts/domain/model/ContactSingleDataItem.kt b/app/src/main/java/com/bnyro/contacts/domain/model/ContactSingleDataItem.kt new file mode 100644 index 00000000..55144526 --- /dev/null +++ b/app/src/main/java/com/bnyro/contacts/domain/model/ContactSingleDataItem.kt @@ -0,0 +1,8 @@ +package com.bnyro.contacts.domain.model + +data class ContactSingleDataItem( + val thumbnail: Any?, + val name: String, + val data: String, + val contactData: ContactData? +) diff --git a/app/src/main/java/com/bnyro/contacts/presentation/features/NumberPickerDialog.kt b/app/src/main/java/com/bnyro/contacts/presentation/features/NumberPickerDialog.kt index 84e35504..77efb7ac 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/features/NumberPickerDialog.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/features/NumberPickerDialog.kt @@ -1,17 +1,33 @@ package com.bnyro.contacts.presentation.features +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Send -import androidx.compose.material3.AlertDialog -import androidx.compose.material3.OutlinedTextField +import androidx.compose.material.icons.automirrored.rounded.ArrowBackIos +import androidx.compose.material.icons.rounded.AddComment +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface import androidx.compose.material3.Text +import androidx.compose.material3.TextField +import androidx.compose.material3.TextFieldDefaults +import androidx.compose.material3.surfaceColorAtElevation import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -19,105 +35,249 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp +import androidx.compose.ui.window.Dialog +import androidx.compose.ui.window.DialogProperties +import coil.compose.AsyncImage import com.bnyro.contacts.R import com.bnyro.contacts.domain.enums.SortOrder import com.bnyro.contacts.domain.model.ContactData -import com.bnyro.contacts.presentation.components.ClickableIcon -import com.bnyro.contacts.presentation.components.ClickableText -import com.bnyro.contacts.presentation.screens.contacts.components.ContactItem +import com.bnyro.contacts.domain.model.ContactSingleDataItem import com.bnyro.contacts.presentation.screens.contacts.model.ContactsModel +import com.bnyro.contacts.presentation.screens.editor.components.ContactIconPlaceholder +import com.bnyro.contacts.presentation.screens.settings.model.ThemeModel @Composable fun NumberPickerDialog( contactsModel: ContactsModel, + themeModel: ThemeModel, onDismissRequest: () -> Unit, - onNumberSelect: (number: String, contactData: ContactData?) -> Unit, + onNumberSelect: (number: String, contactData: ContactData?) -> Unit ) { - - var numbersToSelectFrom: Pair> by remember { - mutableStateOf(null to listOf()) - } - - AlertDialog( + Dialog( onDismissRequest = onDismissRequest, - confirmButton = { - DialogButton(text = stringResource(R.string.cancel)) { - onDismissRequest.invoke() - } - }, - title = { - Text(stringResource(R.string.pick_contact)) - }, - text = { - LazyColumn { - item { - var numberInput by remember { - mutableStateOf("") + properties = remember { DialogProperties(usePlatformDefaultWidth = false) }) { + Surface(color = MaterialTheme.colorScheme.surface) { + Column( + modifier = Modifier + .fillMaxSize() + .padding(horizontal = 8.dp) + ) { + var filteredContacts by remember { + mutableStateOf(contactsModel.contacts.flatMap { + it.numbers.map { num -> + ContactSingleDataItem( + name = it.getNameBySortOrder(SortOrder.FIRSTNAME).orEmpty(), + data = num.value, + thumbnail = it.thumbnail, + contactData = it + ) + } + }) + } + var searchQuery by remember { + mutableStateOf("") + } + TextField( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 10.dp), + value = searchQuery, + onValueChange = { + searchQuery = it + val lowerQuery = searchQuery.lowercase() + filteredContacts = contactsModel.contacts.flatMap { + it.numbers.map { num -> + ContactSingleDataItem( + name = it.getNameBySortOrder(SortOrder.FIRSTNAME).orEmpty(), + data = num.value, + thumbnail = it.thumbnail, + contactData = it + ) + } + }.filter { item -> + item.name.lowercase() + .contains(lowerQuery) || item.data.lowercase().contains(lowerQuery) + } + }, + placeholder = { Text(stringResource(R.string.search_contacts)) }, + colors = TextFieldDefaults.colors( + focusedContainerColor = MaterialTheme.colorScheme.secondaryContainer, + unfocusedContainerColor = MaterialTheme.colorScheme.secondaryContainer, + focusedTextColor = MaterialTheme.colorScheme.onSecondaryContainer, + unfocusedTextColor = MaterialTheme.colorScheme.onSecondaryContainer, + focusedIndicatorColor = Color.Transparent, + unfocusedIndicatorColor = Color.Transparent + ), + shape = RoundedCornerShape(50), + leadingIcon = { + IconButton(onClick = onDismissRequest) { + Icon( + imageVector = Icons.AutoMirrored.Rounded.ArrowBackIos, + contentDescription = null + ) + } } - Row( - modifier = Modifier.fillMaxWidth(), - verticalAlignment = Alignment.CenterVertically - ) { - OutlinedTextField( - modifier = Modifier.weight(1f), - value = numberInput, - onValueChange = { numberInput = it }, - label = { - Text(stringResource(R.string.phone_number)) + ) + + LazyColumn(Modifier.weight(1f), contentPadding = PaddingValues(horizontal = 8.dp)) { + if (searchQuery.length > 2) { + item { + NewNumberCard( + number = searchQuery, + onClick = { + onNumberSelect.invoke(searchQuery, null) + } + ) + Spacer(modifier = Modifier.height(10.dp)) + } + } + items(filteredContacts) { + ContactCard( + name = it.name, + number = it.data, + thumbnail = it.thumbnail, + themeModel = themeModel, + onClick = { + onNumberSelect.invoke(it.data, it.contactData) } ) - ClickableIcon( - modifier = Modifier.padding(top = 3.dp), - icon = Icons.Default.Send - ) { - onNumberSelect.invoke(numberInput, null) - } } - Spacer(modifier = Modifier.height(10.dp)) } - items(contactsModel.contacts.filter { it.numbers.isNotEmpty() }) { - ContactItem( - contact = it, - sortOrder = SortOrder.FIRSTNAME, - selected = false, - onSinglePress = { - if (it.numbers.size > 1) { - numbersToSelectFrom = it to it.numbers.map { num -> num.value } - return@ContactItem true - } + } + } + } +} - onNumberSelect.invoke(it.numbers.first().value, it) - onDismissRequest.invoke() - true - }, - onLongPress = {} +@Composable +private fun ContactCard( + name: String, + number: String, + thumbnail: Any?, + themeModel: ThemeModel, + onClick: () -> Unit = {} +) { + Box( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 6.dp) + .clip(RoundedCornerShape(20.dp)) + .background(MaterialTheme.colorScheme.surfaceColorAtElevation(1.dp)) + .clickable { + onClick.invoke() + } + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 8.dp, vertical = 16.dp), + horizontalArrangement = Arrangement.spacedBy(8.dp), + verticalAlignment = Alignment.CenterVertically + ) { + if (thumbnail != null) { + Box( + modifier = Modifier + .size(42.dp) + .background( + shape = CircleShape, + color = MaterialTheme.colorScheme.primary + ) + ) { + AsyncImage( + model = thumbnail, + modifier = Modifier + .fillMaxSize() + .clip(CircleShape), + contentDescription = null, + contentScale = ContentScale.Crop ) } + } else { + ContactIconPlaceholder( + themeModel = themeModel, + firstChar = name.firstOrNull(), + ) + } + Column(Modifier.weight(1f)) { + Text( + text = name, + color = MaterialTheme.colorScheme.primary, + style = MaterialTheme.typography.titleMedium, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + Text( + text = number, + color = MaterialTheme.colorScheme.secondary, + style = MaterialTheme.typography.bodyMedium, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) } } - ) + } +} - if (numbersToSelectFrom.second.isNotEmpty()) { - AlertDialog( - onDismissRequest = { numbersToSelectFrom = null to emptyList() }, - text = { - LazyColumn { - items(numbersToSelectFrom.second) { - ClickableText(text = it) { - onNumberSelect.invoke(it, numbersToSelectFrom.first) - numbersToSelectFrom = null to emptyList() - onDismissRequest.invoke() - } - } - } - }, - confirmButton = { - DialogButton(stringResource(R.string.cancel)) { - numbersToSelectFrom = null to emptyList() - } +@Composable +private fun NewNumberCard( + number: String, + onClick: () -> Unit = {} +) { + Box( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 6.dp) + .clip(RoundedCornerShape(20.dp)) + .background(MaterialTheme.colorScheme.surfaceColorAtElevation(1.dp)) + .clickable { + onClick.invoke() } - ) + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 8.dp, vertical = 16.dp), + horizontalArrangement = Arrangement.spacedBy(8.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Box( + modifier = Modifier + .size(42.dp) + .background( + shape = CircleShape, + color = MaterialTheme.colorScheme.surfaceVariant + ) + ) { + Icon( + modifier = Modifier + .fillMaxSize() + .padding(8.dp), + imageVector = Icons.Rounded.AddComment, + contentDescription = null, + tint = MaterialTheme.colorScheme.onSurfaceVariant + ) + } + Column(Modifier.weight(1f)) { + Text( + text = stringResource(R.string.start_a_new_conversation), + color = MaterialTheme.colorScheme.primary, + style = MaterialTheme.typography.titleMedium, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + Text( + text = number, + color = MaterialTheme.colorScheme.secondary, + style = MaterialTheme.typography.bodyMedium, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + } + } } -} +} \ No newline at end of file diff --git a/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/SmsListScreen.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/SmsListScreen.kt index d4953fc9..f3597f6d 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/SmsListScreen.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/SmsListScreen.kt @@ -50,6 +50,7 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp +import androidx.lifecycle.viewmodel.compose.viewModel import com.bnyro.contacts.R import com.bnyro.contacts.domain.model.ContactData import com.bnyro.contacts.domain.model.SmsThread @@ -60,6 +61,7 @@ import com.bnyro.contacts.presentation.components.TopBarMoreMenu import com.bnyro.contacts.presentation.features.NumberPickerDialog import com.bnyro.contacts.presentation.screens.calllog.SheetSettingItem import com.bnyro.contacts.presentation.screens.contacts.model.ContactsModel +import com.bnyro.contacts.presentation.screens.settings.model.ThemeModel import com.bnyro.contacts.presentation.screens.sms.components.SmsSearchScreen import com.bnyro.contacts.presentation.screens.sms.components.SmsThreadItem import com.bnyro.contacts.presentation.screens.sms.model.SmsModel @@ -74,6 +76,7 @@ fun SmsListScreen( onNavigate: (NavRoutes) -> Unit, onClickMessage: (address: String, contactData: ContactData?) -> Unit ) { + val themeModel: ThemeModel = viewModel() var showNumberPicker by remember { mutableStateOf(false) } @@ -163,6 +166,7 @@ fun SmsListScreen( if (showNumberPicker) { NumberPickerDialog( contactsModel, + themeModel, onDismissRequest = { showNumberPicker = false }, onNumberSelect = onClickMessage ) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3cd3eeba..a13222df 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -157,4 +157,6 @@ Number blocked Block number Unblock number + Search contacts + Start a new conversation \ No newline at end of file From 93ebca8d8e67772752b56036ddaf4db345de7a0c Mon Sep 17 00:00:00 2001 From: Bnyro Date: Mon, 27 May 2024 19:18:51 +0200 Subject: [PATCH 046/159] fix: automatically scroll to most recent message (closes #396) --- .idea/misc.xml | 1 - .../screens/sms/SmsThreadScreen.kt | 4 +--- .../screens/sms/components/Message.kt | 24 ++++++++++--------- 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/.idea/misc.xml b/.idea/misc.xml index 2998c5e1..f0dff404 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,4 +1,3 @@ - diff --git a/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/SmsThreadScreen.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/SmsThreadScreen.kt index 9ce417ab..a1fa4a0a 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/SmsThreadScreen.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/SmsThreadScreen.kt @@ -13,7 +13,6 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width -import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ArrowBack import androidx.compose.material.icons.filled.PersonAddAlt1 @@ -135,8 +134,7 @@ fun SmsThreadScreen( modifier = Modifier .padding(pV) ) { - val state = rememberLazyListState() - Messages(messages = smsList, scrollState = state, smsModel = smsModel) + Messages(messages = smsList, smsModel = smsModel) Spacer(modifier = Modifier.height(10.dp)) if (subscriptions != null && subscriptions.size >= 2) { diff --git a/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/components/Message.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/components/Message.kt index ebac696d..26a2b652 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/components/Message.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/components/Message.kt @@ -13,8 +13,8 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.ClickableText import androidx.compose.foundation.text.selection.SelectionContainer @@ -48,13 +48,17 @@ import com.bnyro.contacts.util.generateAnnotations @OptIn(ExperimentalMaterial3Api::class) @Composable -fun ColumnScope.Messages( - messages: List, - scrollState: LazyListState, - smsModel: SmsModel -) { - LaunchedEffect(Unit) { - scrollState.scrollToItem(messages.size + 5) +fun ColumnScope.Messages(messages: List, smsModel: SmsModel) { + val scrollState = rememberLazyListState() + + var scrolledToBottom = remember { false } + + LaunchedEffect(messages) { + // only scroll to the latest message once and only if not manually scrolled yet + if (scrollState.firstVisibleItemIndex == 0 && scrollState.firstVisibleItemScrollOffset == 0 && !scrolledToBottom) { + scrollState.scrollToItem(messages.size + 5) + scrolledToBottom = true + } } val timestamped = messages.groupBy { @@ -75,9 +79,7 @@ fun ColumnScope.Messages( ) { timestamped.forEach { timestamp -> item { - DayHeader( - timestamp.key - ) + DayHeader(timestamp.key) } items(items = timestamp.value) { smsData -> val isUserMe = smsData.type in listOf( From 48068954b002333084cc35c9b4ea4d2d322accdf Mon Sep 17 00:00:00 2001 From: Bnyro Date: Mon, 27 May 2024 19:26:43 +0200 Subject: [PATCH 047/159] fix: crashes when checking permission called with applicationContext --- .../domain/repositories/CallLogRepository.kt | 14 ++++------- .../screens/calllog/model/CallModel.kt | 2 +- .../contacts/ui/activities/BaseActivity.kt | 8 +++---- .../bnyro/contacts/util/PermissionHelper.kt | 24 +++++++++---------- .../contacts/util/receivers/SmsReceiver.kt | 9 +++---- 5 files changed, 24 insertions(+), 33 deletions(-) diff --git a/app/src/main/java/com/bnyro/contacts/domain/repositories/CallLogRepository.kt b/app/src/main/java/com/bnyro/contacts/domain/repositories/CallLogRepository.kt index 751517e9..b1ba9914 100644 --- a/app/src/main/java/com/bnyro/contacts/domain/repositories/CallLogRepository.kt +++ b/app/src/main/java/com/bnyro/contacts/domain/repositories/CallLogRepository.kt @@ -13,11 +13,8 @@ import kotlinx.coroutines.withContext class CallLogRepository(private val context: Context) { suspend fun getCallLog(): List = withContext(Dispatchers.IO) { - if (!PermissionHelper.checkPermissions( - context, - arrayOf(Manifest.permission.READ_CALL_LOG) - ) - ) return@withContext emptyList() + if (!PermissionHelper.hasPermission(context, Manifest.permission.READ_CALL_LOG)) + return@withContext emptyList() val callLog = mutableListOf() @@ -47,11 +44,8 @@ class CallLogRepository(private val context: Context) { } suspend fun deleteAll(callLog: List) = withContext(Dispatchers.IO) { - if (!PermissionHelper.checkPermissions( - context, - arrayOf(Manifest.permission.WRITE_CALL_LOG) - ) - ) return@withContext + if (!PermissionHelper.hasPermission(context, Manifest.permission.WRITE_CALL_LOG)) + return@withContext callLog.distinctBy { it.phoneNumber }.forEach { entry -> context.contentResolver.delete( diff --git a/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/model/CallModel.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/model/CallModel.kt index a8b9e4a8..2b2dee09 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/model/CallModel.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/model/CallModel.kt @@ -105,7 +105,7 @@ class CallModel(private val application: Application, savedStateHandle: SavedSta } fun callNumber(number: String = numberToCall) { - if (!PermissionHelper.checkPermissions(application.applicationContext, phonePerms)) return + if (!PermissionHelper.hasPermission(application.applicationContext, *phonePerms)) return val intent = Intent(Intent.ACTION_CALL, Uri.parse("tel:$number")).apply { flags = Intent.FLAG_ACTIVITY_NEW_TASK diff --git a/app/src/main/java/com/bnyro/contacts/ui/activities/BaseActivity.kt b/app/src/main/java/com/bnyro/contacts/ui/activities/BaseActivity.kt index 00a115ff..805e548e 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/activities/BaseActivity.kt +++ b/app/src/main/java/com/bnyro/contacts/ui/activities/BaseActivity.kt @@ -43,7 +43,7 @@ abstract class BaseActivity : FragmentActivity() { val roleManager = context.getSystemService(RoleManager::class.java) if (roleManager!!.isRoleAvailable(RoleManager.ROLE_SMS)) { if (roleManager.isRoleHeld(RoleManager.ROLE_SMS)) { - getSmsPermissions(context) + getSmsPermissions() } else { val intent = roleManager.createRequestRoleIntent(RoleManager.ROLE_SMS) context.startActivity(intent) @@ -51,7 +51,7 @@ abstract class BaseActivity : FragmentActivity() { } } else { if (Telephony.Sms.getDefaultSmsPackage(context) == context.packageName) { - getSmsPermissions(context) + getSmsPermissions() } else { val intent = Intent(Telephony.Sms.Intents.ACTION_CHANGE_DEFAULT) intent.putExtra(Telephony.Sms.Intents.EXTRA_PACKAGE_NAME, context.packageName) @@ -60,8 +60,8 @@ abstract class BaseActivity : FragmentActivity() { } } - private fun getSmsPermissions(context: Context) { - PermissionHelper.checkPermissions(context, smsPermissions) + private fun getSmsPermissions() { + PermissionHelper.checkPermissions(this, smsPermissions) } companion object { diff --git a/app/src/main/java/com/bnyro/contacts/util/PermissionHelper.kt b/app/src/main/java/com/bnyro/contacts/util/PermissionHelper.kt index c6b05d62..05dc952a 100644 --- a/app/src/main/java/com/bnyro/contacts/util/PermissionHelper.kt +++ b/app/src/main/java/com/bnyro/contacts/util/PermissionHelper.kt @@ -6,23 +6,23 @@ import android.content.pm.PackageManager import androidx.core.app.ActivityCompat object PermissionHelper { - fun checkPermissions(context: Context, permissions: Array): Boolean { + fun checkPermissions(activity: Activity, permissions: Array): Boolean { if (permissions.isEmpty()) return true - if (!hasPermission(context, permissions.first())) { - ActivityCompat.requestPermissions( - context as Activity, - permissions, - 1 - ) + + if (!hasPermission(activity, *permissions)) { + ActivityCompat.requestPermissions(activity, permissions, 1) return false } + return true } - fun hasPermission(context: Context, permission: String): Boolean { - return ActivityCompat.checkSelfPermission( - context, - permission - ) == PackageManager.PERMISSION_GRANTED + fun hasPermission(context: Context, vararg permissions: String): Boolean { + return permissions.all { permission -> + ActivityCompat.checkSelfPermission( + context, + permission + ) == PackageManager.PERMISSION_GRANTED + } } } diff --git a/app/src/main/java/com/bnyro/contacts/util/receivers/SmsReceiver.kt b/app/src/main/java/com/bnyro/contacts/util/receivers/SmsReceiver.kt index e2d29c47..e1be50fa 100644 --- a/app/src/main/java/com/bnyro/contacts/util/receivers/SmsReceiver.kt +++ b/app/src/main/java/com/bnyro/contacts/util/receivers/SmsReceiver.kt @@ -140,13 +140,10 @@ class SmsReceiver : BroadcastReceiver() { builder.addAction(copyMessageAction) } - if (!PermissionHelper.checkPermissions( - context, - NotificationHelper.notificationPermissions - ) - ) { - return + NotificationHelper.notificationPermissions.firstOrNull()?.let { + if (!PermissionHelper.hasPermission(context, it)) return } + NotificationManagerCompat.from(context).notify(notificationId, builder.build()) } From bb82b6e1f6accf5bb7197d0dca2e96ee0a271a91 Mon Sep 17 00:00:00 2001 From: Bnyro Date: Mon, 27 May 2024 19:29:47 +0200 Subject: [PATCH 048/159] chore: fix some linter warnings --- .../presentation/screens/about/AboutScreen.kt | 4 ++-- .../presentation/screens/calllog/model/CallModel.kt | 11 +++++------ .../presentation/screens/settings/SettingsScreen.kt | 8 ++++---- .../presentation/screens/sms/components/Message.kt | 4 ++-- 4 files changed, 13 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/com/bnyro/contacts/presentation/screens/about/AboutScreen.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/about/AboutScreen.kt index 5b1a8abe..17081235 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/screens/about/AboutScreen.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/about/AboutScreen.kt @@ -8,7 +8,7 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ArrowBack -import androidx.compose.material3.Divider +import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Scaffold import androidx.compose.material3.Text @@ -55,7 +55,7 @@ fun AboutScreen(onBackPress: () -> Unit) { painter = painterResource(R.drawable.ic_launcher_foreground), contentDescription = null ) - Divider( + HorizontalDivider( modifier = Modifier .fillMaxWidth() .height(2.dp) diff --git a/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/model/CallModel.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/model/CallModel.kt index 2b2dee09..e8e17567 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/model/CallModel.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/model/CallModel.kt @@ -34,8 +34,8 @@ import kotlinx.coroutines.withContext class CallModel(private val application: Application, savedStateHandle: SavedStateHandle) : AndroidViewModel(application) { - val callLogRepository = (application as App).callLogRepository - val phoneLookupRepository = (application as App).phoneLookupRepository + private val callLogRepository = (application as App).callLogRepository + private val phoneLookupRepository = (application as App).phoneLookupRepository val initialPhoneNumber = savedStateHandle.get(HomeRoutes.Phone.phoneNumber) @@ -61,8 +61,7 @@ class CallModel(private val application: Application, savedStateHandle: SavedSta val subscriptions = SmsUtil.getSubscriptions(application.applicationContext) - var chosenSubInfo = - subscriptions.firstOrNull() + private var chosenSubInfo = subscriptions.firstOrNull() init { @@ -75,7 +74,7 @@ class CallModel(private val application: Application, savedStateHandle: SavedSta } fun onSubscriptionIndexChange(index: Int) { - chosenSubInfo = subscriptions.get(index) + chosenSubInfo = subscriptions[index] } fun onNumberInput(digit: String) { @@ -137,7 +136,7 @@ class CallModel(private val application: Application, savedStateHandle: SavedSta } } - fun playToneForDigit(digit: String) { + private fun playToneForDigit(digit: String) { val numericDigit = when (digit) { "0" -> ToneGenerator.TONE_DTMF_0 "1" -> ToneGenerator.TONE_DTMF_1 diff --git a/app/src/main/java/com/bnyro/contacts/presentation/screens/settings/SettingsScreen.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/settings/SettingsScreen.kt index 70509b1d..4fa63170 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/screens/settings/SettingsScreen.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/settings/SettingsScreen.kt @@ -7,7 +7,7 @@ import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ArrowBack -import androidx.compose.material3.Divider +import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.LargeTopAppBar import androidx.compose.material3.MaterialTheme @@ -96,7 +96,7 @@ fun SettingsScreen(themeModel: ThemeModel, smsModel: SmsModel, onBackPress: () - ) { themeModel.colorfulIcons = it } - Divider( + HorizontalDivider( modifier = Modifier.padding(top = 12.dp, bottom = 8.dp), color = MaterialTheme.colorScheme.surfaceVariant ) @@ -108,7 +108,7 @@ fun SettingsScreen(themeModel: ThemeModel, smsModel: SmsModel, onBackPress: () - ) { smsModel.refreshLocalSmsPreference() } - Divider( + HorizontalDivider( modifier = Modifier.padding(top = 12.dp, bottom = 8.dp), color = MaterialTheme.colorScheme.surfaceVariant ) @@ -126,7 +126,7 @@ fun SettingsScreen(themeModel: ThemeModel, smsModel: SmsModel, onBackPress: () - ) { themeModel.collapsableBottomBar = it } - Divider( + HorizontalDivider( modifier = Modifier.padding(top = 12.dp, bottom = 8.dp), color = MaterialTheme.colorScheme.surfaceVariant ) diff --git a/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/components/Message.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/components/Message.kt index 26a2b652..e37ee2f6 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/components/Message.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/components/Message.kt @@ -18,7 +18,7 @@ import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.ClickableText import androidx.compose.foundation.text.selection.SelectionContainer -import androidx.compose.material3.Divider +import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.LocalContentColor import androidx.compose.material3.MaterialTheme @@ -158,7 +158,7 @@ fun DayHeader(dayString: String) { @Composable private fun RowScope.DayHeaderLine() { - Divider( + HorizontalDivider( modifier = Modifier .weight(1f) .align(Alignment.CenterVertically), From 1f34153bf65b3aff41eaa7f24da559f3ef766589 Mon Sep 17 00:00:00 2001 From: Bnyro Date: Sat, 1 Jun 2024 15:21:34 +0200 Subject: [PATCH 049/159] fix: crash when vcard exporter fails to parse dates (closes #399) --- app/src/main/java/com/bnyro/contacts/util/CalendarUtils.kt | 3 ++- app/src/main/java/com/bnyro/contacts/util/VcardHelper.kt | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/bnyro/contacts/util/CalendarUtils.kt b/app/src/main/java/com/bnyro/contacts/util/CalendarUtils.kt index c5ce5209..2ec82731 100644 --- a/app/src/main/java/com/bnyro/contacts/util/CalendarUtils.kt +++ b/app/src/main/java/com/bnyro/contacts/util/CalendarUtils.kt @@ -20,10 +20,11 @@ object CalendarUtils { val calendar = Calendar.getInstance() calendar.clear(Calendar.ZONE_OFFSET) calendar.timeInMillis = milliSeconds + return formatter.format(calendar.time) } - fun dateToMillis(date: String) = isoDateFormat.parse(date)?.time + fun dateToMillis(date: String) = kotlin.runCatching { isoDateFormat.parse(date) }.getOrNull()?.time fun localizeIsoDate(isoDate: String): String { val isDateWithoutYear = isoDate.startsWith('-') diff --git a/app/src/main/java/com/bnyro/contacts/util/VcardHelper.kt b/app/src/main/java/com/bnyro/contacts/util/VcardHelper.kt index d2209ab9..65ac0ea0 100644 --- a/app/src/main/java/com/bnyro/contacts/util/VcardHelper.kt +++ b/app/src/main/java/com/bnyro/contacts/util/VcardHelper.kt @@ -74,7 +74,9 @@ object VcardHelper { addNote(note.value) } } - contact.events.forEach { event -> + contact.events + .filter { it.value.isNotBlank() } + .forEach { event -> when (event.type) { ContactsContract.CommonDataKinds.Event.TYPE_BIRTHDAY -> { CalendarUtils.dateToMillis(event.value)?.let { time -> From f7e22ce46c39b9fe9195d9bea13c8c9f36b3c76b Mon Sep 17 00:00:00 2001 From: Suhas Dissanayake Date: Sat, 8 Jun 2024 12:51:29 +0530 Subject: [PATCH 050/159] feat: show call disconnect status --- .../contacts/ui/activities/CallActivity.kt | 22 ++++++++++++++++++- .../contacts/util/services/CallService.kt | 4 ++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/bnyro/contacts/ui/activities/CallActivity.kt b/app/src/main/java/com/bnyro/contacts/ui/activities/CallActivity.kt index bf83618a..04de8a86 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/activities/CallActivity.kt +++ b/app/src/main/java/com/bnyro/contacts/ui/activities/CallActivity.kt @@ -1,5 +1,6 @@ package com.bnyro.contacts.ui.activities +import android.app.AlertDialog import android.content.BroadcastReceiver import android.content.ComponentName import android.content.Context @@ -22,6 +23,7 @@ import androidx.compose.ui.Modifier import androidx.core.content.ContextCompat import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.get +import com.bnyro.contacts.R import com.bnyro.contacts.presentation.screens.dialer.DialerScreen import com.bnyro.contacts.presentation.screens.dialer.model.DialerModel import com.bnyro.contacts.ui.theme.ConnectYouTheme @@ -70,7 +72,12 @@ class CallActivity : ComponentActivity() { private val closeAlertReciever = object : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { if (intent?.getStringExtra(ACTION_EXTRA_KEY) == CLOSE_ACTION) { - finish() + val reason = intent.getStringExtra(ACTION_EXTRA_REASON) + if (reason != null) { + showAlert(getString(R.string.call_ended), reason) + } else { + finish() + } } } } @@ -138,10 +145,23 @@ class CallActivity : ComponentActivity() { unbindService(serviceConnection) } + fun showAlert(title: String, message: String) { + AlertDialog.Builder(this) + .setTitle(title) + .setMessage(message) + .setPositiveButton(android.R.string.ok) { dialog, _ -> + dialog.dismiss() + finish() + } + .create() + .show() + } + companion object { const val CALL_ALERT_CLOSE_ACTION = "com.bnyro.contacts.ALARM_ALERT_CLOSE_ACTION" const val ACTION_EXTRA_KEY = "action" const val CLOSE_ACTION = "CLOSE" + const val ACTION_EXTRA_REASON = "reason" private const val windowFlags = WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD or WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON or WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON } diff --git a/app/src/main/java/com/bnyro/contacts/util/services/CallService.kt b/app/src/main/java/com/bnyro/contacts/util/services/CallService.kt index 2b9118aa..a437db8e 100644 --- a/app/src/main/java/com/bnyro/contacts/util/services/CallService.kt +++ b/app/src/main/java/com/bnyro/contacts/util/services/CallService.kt @@ -78,6 +78,10 @@ class CallService : InCallService() { if (state == Call.STATE_DISCONNECTED) { val closeCallAlertIntent = Intent(CallActivity.CALL_ALERT_CLOSE_ACTION).apply { putExtra(CallActivity.ACTION_EXTRA_KEY, CallActivity.CLOSE_ACTION) + putExtra(CallActivity.ACTION_EXTRA_REASON, + call.details?.disconnectCause?.description?.takeIf { it.isNotBlank() } + ?.toString() + ) `package` = packageName } sendBroadcast(closeCallAlertIntent) From d5654cbb02f5305690250ede3001df372ff71edd Mon Sep 17 00:00:00 2001 From: Bnyro Date: Wed, 12 Jun 2024 23:28:44 +0200 Subject: [PATCH 051/159] refactor: introduce keys for lazy columns to avoid issues --- .../bnyro/contacts/presentation/features/AddToContactDialog.kt | 3 ++- .../com/bnyro/contacts/presentation/features/GroupsDialog.kt | 2 +- .../contacts/presentation/screens/calllog/CallLogsScreen.kt | 2 +- .../presentation/screens/calllog/components/NumberInput.kt | 2 +- .../presentation/screens/contacts/components/ContactsList.kt | 2 +- .../bnyro/contacts/presentation/screens/sms/SmsListScreen.kt | 2 +- .../contacts/presentation/screens/sms/components/Message.kt | 2 +- .../presentation/screens/sms/components/SmsSearchScreen.kt | 2 +- .../com/bnyro/contacts/ui/activities/PickContactActivity.kt | 2 +- 9 files changed, 10 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/com/bnyro/contacts/presentation/features/AddToContactDialog.kt b/app/src/main/java/com/bnyro/contacts/presentation/features/AddToContactDialog.kt index 5adb20c7..55317e19 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/features/AddToContactDialog.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/features/AddToContactDialog.kt @@ -88,7 +88,8 @@ fun AddToContactDialog(newNumber: String) { it.displayName.orEmpty().lowercase().contains( searchQuery.lowercase() ) - } + }, + key = ContactData::contactId ) { ClickableText(text = it.displayName.orEmpty()) { scope.launch { diff --git a/app/src/main/java/com/bnyro/contacts/presentation/features/GroupsDialog.kt b/app/src/main/java/com/bnyro/contacts/presentation/features/GroupsDialog.kt index 7672d1da..5414a660 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/features/GroupsDialog.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/features/GroupsDialog.kt @@ -72,7 +72,7 @@ fun GroupsDialog( LazyColumn( modifier = Modifier.height(300.dp) ) { - items(contactGroups) { group -> + items(contactGroups, key = ContactsGroup::rowId) { group -> Row( verticalAlignment = Alignment.CenterVertically ) { diff --git a/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/CallLogsScreen.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/CallLogsScreen.kt index 15cf63ab..a567ab95 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/CallLogsScreen.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/CallLogsScreen.kt @@ -149,7 +149,7 @@ fun CallLogsScreen( stickyHeader { CharacterHeader(text = time) } - items(callLogs) { callLog -> + items(callLogs, key = { callLog -> "${callLog.time} ${callLog.phoneNumber}"}) { callLog -> val contact = remember { contactsModel.getContactByNumber(callLog.phoneNumber) } diff --git a/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/components/NumberInput.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/components/NumberInput.kt index 173b6e43..ee23b3a3 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/components/NumberInput.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/components/NumberInput.kt @@ -312,7 +312,7 @@ fun ColumnScope.PhoneNumberDisplay( .weight(1f), verticalArrangement = Arrangement.spacedBy(8.dp, alignment = Alignment.Bottom) ) { - items(contacts, key = { it.number }) { contact -> + items(contacts, key = BasicContactData::number) { contact -> ContactSuggestionItem(onClickContact, contact) } } diff --git a/app/src/main/java/com/bnyro/contacts/presentation/screens/contacts/components/ContactsList.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/contacts/components/ContactsList.kt index 450a0b3e..8ce85e76 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/screens/contacts/components/ContactsList.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/contacts/components/ContactsList.kt @@ -63,7 +63,7 @@ fun ContactsList( stickyHeader { CharacterHeader(firstLetter.orEmpty()) } - items(groupedContacts) { + items(groupedContacts, key = ContactData::contactId) { ContactItem( modifier = Modifier.padding(horizontal = 10.dp), contact = it, diff --git a/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/SmsListScreen.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/SmsListScreen.kt index f3597f6d..d19d9c7d 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/SmsListScreen.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/SmsListScreen.kt @@ -150,7 +150,7 @@ fun SmsListScreen( scrollConnection?.let { modifier.nestedScroll(it) } ?: modifier } ) { - items(threadList) { thread -> + items(threadList, key = SmsThread::threadId) { thread -> SmsThreadItem(smsModel, thread, onClick = onClickMessage, onLongClick = { selectedThread = thread }) diff --git a/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/components/Message.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/components/Message.kt index e37ee2f6..f1f5b0d8 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/components/Message.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/components/Message.kt @@ -81,7 +81,7 @@ fun ColumnScope.Messages(messages: List, smsModel: SmsModel) { item { DayHeader(timestamp.key) } - items(items = timestamp.value) { smsData -> + items(items = timestamp.value, key = SmsData::id) { smsData -> val isUserMe = smsData.type in listOf( Telephony.Sms.MESSAGE_TYPE_DRAFT, Telephony.Sms.MESSAGE_TYPE_SENT, diff --git a/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/components/SmsSearchScreen.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/components/SmsSearchScreen.kt index 33eb88d0..589ae338 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/components/SmsSearchScreen.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/components/SmsSearchScreen.kt @@ -84,7 +84,7 @@ fun SmsSearchScreen( modifier = Modifier .fillMaxSize() ) { - items(visibleThreads) { thread -> + items(visibleThreads, key = SmsThread::threadId) { thread -> SmsThreadItem(smsModel, thread, onClickMessage) } } diff --git a/app/src/main/java/com/bnyro/contacts/ui/activities/PickContactActivity.kt b/app/src/main/java/com/bnyro/contacts/ui/activities/PickContactActivity.kt index bf2abe70..1dae3654 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/activities/PickContactActivity.kt +++ b/app/src/main/java/com/bnyro/contacts/ui/activities/PickContactActivity.kt @@ -72,7 +72,7 @@ class PickContactActivity : BaseActivity() { LazyColumn( modifier = Modifier.weight(1f) ) { - items(contactsModel.contacts) { + items(contactsModel.contacts, key = ContactData::contactId) { ContactItem( modifier = Modifier.padding(horizontal = 10.dp), contact = it, From 8ca2f32630ca007142a5c83b5a628fa572a3eaf1 Mon Sep 17 00:00:00 2001 From: Bnyro Date: Wed, 12 Jun 2024 23:37:54 +0200 Subject: [PATCH 052/159] feat: normalize query in contacts search (closes #398) --- .../com/bnyro/contacts/util/ContactsHelper.kt | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/bnyro/contacts/util/ContactsHelper.kt b/app/src/main/java/com/bnyro/contacts/util/ContactsHelper.kt index c667340a..eefba4e6 100644 --- a/app/src/main/java/com/bnyro/contacts/util/ContactsHelper.kt +++ b/app/src/main/java/com/bnyro/contacts/util/ContactsHelper.kt @@ -21,8 +21,11 @@ import com.google.i18n.phonenumbers.PhoneNumberUtil import ezvcard.parameter.AddressType import ezvcard.parameter.EmailType import ezvcard.parameter.TelephoneType +import java.text.Normalizer object ContactsHelper { + private val REGEX_NORMALIZE = "\\p{InCombiningDiacriticalMarks}+".toRegex() + val emailTypes = listOf( TranslatedType( ContactsContract.CommonDataKinds.Email.TYPE_HOME, @@ -150,6 +153,12 @@ object ContactsHelper { } } + private fun normalizeText(text: String): String { + return Normalizer.normalize(text, Normalizer.Form.NFD) + .replace(REGEX_NORMALIZE, "") + .lowercase() + } + fun normalizePhoneNumber(number: String): String { val phoneUtil = PhoneNumberUtil.getInstance() val phoneNumber = runCatching { phoneUtil.parse(number, null) } @@ -158,6 +167,8 @@ object ContactsHelper { } fun matches(contactData: ContactData, query: String): Boolean { + val normalizedQuery = normalizeText(query) + val contactInfoStrings = listOf( contactData.numbers, contactData.emails, @@ -174,15 +185,12 @@ object ContactsHelper { ) return contactInfoStrings.filterNotNull().any { str -> - str.lowercase().contains(query) + normalizeText(str).contains(normalizedQuery) } } - fun filter(contacts: List, searchQuery: String): List { - val query = searchQuery.lowercase() - - return contacts.filter { matches(it, query) } - } + fun filter(contacts: List, searchQuery: String): List = + contacts.filter { matches(it, searchQuery) } fun isContactEmpty(contactData: ContactData): Boolean { val stringProperties = listOf( From b66569b15b71cdea190b661078bd6fa9aac0209c Mon Sep 17 00:00:00 2001 From: Bnyro Date: Wed, 12 Jun 2024 23:41:30 +0200 Subject: [PATCH 053/159] fix: normalize phone numbers for received sms --- .../com/bnyro/contacts/domain/repositories/DeviceSmsRepo.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/bnyro/contacts/domain/repositories/DeviceSmsRepo.kt b/app/src/main/java/com/bnyro/contacts/domain/repositories/DeviceSmsRepo.kt index 9b6931e0..d60334c6 100644 --- a/app/src/main/java/com/bnyro/contacts/domain/repositories/DeviceSmsRepo.kt +++ b/app/src/main/java/com/bnyro/contacts/domain/repositories/DeviceSmsRepo.kt @@ -80,7 +80,7 @@ class DeviceSmsRepo : SmsRepository { override suspend fun persistSms(context: Context, smsData: SmsData) { val values = ContentValues() - values.put(Telephony.Sms.ADDRESS, smsData.address) + values.put(Telephony.Sms.ADDRESS, ContactsHelper.normalizePhoneNumber(smsData.address)) values.put(Telephony.Sms.BODY, smsData.body) values.put(Telephony.Sms.DATE, smsData.timestamp) values.put(Telephony.Sms.READ, 1) From 0f2a90f04c53bdb1b43708f02a04625edce6939e Mon Sep 17 00:00:00 2001 From: Bnyro Date: Wed, 12 Jun 2024 23:50:18 +0200 Subject: [PATCH 054/159] perf: optimize searching threadIds for local sms --- .../java/com/bnyro/contacts/data/database/dao/LocalSmsDao.kt | 4 ++-- .../com/bnyro/contacts/domain/repositories/LocalSmsRepo.kt | 4 +--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/bnyro/contacts/data/database/dao/LocalSmsDao.kt b/app/src/main/java/com/bnyro/contacts/data/database/dao/LocalSmsDao.kt index 1ce1e6be..e832bf4d 100644 --- a/app/src/main/java/com/bnyro/contacts/data/database/dao/LocalSmsDao.kt +++ b/app/src/main/java/com/bnyro/contacts/data/database/dao/LocalSmsDao.kt @@ -20,6 +20,6 @@ interface LocalSmsDao { @Insert suspend fun createSms(smsData: SmsData): Long - @Query("SELECT * FROM localSms WHERE address = :address") - suspend fun getSmsByAddress(address: String): List + @Query("SELECT threadId FROM localSms WHERE address = :address LIMIT 1") + suspend fun getThreadId(address: String): Long? } diff --git a/app/src/main/java/com/bnyro/contacts/domain/repositories/LocalSmsRepo.kt b/app/src/main/java/com/bnyro/contacts/domain/repositories/LocalSmsRepo.kt index 5239ad69..7fa65b31 100644 --- a/app/src/main/java/com/bnyro/contacts/domain/repositories/LocalSmsRepo.kt +++ b/app/src/main/java/com/bnyro/contacts/domain/repositories/LocalSmsRepo.kt @@ -17,9 +17,7 @@ class LocalSmsRepo : SmsRepository { override suspend fun getOrCreateThreadId(context: Context, address: String): Long { return DatabaseHolder.Db.localSmsDao() - .getSmsByAddress(ContactsHelper.normalizePhoneNumber(address)) - .firstOrNull() - ?.threadId + .getThreadId(ContactsHelper.normalizePhoneNumber(address)) ?: Random.nextLong() } From 6e6683e1109af69ef1fa3e328bc64d8c38976506 Mon Sep 17 00:00:00 2001 From: ssantos Date: Wed, 1 May 2024 20:07:12 +0200 Subject: [PATCH 055/159] Translated using Weblate (Portuguese) Currently translated at 100.0% (117 of 117 strings) Co-authored-by: ssantos Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/pt/ Translation: You Apps/Connect You --- app/src/main/res/values-pt/strings.xml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 4148c55b..56387a2f 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -107,4 +107,13 @@ Contactos A SMS será gravada apenas nesta aplicação e não no dispositivo. Número de telefone + Deseja importar o ficheiro VCF selecionado? + Adicionar nota + Adicionar evento + Data + Adicionar endereço + Adicionar site + Adicionar número de telefone + Adicionar E-mail + Quer enviar a mensagem como múltiplas mensagens pequenas? \ No newline at end of file From 6edbf81e1d09465587ab6a5222383bb1bbbbbf3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?O=C4=9Fuz=20Ersen?= Date: Wed, 1 May 2024 20:07:12 +0200 Subject: [PATCH 056/159] Translated using Weblate (Turkish) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently translated at 100.0% (137 of 137 strings) Translated using Weblate (Turkish) Currently translated at 100.0% (136 of 136 strings) Translated using Weblate (Turkish) Currently translated at 100.0% (133 of 133 strings) Translated using Weblate (Turkish) Currently translated at 100.0% (121 of 121 strings) Translated using Weblate (Turkish) Currently translated at 100.0% (118 of 118 strings) Co-authored-by: Oğuz Ersen Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/tr/ Translation: You Apps/Connect You --- app/src/main/res/values-tr/strings.xml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 54154197..e039c350 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -117,4 +117,24 @@ Etkinlik Ekle Tarih Seçilen vCard\'ı içe aktarmak istiyor musunuz? + Amoled + Güvenlik + Biyometrik Kimlik Doğrulaması + Uygulamaya erişilmeden önce biyometrik kimlik doğrulaması (örn. parmak izi) gerektir. + Bağlantı kesiliyor… + Bağlanıyor… + %1$s aramak istiyor musunuz? + Sesini kapat + Hoparlör + Kabul et + Tuş takımı + Aramalar + Reddet + Devam ediyor + Bilinmeyen Numara + Çalıyor… + Arama Sona Erdi + Aranıyor... + Arayan + Tuş takımı \ No newline at end of file From 0ee6a6629c4d5c3f58d81c823d800f97a3788adb Mon Sep 17 00:00:00 2001 From: Pascal Dietrich Date: Wed, 1 May 2024 20:07:13 +0200 Subject: [PATCH 057/159] Translated using Weblate (German) Currently translated at 97.7% (133 of 136 strings) Translated using Weblate (German) Currently translated at 97.7% (130 of 133 strings) Translated using Weblate (German) Currently translated at 100.0% (121 of 121 strings) Translated using Weblate (German) Currently translated at 100.0% (118 of 118 strings) Co-authored-by: Pascal Dietrich Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/de/ Translation: You Apps/Connect You --- app/src/main/res/values-de/strings.xml | 30 ++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 8b444d6e..d7fe8ee1 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -13,7 +13,7 @@ Mobil Andere Suchen - Bist du Sicher\? Es kann nicht Rückgängig gemacht werden. + Sind Sie Sicher? Dies kann nicht rückgängig gemacht werden. Abbrechen Erneut versuchen Kontakt löschen @@ -101,10 +101,36 @@ Kontakte Nachricht senden Nachrichten - Verbindungsfehler! Stellen sie sicher, dass der Flugmodus ausgeschaltet ist! + Verbindungsfehler! Stellen Sie sicher, dass der Flugmodus ausgeschaltet ist. Telefonnummer Unterhaltung löschen Nachricht löschen Private SMS Datenbank Speichern + Amoled + Die SMS wir nur in der App gespeichert und ist für andere Apps nicht zugänglich. + Möchten Sie die ausgewählte VCF-Datei importieren? + Website hinzufügen + Telefonnummer hinzufügen + Adresse hinzufügen + Notiz hinzufügen + Ereignis hinzufügen + Datum + E-Mail-Adresse hinzufügen + Möchten Sie die Nachricht als mehrere kleinere Nachrichten versenden? + Biometrische Authentifizierung (z.B. Fingerabdruck) verlangen, wenn die App geöffnet wird. + Biometrische Authentifizierung + Sicherheit + Möchten Sie %1$s anrufen? + Klingeln… + Lautsprecher + Verbinden… + Trennen… + Ablehnen + Annehmen + Anrufe + Unbekannte Nummer + Wählen… + Anruf von + Anruf beendet \ No newline at end of file From 278ef07c76681a02956a0ae9a8e95b3ad3281b79 Mon Sep 17 00:00:00 2001 From: Giovanni Donisi Date: Wed, 1 May 2024 20:07:13 +0200 Subject: [PATCH 058/159] Translated using Weblate (Italian) Currently translated at 100.0% (121 of 121 strings) Translated using Weblate (Italian) Currently translated at 100.0% (118 of 118 strings) Co-authored-by: Giovanni Donisi Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/it/ Translation: You Apps/Connect You --- app/src/main/res/values-it/strings.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index af09ab3d..c581962c 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -116,4 +116,8 @@ Gli SMS saranno memorizzati solo all\'interno di quest\'app e non saranno accessibili in altre app. Elimina thread Vuoi importare il file VCF selezionato? + Amoled + Sicurezza + Autenticazione biometrica + Richiedi l\'autenticazione biometrica (ad esempio l\'impronta digitale) prima di poter accedere all\'app. \ No newline at end of file From 40e2576927f819e205ea612e3a967c9405fb1d0e Mon Sep 17 00:00:00 2001 From: Rex_sa Date: Wed, 1 May 2024 20:07:13 +0200 Subject: [PATCH 059/159] Translated using Weblate (Arabic) Currently translated at 100.0% (137 of 137 strings) Translated using Weblate (Arabic) Currently translated at 100.0% (136 of 136 strings) Translated using Weblate (Arabic) Currently translated at 100.0% (133 of 133 strings) Translated using Weblate (Arabic) Currently translated at 100.0% (121 of 121 strings) Translated using Weblate (Arabic) Currently translated at 100.0% (118 of 118 strings) Co-authored-by: Rex_sa Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/ar/ Translation: You Apps/Connect You --- app/src/main/res/values-ar/strings.xml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index 2b694d41..96baa2a5 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -116,4 +116,24 @@ أضف حدثًا ‪التاريخ هل ترغب في استيراد ملف VCF المحدد؟ + ‬أموليد + الأمان + المصادقة البيومترية + اطلب المصادقة البيومترية (مثل بصمة الإصبع) قبل أن تتمكن من الوصول إلى التطبيق. + قيد التنفيذ + الرنين… + جارٍ قطع الاتصال… + كتم + المكالمات + رفض + قبول + جارِ الاتصال… + هل تريد الاتصال بـ %1$s؟ + مكبرات الصوت + رقم غير معروف + لوحة الطلب + انتهت المكالمة + الطلب... + اتصال من + لوحة المفاتيح \ No newline at end of file From 696a9edc0085efa01399cbd999c3877839034a76 Mon Sep 17 00:00:00 2001 From: Fjuro Date: Wed, 1 May 2024 20:07:13 +0200 Subject: [PATCH 060/159] Translated using Weblate (Czech) Currently translated at 100.0% (137 of 137 strings) Translated using Weblate (Czech) Currently translated at 100.0% (136 of 136 strings) Translated using Weblate (Czech) Currently translated at 100.0% (133 of 133 strings) Translated using Weblate (Czech) Currently translated at 100.0% (121 of 121 strings) Translated using Weblate (Czech) Currently translated at 100.0% (118 of 118 strings) Co-authored-by: Fjuro Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/cs/ Translation: You Apps/Connect You --- app/src/main/res/values-cs/strings.xml | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index 04c6f764..36c58ee0 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -72,7 +72,7 @@ Barevné ikony kontaktů Zpráva Vytvořit zástupce - Zavolat + Hovor Nový kontakt Kontakt Další @@ -116,4 +116,24 @@ Přidat telefonní číslo Datum Chcete importovat vybraný soubor VCF? + Amoled + Zabezpečení + Biometrické ověřování + Vyžadovat biometrické ověřování (např. otisk prstu) před přístupem do aplikace. + Probíhá + Vyzvánění… + Odpojování… + Připojování… + Chcete zavolat kontaktu %1$s? + Ztlumit + Reproduktory + Hovory + Odmítnout + Přijmout + Neznámé číslo + Číselník + Hovor ukončen + Vyzvánění... + Hovor od + Číselník \ No newline at end of file From 798733a8f5d9b64c5e62d4ac1d9b4e1bb54e004b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?jos=C3=A9=20m?= Date: Wed, 1 May 2024 20:07:13 +0200 Subject: [PATCH 061/159] Translated using Weblate (Galician) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently translated at 100.0% (137 of 137 strings) Translated using Weblate (Galician) Currently translated at 100.0% (133 of 133 strings) Translated using Weblate (Galician) Currently translated at 100.0% (121 of 121 strings) Translated using Weblate (Galician) Currently translated at 100.0% (118 of 118 strings) Co-authored-by: josé m Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/gl/ Translation: You Apps/Connect You --- app/src/main/res/values-gl/strings.xml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml index 099d7f66..5d1c08dd 100644 --- a/app/src/main/res/values-gl/strings.xml +++ b/app/src/main/res/values-gl/strings.xml @@ -116,4 +116,24 @@ Engadir evento Data Queres importar o ficheiro VCF seleccionado? + AMOLED + Seguridade + Autenticación biométrica + Requerir autenticación biométrica (ex. impresión dactilar) para poder acceder á app. + En proceso + A chamar… + Altofalantes + Teclado + Rexeitar + A conectar… + A desconectar… + Queres chamar a %1$s? + Acalar + Chamadas + Aceptar + Número Descoñecido + Rematou a chamada + Marcando... + Chamada de + Teclado \ No newline at end of file From 38f61354b725eb53960de93ff12e8f31d6e5f671 Mon Sep 17 00:00:00 2001 From: Yaron Shahrabani Date: Wed, 1 May 2024 20:07:13 +0200 Subject: [PATCH 062/159] Translated using Weblate (Hebrew) Currently translated at 100.0% (118 of 118 strings) Co-authored-by: Yaron Shahrabani Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/he/ Translation: You Apps/Connect You --- app/src/main/res/values-iw/strings.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml index 9f9a4b2a..66523a63 100644 --- a/app/src/main/res/values-iw/strings.xml +++ b/app/src/main/res/values-iw/strings.xml @@ -107,4 +107,14 @@ מחיקת הודעה המסרונים יאוחסנו ביישומון בלבד וליישומונים אחרים לא תהיה גישה אליהם. מספר טלפון + הוספת מספר טלפון + הוספת דוא״ל + הוספת הערה + הוספת אירוע + לייבא את קובץ ה־VCF הנבחר? + הוספת כתובת + הוספת אתר + תאריך + אמולד + לשלוח את ההודעה מחולקת לכמה הודעות קטנות? \ No newline at end of file From 82cffc4619d3cbf7f25b2a70378c63371358813a Mon Sep 17 00:00:00 2001 From: NEXI Date: Wed, 1 May 2024 20:07:13 +0200 Subject: [PATCH 063/159] Translated using Weblate (Serbian) Currently translated at 100.0% (137 of 137 strings) Translated using Weblate (Serbian) Currently translated at 100.0% (136 of 136 strings) Translated using Weblate (Serbian) Currently translated at 100.0% (133 of 133 strings) Translated using Weblate (Serbian) Currently translated at 100.0% (121 of 121 strings) Translated using Weblate (Serbian) Currently translated at 100.0% (118 of 118 strings) Co-authored-by: NEXI Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/sr/ Translation: You Apps/Connect You --- app/src/main/res/values-sr/strings.xml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml index 79bcfaae..edbe1ece 100644 --- a/app/src/main/res/values-sr/strings.xml +++ b/app/src/main/res/values-sr/strings.xml @@ -127,4 +127,24 @@ Додај веб-сајт Додај белешку Желите ли да увезете изабрани VCF фајл? + Amoled + Биометријска аутентификација + Безбедност + Захтевајте биометријску аутентификацију (нпр. отисак прста) пре него што се апликацији може приступити. + Звони… + Утишај + Звучници + Прихвати + Непознати број + Бирач бројева + У току + Прекидање везе… + Повезивање… + Желите ли да зовете %1$s? + Одбиј + Позиви + Позив је завршен + Бирање... + Позив од + Тастатура \ No newline at end of file From 7a6236d070fc8140c2069ab507705deb61899979 Mon Sep 17 00:00:00 2001 From: Scrambled777 Date: Wed, 1 May 2024 20:07:13 +0200 Subject: [PATCH 064/159] Translated using Weblate (Hindi) Currently translated at 100.0% (137 of 137 strings) Translated using Weblate (Hindi) Currently translated at 100.0% (136 of 136 strings) Translated using Weblate (Hindi) Currently translated at 100.0% (133 of 133 strings) Translated using Weblate (Hindi) Currently translated at 100.0% (121 of 121 strings) Translated using Weblate (Hindi) Currently translated at 100.0% (118 of 118 strings) Co-authored-by: Scrambled777 Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/hi/ Translation: You Apps/Connect You --- app/src/main/res/values-hi/strings.xml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/app/src/main/res/values-hi/strings.xml b/app/src/main/res/values-hi/strings.xml index 5b96fa21..0902eef9 100644 --- a/app/src/main/res/values-hi/strings.xml +++ b/app/src/main/res/values-hi/strings.xml @@ -116,4 +116,24 @@ कार्यक्रम जोड़ें तारीख खोजें + Amoled + सुरक्षा + बॉयोमीट्रिक प्रमाणीकरण + ऐप तक पहुंचने से पहले बायोमेट्रिक प्रमाणीकरण (जैसे फ़िंगरप्रिंट) की आवश्यकता है। + प्रगति पर है + बज रहा है… + डिस्कनेक्ट हो रहा है… + क्या आप %1$s को कॉल करना चाहते हैं? + मूक + स्पीकर + कॉल + अस्वीकार + स्वीकारें + अज्ञात नंबर + डायलपैड + कनेक्ट हो रहा है… + कॉल समाप्त + डायल किया जा रहा है..। + इनसे कॉल + कीपैड \ No newline at end of file From f7d1409521e869ddc319b27fbcbb8ead753e09d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=A7=E7=8E=8B=E5=8F=AB=E6=88=91=E6=9D=A5=E5=B7=A1?= =?UTF-8?q?=E5=B1=B1?= Date: Wed, 1 May 2024 20:07:14 +0200 Subject: [PATCH 065/159] Translated using Weblate (Chinese (Simplified)) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently translated at 100.0% (118 of 118 strings) Co-authored-by: 大王叫我来巡山 Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/zh_Hans/ Translation: You Apps/Connect You --- app/src/main/res/values-zh-rCN/strings.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 4e6c197d..865f1307 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -116,4 +116,5 @@ 添加地址 添加事件 您是否要导入选定的 VCF 文件? + Amoled \ No newline at end of file From 2c8578d2f1cddac46895c82e9c43af6e9602de16 Mon Sep 17 00:00:00 2001 From: 0que <0que@users.noreply.hosted.weblate.org> Date: Wed, 1 May 2024 20:07:14 +0200 Subject: [PATCH 066/159] Translated using Weblate (Russian) Currently translated at 100.0% (137 of 137 strings) Translated using Weblate (Russian) Currently translated at 100.0% (133 of 133 strings) Translated using Weblate (Russian) Currently translated at 100.0% (121 of 121 strings) Translated using Weblate (Russian) Currently translated at 100.0% (118 of 118 strings) Co-authored-by: 0que <0que@users.noreply.hosted.weblate.org> Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/ru/ Translation: You Apps/Connect You --- app/src/main/res/values-ru/strings.xml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 586eb363..8633ff62 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -116,4 +116,24 @@ Добавить адрес Дата Импортировать выбранный VCF файл? + Чёрная + Безопасность + Вход по биометрии + Требовать биометрическую аутентификацию для открытия приложения. Например по отпечатку пальца. + Вызов… + Откл. звук + Динамики + Клавиатура набора + В процессе + Отключение… + Вызовы + Подключение… + Отклонить + Хотите набрать %1$s? + Принять + Неизвестный номер + Вызов завершён + Вызов... + Позвонить из + Клавиатура набора \ No newline at end of file From ef4378e90b2ae6f189e2acaf169877d7eb8bf882 Mon Sep 17 00:00:00 2001 From: Software In Interlingua Date: Wed, 1 May 2024 20:07:14 +0200 Subject: [PATCH 067/159] Translated using Weblate (Interlingua) Currently translated at 100.0% (118 of 118 strings) Co-authored-by: Software In Interlingua Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/ia/ Translation: You Apps/Connect You --- app/src/main/res/values-ia/strings.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/res/values-ia/strings.xml b/app/src/main/res/values-ia/strings.xml index 812874d7..0988d8be 100644 --- a/app/src/main/res/values-ia/strings.xml +++ b/app/src/main/res/values-ia/strings.xml @@ -116,4 +116,5 @@ Data Adder evento Desira tu importar le file VCF seligite? + Amoled \ No newline at end of file From 842dd9454b7799e6acbbe7a5d6bc1f64dab758d0 Mon Sep 17 00:00:00 2001 From: gallegonovato Date: Wed, 1 May 2024 20:07:14 +0200 Subject: [PATCH 068/159] Translated using Weblate (Spanish) Currently translated at 100.0% (137 of 137 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (136 of 136 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (133 of 133 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (121 of 121 strings) Co-authored-by: gallegonovato Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/es/ Translation: You Apps/Connect You --- app/src/main/res/values-es/strings.xml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 815d50ad..18551ddc 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -116,4 +116,24 @@ Añadir nota Fecha ¿Desea importar el archivo VCF seleccionado? + Amoled + Autenticación biométrica + Seguridad + Requerir autenticación biométrica (por ejemplo, huella digital) antes de poder acceder a la aplicación. + En curso + Sonando… + Desconectando… + Conectando… + ¿Quieres llamar a %1$s? + Silenciar + Altavoces + Llamadas + Rechazar + Aceptar + Número desconocido + Marcador + Llamada terminada + Llamada de + Llamando... + Teclado \ No newline at end of file From 23d5ce8c08276a1c22e75658de6e98ca8df59b3f Mon Sep 17 00:00:00 2001 From: ngocanhtve Date: Wed, 1 May 2024 20:07:14 +0200 Subject: [PATCH 069/159] Translated using Weblate (Vietnamese) Currently translated at 33.0% (40 of 121 strings) Co-authored-by: ngocanhtve Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/vi/ Translation: You Apps/Connect You --- app/src/main/res/values-vi/strings.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index f900c536..e4928585 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -1,5 +1,5 @@ - + Nơi làm việc Di động Khác @@ -49,4 +49,5 @@ Tên Tổ chức Nhà riêng + Tìm kiếm \ No newline at end of file From db3d29f8210863b3cc69da4cf3012737d75231d9 Mon Sep 17 00:00:00 2001 From: Dan Date: Wed, 1 May 2024 20:07:14 +0200 Subject: [PATCH 070/159] Translated using Weblate (Ukrainian) Currently translated at 100.0% (137 of 137 strings) Translated using Weblate (Ukrainian) Currently translated at 100.0% (121 of 121 strings) Co-authored-by: Dan Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/uk/ Translation: You Apps/Connect You --- app/src/main/res/values-uk/strings.xml | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 02d2f982..5bdc4872 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -8,7 +8,7 @@ Скопійовано в буфер обміну Ім’я не може бути порожнім! Факс дому - Ви впевнені? Це не можна відмінити! + Ви впевнені? Це не можна скасувати! Створити контакт Поділитися Спробувати знову @@ -37,7 +37,7 @@ День народження Річниця Ел. пошта - Добре + Прийняти Скидання Порожньо. Пристрій @@ -82,7 +82,7 @@ Вебсайт Домашня сторінка FTP - Нікнейм + Псевдонім Показувати більше полів Блоґ Більше @@ -117,4 +117,24 @@ Додати примітку Додати подію Ви хочете імпортувати вибраний VCF-файл? + Біометрична перевірка + Вимагати біометричну перевірку (наприклад, відбиток пальця) перед тим, як отримати доступ до застосунку. + Безпека + Чорний + З\'єднання… + Панель набору + У процесі + Виклик… + Від\'єднання… + Бажаєте зателефонувати %1$s? + Вимк. звук + Динаміки + Виклики + Відхилити + Прийняти + Невідомий номер + Виклик завершено + Набір номера... + Виклик з + Клавіатура \ No newline at end of file From 199e1b6ec8069d1c96abd696b1c7e9a0e3c86775 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Priit=20J=C3=B5er=C3=BC=C3=BCt?= Date: Wed, 1 May 2024 20:07:15 +0200 Subject: [PATCH 071/159] Translated using Weblate (Estonian) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently translated at 100.0% (121 of 121 strings) Co-authored-by: Priit Jõerüüt Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/et/ Translation: You Apps/Connect You --- app/src/main/res/values-et/strings.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/src/main/res/values-et/strings.xml b/app/src/main/res/values-et/strings.xml index d0302cba..cafb6706 100644 --- a/app/src/main/res/values-et/strings.xml +++ b/app/src/main/res/values-et/strings.xml @@ -116,4 +116,8 @@ Hele kujundus Rakenduse teave Kas sa soovid importida valitud VCF-faili? + Amoled + Turvalisus + Biomeetriline autentimine + Muuda biomeetriline autentimine (näiteks sõrmejälje alusel) nõutavaks enne rakenduse avamist. \ No newline at end of file From 2b744243b3461b51b2079a798a266e3d7e74e28b Mon Sep 17 00:00:00 2001 From: Linerly Date: Wed, 1 May 2024 20:07:15 +0200 Subject: [PATCH 072/159] Translated using Weblate (Indonesian) Currently translated at 100.0% (137 of 137 strings) Translated using Weblate (Indonesian) Currently translated at 100.0% (136 of 136 strings) Translated using Weblate (Indonesian) Currently translated at 100.0% (133 of 133 strings) Translated using Weblate (Indonesian) Currently translated at 100.0% (121 of 121 strings) Co-authored-by: Linerly Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/id/ Translation: You Apps/Connect You --- app/src/main/res/values-in/strings.xml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index e8fa4002..08ffab2d 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -116,4 +116,24 @@ Jumlah maksimal data cadangan yang diizinkan Direktori Tidak ada + AMOLED + Keamanan + Autentikasi Biometrik + Meminta autentikasi biometrik (seperti sidik jari) sebelum aplikasi dapat diakses. + Sedang berlangsung + Berdering… + Memutuskan hubungan… + Menghubungkan… + Apakah Anda ingin memanggil %1$s? + Speaker + Bisukan + Nomor Tidak Dikenal + Panggilan + Tolak + Terima + Tombol Angka + Panggilan Berakhir + Memanggil... + Panggilan Dari + Papan tombol \ No newline at end of file From e23f3af37474990c829329701688799d782f5a0f Mon Sep 17 00:00:00 2001 From: Raymond Nee Date: Wed, 1 May 2024 20:07:15 +0200 Subject: [PATCH 073/159] Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (121 of 121 strings) Co-authored-by: Raymond Nee Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/zh_Hans/ Translation: You Apps/Connect You --- app/src/main/res/values-zh-rCN/strings.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 865f1307..44c7eb80 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -117,4 +117,7 @@ 添加事件 您是否要导入选定的 VCF 文件? Amoled + 安全 + 生物识别认证 + 访问应用前要求生物识别认证(如指纹)。 \ No newline at end of file From a4541db8d579f03f598421a3d411e0e8d8697641 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=A7=E7=8E=8B=E5=8F=AB=E6=88=91=E6=9D=A5=E5=B7=A1?= =?UTF-8?q?=E5=B1=B1?= Date: Wed, 1 May 2024 20:07:15 +0200 Subject: [PATCH 074/159] Translated using Weblate (Chinese (Simplified)) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently translated at 100.0% (137 of 137 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (136 of 136 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (133 of 133 strings) Co-authored-by: 大王叫我来巡山 Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/zh_Hans/ Translation: You Apps/Connect You --- app/src/main/res/values-zh-rCN/strings.xml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 44c7eb80..37fac99c 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -120,4 +120,20 @@ 安全 生物识别认证 访问应用前要求生物识别认证(如指纹)。 + 进行中 + 正在响铃… + 正在断开连接… + 正在连接… + 你要呼叫 %1$s 吗? + 静音 + 扬声器 + 通话 + 拒绝 + 接受 + 未知号码 + 拨号盘 + 来电方 + 通话结束了 + 拨号中… + 键盘 \ No newline at end of file From d49107c8390317d8dd0ded3ca61aa07e4cccd340 Mon Sep 17 00:00:00 2001 From: Scrambled777 Date: Tue, 7 May 2024 03:29:44 +0000 Subject: [PATCH 075/159] Translated using Weblate (Hindi) Currently translated at 100.0% (137 of 137 strings) Translation: You Apps/Connect You Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/hi/ --- app/src/main/res/values-hi/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values-hi/strings.xml b/app/src/main/res/values-hi/strings.xml index 0902eef9..ebfd2aef 100644 --- a/app/src/main/res/values-hi/strings.xml +++ b/app/src/main/res/values-hi/strings.xml @@ -133,7 +133,7 @@ डायलपैड कनेक्ट हो रहा है… कॉल समाप्त - डायल किया जा रहा है..। + डायल किया जा रहा है… इनसे कॉल कीपैड \ No newline at end of file From 26e875581eefa56b47dcc8af19bcf5e709d28487 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Priit=20J=C3=B5er=C3=BC=C3=BCt?= Date: Mon, 13 May 2024 07:23:58 +0000 Subject: [PATCH 076/159] Translated using Weblate (Estonian) Currently translated at 100.0% (137 of 137 strings) Translation: You Apps/Connect You Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/et/ --- app/src/main/res/values-et/strings.xml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/app/src/main/res/values-et/strings.xml b/app/src/main/res/values-et/strings.xml index cafb6706..c1443e1d 100644 --- a/app/src/main/res/values-et/strings.xml +++ b/app/src/main/res/values-et/strings.xml @@ -120,4 +120,20 @@ Turvalisus Biomeetriline autentimine Muuda biomeetriline autentimine (näiteks sõrmejälje alusel) nõutavaks enne rakenduse avamist. + Toimetamisel + Helistame… + Katkestame ühendust… + Ühendame… + Kas sa soovid helistada %1$sle? + Summuta + Kõlarid + Kõned + Keeldu + Võta vastu + Tundmatu number + Klahvistik + Kõne on lõppenud + Helistame… + Kõne teine osapool: + Klahvistik \ No newline at end of file From b058ea0e63d16c23ab3159c6315d343e5ba69b7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?O=C4=9Fuz=20Ersen?= Date: Tue, 14 May 2024 17:07:29 +0000 Subject: [PATCH 077/159] Translated using Weblate (Turkish) Currently translated at 100.0% (145 of 145 strings) Translation: You Apps/Connect You Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/tr/ --- app/src/main/res/values-tr/strings.xml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index e039c350..dc445dea 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -137,4 +137,12 @@ Aranıyor... Arayan Tuş takımı + Zil sesini değiştir + Son aramalar + Numara engellendi + Numarayı engelle + Numaranın engelini kaldır + Numaraları engelle + Özel zil sesi seç + Numara engelleme desteklenmiyor, Connect You öntanımlı SMS/Telefon uygulaması değil \ No newline at end of file From 13ea3e77d3acfef113b9cc2ac17f9c70b862a058 Mon Sep 17 00:00:00 2001 From: Pascal Dietrich Date: Tue, 14 May 2024 10:03:10 +0000 Subject: [PATCH 078/159] Translated using Weblate (German) Currently translated at 95.8% (139 of 145 strings) Translation: You Apps/Connect You Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/de/ --- app/src/main/res/values-de/strings.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index d7fe8ee1..b0d46062 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -133,4 +133,10 @@ Wählen… Anruf von Anruf beendet + Nummern blockieren + Letzte Anrufe + Benutzerdefinierten Klingelton auswählen + Nummer blockiert + Klingelton ändern + Nummer blockieren \ No newline at end of file From cd512b5856ef32c3e426f87467e24429a652be6b Mon Sep 17 00:00:00 2001 From: Rex_sa Date: Tue, 14 May 2024 15:44:40 +0000 Subject: [PATCH 079/159] Translated using Weblate (Arabic) Currently translated at 100.0% (145 of 145 strings) Translation: You Apps/Connect You Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/ar/ --- app/src/main/res/values-ar/strings.xml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index 96baa2a5..ffbba6aa 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -28,7 +28,7 @@ المؤلِّف الترجمة نُسخ للحافظة - ابحث + بحث أعِد التعيين أنشئ جهة اتِّصال أعد المحاولة @@ -136,4 +136,12 @@ الطلب... اتصال من لوحة المفاتيح + المكالمات الأخيرة + حظر الأرقام + حظر الأرقام غير مدعوم، Connect You ليس هو التطبيق الافتراضي للرسائل القصيرة/الهاتف + الرقم محظور + حظر الرقم + إلغاء حظر الرقم + حدد نغمة رنين مخصصة + تغيير نغمة الرنين \ No newline at end of file From 25ec737cad3065089941fdb5b255d357bfe266d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=A7=E7=8E=8B=E5=8F=AB=E6=88=91=E6=9D=A5=E5=B7=A1?= =?UTF-8?q?=E5=B1=B1?= Date: Wed, 15 May 2024 08:52:08 +0000 Subject: [PATCH 080/159] Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (145 of 145 strings) Translation: You Apps/Connect You Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/zh_Hans/ --- app/src/main/res/values-zh-rCN/strings.xml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 37fac99c..90ecbc59 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -136,4 +136,12 @@ 通话结束了 拨号中… 键盘 + 更改铃声 + 最近通话 + 取消封禁号码 + 封禁号码 + 选择自定义铃声 + 已封禁号码 + 不支持封禁号码。 Connect You 不是默认的短信/通话应用 + 封禁号码 \ No newline at end of file From fe5874a082f530fa913d0ddf4a81c050d4f244f0 Mon Sep 17 00:00:00 2001 From: Fjuro Date: Tue, 14 May 2024 08:58:30 +0000 Subject: [PATCH 081/159] Translated using Weblate (Czech) Currently translated at 100.0% (145 of 145 strings) Translation: You Apps/Connect You Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/cs/ --- app/src/main/res/values-cs/strings.xml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index 36c58ee0..be472aef 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -136,4 +136,12 @@ Vyzvánění... Hovor od Číselník + Číslo zablokováno + Zablokovat číslo + Nedávné hovory + Nastavit vlastní vyzvánění + Změnit vyzvánění + Zablokovat čísla + Blokování čísla není podporováno, aplikace Connect You není výchozí aplikací telefonu/SMS + Odblokovat číslo \ No newline at end of file From f0c10d31a16a7f7f6a70cfdf4bbef1150147d174 Mon Sep 17 00:00:00 2001 From: NEXI Date: Tue, 14 May 2024 19:18:09 +0000 Subject: [PATCH 082/159] Translated using Weblate (Serbian) Currently translated at 100.0% (145 of 145 strings) Translation: You Apps/Connect You Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/sr/ --- app/src/main/res/values-sr/strings.xml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml index edbe1ece..d7aaf228 100644 --- a/app/src/main/res/values-sr/strings.xml +++ b/app/src/main/res/values-sr/strings.xml @@ -147,4 +147,12 @@ Бирање... Позив од Тастатура + Недавни позиви + Број је блокиран + Блокирај број + Изабери прилагођену мелодију звона + Промени мелодију звона + Деблокирај број + Блокирај бројеве + Блокирање бројева није подржано, Connect You није подразумевана апликација за SMS/телефон \ No newline at end of file From a8206817c35447c55a999379c17d1125ff27af73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Priit=20J=C3=B5er=C3=BC=C3=BCt?= Date: Tue, 14 May 2024 08:06:42 +0000 Subject: [PATCH 083/159] Translated using Weblate (Estonian) Currently translated at 100.0% (145 of 145 strings) Translation: You Apps/Connect You Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/et/ --- app/src/main/res/values-et/strings.xml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/src/main/res/values-et/strings.xml b/app/src/main/res/values-et/strings.xml index c1443e1d..6592ca95 100644 --- a/app/src/main/res/values-et/strings.xml +++ b/app/src/main/res/values-et/strings.xml @@ -136,4 +136,12 @@ Helistame… Kõne teine osapool: Klahvistik + Hiljutised kõned + Blokeeri telefoninumbreid + Telefoninumber on blokeeritud + Blokeeri telefoninumber + Vali sobilik telefonihelin + Muuda telefonihelinat + Kuna Connect You pole selles nutiseadmes vaikimisi kasutatav telefoni- ja SMS-sõnumirakendus, siis telefoninumbrite blokeerimine ei ole võimalik + Lõpeta telefoninumbri blokeerimine \ No newline at end of file From 7ed66060a7ef508333f892bac5c6e9cb21f90090 Mon Sep 17 00:00:00 2001 From: gallegonovato Date: Wed, 15 May 2024 19:21:48 +0000 Subject: [PATCH 084/159] Translated using Weblate (Spanish) Currently translated at 100.0% (145 of 145 strings) Translation: You Apps/Connect You Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/es/ --- app/src/main/res/values-es/strings.xml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 18551ddc..3292633c 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -136,4 +136,12 @@ Llamada de Llamando... Teclado + Cambiar tono de llamada + Bloquar números + No se admite el bloqueo de números, Connect You no es la aplicación SMS/Teléfono predeterminada + Bloquar número + Seleccionar tono de llamada personalizado + Últimas llamadas + Número bloqueado + Desbloquear número \ No newline at end of file From fd68fb712d8460e49e5cf5d363df131fc68eec40 Mon Sep 17 00:00:00 2001 From: Scrambled777 Date: Wed, 15 May 2024 14:08:43 +0000 Subject: [PATCH 085/159] Translated using Weblate (Hindi) Currently translated at 100.0% (145 of 145 strings) Translation: You Apps/Connect You Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/hi/ --- app/src/main/res/values-hi/strings.xml | 62 +++++++++++++++----------- 1 file changed, 35 insertions(+), 27 deletions(-) diff --git a/app/src/main/res/values-hi/strings.xml b/app/src/main/res/values-hi/strings.xml index ebfd2aef..cf52cac4 100644 --- a/app/src/main/res/values-hi/strings.xml +++ b/app/src/main/res/values-hi/strings.xml @@ -9,14 +9,14 @@ फोन पता संगठन - घर + गृह वर्षगांठ टिप्पणी ब्लॉग FTP मुखपृष्ठ समूह प्रबंधित करें - सभी के लिए समूह हटाएं + सभी के लिए समूह मिटाएं समूह पहले से मौजूद है क्या आप संदेश को अनेक छोटे-छोटे रूपों में भेजना चाहते हैं? प्रारंभ टैब @@ -25,13 +25,13 @@ रंगीन संपर्क चिह्न अनुवाद vCard आयात करें - क्या आप चयनित VCF फ़ाइल आयात करना चाहते हैं? + क्या आप चयनित VCF फाइल आयात करना चाहते हैं? क्लिपबोर्ड पर कॉपी किया गया - संपर्क हटाएँ + संपर्क मिटाएं ठीक है रीसेट - पुन: प्रयास - शेयर + पुन: प्रयास करें + साझा करें संपर्क में जोडें शॉर्टकट बनाएं संपर्क @@ -39,21 +39,21 @@ संपादित करें फिल्टर अधिक - और फ़ील्ड दिखाएं + अधिक क्षेत्र दिखाएं कम दिखाएं संपर्क चुनें सहेजें नाम खाली नहीं हो सकता! आयातित। - निर्यात किया गया। - यहाँ कुछ नहीं। + निर्यातित। + यहां कुछ नहीं। ई-मेल आयोजन वेबसाइट - तस्वीर + फोटो फोन नंबर - क्रमबद्ध करें - खाते का प्रकार + छंटाई क्रम + खाता प्रकार नाम पहला नाम उपनाम @@ -62,10 +62,10 @@ कार्य मोबाइल अन्य - कस्टम + तदनुकूल कार - घर फैक्स - फैक्स कार्य + गृह फैक्स + कार्य फैक्स सहायक जन्मदिन शीर्षक @@ -74,24 +74,24 @@ डिवाइस स्थानीय कॉपी - स्थान परिवर्तन - हटाएं + स्थानान्तर + मिटाएं %1$s चयनित संदेश संदेश भेजें उत्तर - कनेक्ट नहीं हो सका। कृपया सुनिश्चित करें कि हवाई जहाज़ मोड बंद है। + जुड़ नहीं सका। कृपया सुनिश्चित करें कि हवाई जहाज़ मोड बंद है। संदेश बहुत लंबा है! निजी SMS डेटाबेस थ्रेड मिटाएं - संदेश हटाएं - सेटिंग्स + संदेश मिटाएं + सेटिंग थीम सिस्टम हल्की गहरी बैकअप अंतराल - डॉयरेक्टरी + निर्देशिका दोनों कोई नहीं विविध @@ -100,12 +100,12 @@ दिखावट बैकअप पासवर्ड - बारे में + ऐप के बारे में लाइसेंस लेखक संस्करण वेबसाइट जोड़ें - फोन नंबर डालें + फोन नंबर जोड़ें SMS केवल ऐप के भीतर संग्रहीत किया जाएगा और अन्य ऐप्स में पहुंच योग्य नहीं होगा। vCard निर्यात करें SIM से आयात करें @@ -116,10 +116,10 @@ कार्यक्रम जोड़ें तारीख खोजें - Amoled + एमोलेड सुरक्षा बॉयोमीट्रिक प्रमाणीकरण - ऐप तक पहुंचने से पहले बायोमेट्रिक प्रमाणीकरण (जैसे फ़िंगरप्रिंट) की आवश्यकता है। + ऐप तक पहुंचने से पहले बायोमेट्रिक प्रमाणीकरण (जैसे फिंगरप्रिंट) की आवश्यकता है। प्रगति पर है बज रहा है… डिस्कनेक्ट हो रहा है… @@ -127,13 +127,21 @@ मूक स्पीकर कॉल - अस्वीकार + अस्वीकारें स्वीकारें अज्ञात नंबर डायलपैड - कनेक्ट हो रहा है… + जुड़ रहा है… कॉल समाप्त डायल किया जा रहा है… इनसे कॉल कीपैड + तदनुकूल रिंगटोन चुनें + हालिया कॉल + नंबरों को ब्लॉक करें + नंबर ब्लॉक करना समर्थित नहीं है, Connect You तयशुदा SMS/फोन ऐप नहीं है + नंबर ब्लॉक किया गया + नंबर ब्लॉक करें + नंबर अनब्लॉक करें + रिंगटोन बदलें \ No newline at end of file From 6da9eaca6b98d29ff71c653f9a7a38bda99e3940 Mon Sep 17 00:00:00 2001 From: 0que <0que@users.noreply.hosted.weblate.org> Date: Thu, 16 May 2024 16:56:29 +0000 Subject: [PATCH 086/159] Translated using Weblate (Russian) Currently translated at 100.0% (145 of 145 strings) Translation: You Apps/Connect You Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/ru/ --- app/src/main/res/values-ru/strings.xml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 8633ff62..fb54644d 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -136,4 +136,12 @@ Вызов... Позвонить из Клавиатура набора + Изменить мелодию + Номер заблокирован + Недавние вызовы + Блокировка номеров недоступна. Установите Connect You в качестве приложения для SMS и вызовов + Выбрать свою мелодию + Заблокировать номера + Заблокировать номер + Разблокировать номер \ No newline at end of file From 924da7cb97addac16be259a9d9ebd7b4b8bcee6b Mon Sep 17 00:00:00 2001 From: Dan Date: Fri, 17 May 2024 11:09:09 +0000 Subject: [PATCH 087/159] Translated using Weblate (Ukrainian) Currently translated at 100.0% (145 of 145 strings) Translation: You Apps/Connect You Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/uk/ --- app/src/main/res/values-uk/strings.xml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 5bdc4872..411d5d05 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -137,4 +137,12 @@ Набір номера... Виклик з Клавіатура + Виберіть власний рингтон + Змінити рингтон + Заблокувати номер + Нещодавні виклики + Заблокувати номери + Блокування номерів не підтримується. Connect You не типовий застосунок для SMS та викликів + Номер заблоковано + Розблокувати номер \ No newline at end of file From 7966c109060c844f2a2def9d51a803f67fc85441 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?jos=C3=A9=20m?= Date: Fri, 17 May 2024 07:02:36 +0000 Subject: [PATCH 088/159] Translated using Weblate (Galician) Currently translated at 100.0% (145 of 145 strings) Translation: You Apps/Connect You Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/gl/ --- app/src/main/res/values-gl/strings.xml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml index 5d1c08dd..7f371f7e 100644 --- a/app/src/main/res/values-gl/strings.xml +++ b/app/src/main/res/values-gl/strings.xml @@ -136,4 +136,12 @@ Marcando... Chamada de Teclado + Cambiar ton + Chamadas recentes + Bloquear números + Elixe ton persoal + Non podemos bloquear números, Connect You non é a app de SMS/Teléfono por defecto + Número bloqueado + Bloquear número + Desbloquear número \ No newline at end of file From 352af7c5fc1c6544c1c7498c60513453a985427e Mon Sep 17 00:00:00 2001 From: Femini Date: Sat, 18 May 2024 17:49:55 +0000 Subject: [PATCH 089/159] Translated using Weblate (Azerbaijani) Currently translated at 62.5% (92 of 147 strings) Translation: You Apps/Connect You Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/az/ --- app/src/main/res/values-az/strings.xml | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/app/src/main/res/values-az/strings.xml b/app/src/main/res/values-az/strings.xml index af1114ab..2d11943f 100644 --- a/app/src/main/res/values-az/strings.xml +++ b/app/src/main/res/values-az/strings.xml @@ -9,10 +9,10 @@ Tərcümə Connect You Axtarış - Buferə kopyalandı + Buferə köçürüldü Əlaqəni sil Təkrar cəhd et - Hadisə + Tədbir Siz əminsiniz\? Bu geri qaytarıla bilməz! Ləğv et İxrac edildi. @@ -39,10 +39,10 @@ İş faks Oldu Sıfırla - Kopyala + Köçür Köçürt Tənzimləmələr - Başlanğıc paneli + Başlanğıc səhifəsi Burada heç nə yoxdur. Cihaz Yerli @@ -53,8 +53,8 @@ Tema Sistem İşıqlı - Qaranlıq - Avtomatik Nüsxələmə + Qara + Avtomatik nüsxələmə Kataloq seç Hər ikisi Heç biri @@ -73,7 +73,7 @@ Sürüşdürdükdə alt çubuğu yığcamlaşdır Qısayol Yarat Məktub - Zəng et + Nömrə yaz Redaktə et Filtr Daha çox @@ -87,4 +87,10 @@ Ləqəb Yeni əlaqə Əlaqə + Şəkil + Ad + Əlaqələr + Əlaqə seç + Saxla + Telefon nömrəsi \ No newline at end of file From a0d6a9522bdcca912401267dfdc43ce6e5d1a3b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?O=C4=9Fuz=20Ersen?= Date: Sat, 18 May 2024 09:41:54 +0000 Subject: [PATCH 090/159] Translated using Weblate (Turkish) Currently translated at 100.0% (147 of 147 strings) Translation: You Apps/Connect You Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/tr/ --- app/src/main/res/values-tr/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index dc445dea..c90c4e59 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -145,4 +145,6 @@ Numaraları engelle Özel zil sesi seç Numara engelleme desteklenmiyor, Connect You öntanımlı SMS/Telefon uygulaması değil + Kişileri ara + Yeni görüşme başlat \ No newline at end of file From 323e108bad29f761c2025b349ddb929f28e19835 Mon Sep 17 00:00:00 2001 From: Pascal Dietrich Date: Sat, 18 May 2024 20:09:08 +0000 Subject: [PATCH 091/159] Translated using Weblate (German) Currently translated at 95.2% (140 of 147 strings) Translation: You Apps/Connect You Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/de/ --- app/src/main/res/values-de/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index b0d46062..d0075553 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -139,4 +139,6 @@ Nummer blockiert Klingelton ändern Nummer blockieren + Eine neue Unterhaltung beginnen + Kontakte suchen \ No newline at end of file From 130960f6002f72ab72d49e22b7a5954b459cc2d9 Mon Sep 17 00:00:00 2001 From: Dan Date: Sat, 18 May 2024 16:50:43 +0000 Subject: [PATCH 092/159] Translated using Weblate (Ukrainian) Currently translated at 100.0% (147 of 147 strings) Translation: You Apps/Connect You Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/uk/ --- app/src/main/res/values-uk/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 411d5d05..3b0461dc 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -145,4 +145,6 @@ Блокування номерів не підтримується. Connect You не типовий застосунок для SMS та викликів Номер заблоковано Розблокувати номер + Почати нову розмову + Пошук контактів \ No newline at end of file From 98ba129955724ac7d284cb51cf52886ce28ddd26 Mon Sep 17 00:00:00 2001 From: Rex_sa Date: Sat, 18 May 2024 23:11:03 +0000 Subject: [PATCH 093/159] Translated using Weblate (Arabic) Currently translated at 100.0% (147 of 147 strings) Translation: You Apps/Connect You Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/ar/ --- app/src/main/res/values-ar/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index ffbba6aa..a3532a62 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -144,4 +144,6 @@ إلغاء حظر الرقم حدد نغمة رنين مخصصة تغيير نغمة الرنين + بحث في جهات الاتصال + بدء محادثة جديدة \ No newline at end of file From 045965767814832165ba45449476055f90429039 Mon Sep 17 00:00:00 2001 From: 0que <0que@users.noreply.hosted.weblate.org> Date: Sun, 19 May 2024 07:17:51 +0000 Subject: [PATCH 094/159] Translated using Weblate (Russian) Currently translated at 100.0% (147 of 147 strings) Translation: You Apps/Connect You Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/ru/ --- app/src/main/res/values-ru/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index fb54644d..8d18966a 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -144,4 +144,6 @@ Заблокировать номера Заблокировать номер Разблокировать номер + Поиск контактов + Начать беседу \ No newline at end of file From 46992305b607b56c65aec74a225ca6d957125dc4 Mon Sep 17 00:00:00 2001 From: gallegonovato Date: Sat, 18 May 2024 18:35:06 +0000 Subject: [PATCH 095/159] Translated using Weblate (Spanish) Currently translated at 100.0% (147 of 147 strings) Translation: You Apps/Connect You Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/es/ --- app/src/main/res/values-es/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 3292633c..c61919f9 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -144,4 +144,6 @@ Últimas llamadas Número bloqueado Desbloquear número + Iniciar una nueva conversión + Buscar contactos \ No newline at end of file From 77aa873d7c4d8ddc041b025d408dcf47c515be1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?jos=C3=A9=20m?= Date: Sun, 19 May 2024 05:26:19 +0000 Subject: [PATCH 096/159] Translated using Weblate (Galician) Currently translated at 100.0% (147 of 147 strings) Translation: You Apps/Connect You Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/gl/ --- app/src/main/res/values-gl/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml index 7f371f7e..39014fe4 100644 --- a/app/src/main/res/values-gl/strings.xml +++ b/app/src/main/res/values-gl/strings.xml @@ -144,4 +144,6 @@ Número bloqueado Bloquear número Desbloquear número + Buscar contactos + Comezar nova conversa \ No newline at end of file From 5f51d482b6e36137d83634f1dc88bd41b896fe88 Mon Sep 17 00:00:00 2001 From: Scrambled777 Date: Sat, 18 May 2024 10:07:21 +0000 Subject: [PATCH 097/159] Translated using Weblate (Hindi) Currently translated at 100.0% (147 of 147 strings) Translation: You Apps/Connect You Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/hi/ --- app/src/main/res/values-hi/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/values-hi/strings.xml b/app/src/main/res/values-hi/strings.xml index cf52cac4..a9c9c1e2 100644 --- a/app/src/main/res/values-hi/strings.xml +++ b/app/src/main/res/values-hi/strings.xml @@ -144,4 +144,6 @@ नंबर ब्लॉक करें नंबर अनब्लॉक करें रिंगटोन बदलें + संपर्क खोजें + एक नई बातचीत शुरू करें \ No newline at end of file From ecfbb031cd7a6e5a2f88dbc4bcb136d8b70ee4b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=A7=E7=8E=8B=E5=8F=AB=E6=88=91=E6=9D=A5=E5=B7=A1?= =?UTF-8?q?=E5=B1=B1?= Date: Mon, 20 May 2024 07:04:37 +0000 Subject: [PATCH 098/159] Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (147 of 147 strings) Translation: You Apps/Connect You Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/zh_Hans/ --- app/src/main/res/values-zh-rCN/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 90ecbc59..50e3bdd2 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -144,4 +144,6 @@ 已封禁号码 不支持封禁号码。 Connect You 不是默认的短信/通话应用 封禁号码 + 搜索联系人 + 开始新对话 \ No newline at end of file From f113dd18f6e08e4c49f0a812565b186cbebe9865 Mon Sep 17 00:00:00 2001 From: Fjuro Date: Sun, 19 May 2024 12:19:09 +0000 Subject: [PATCH 099/159] Translated using Weblate (Czech) Currently translated at 100.0% (147 of 147 strings) Translation: You Apps/Connect You Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/cs/ --- app/src/main/res/values-cs/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index be472aef..c38dd058 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -144,4 +144,6 @@ Zablokovat čísla Blokování čísla není podporováno, aplikace Connect You není výchozí aplikací telefonu/SMS Odblokovat číslo + Hledat kontakty + Začít novou konverzaci \ No newline at end of file From 9b56ef68adac2484e82f734ad97181f5e51fa100 Mon Sep 17 00:00:00 2001 From: NEXI Date: Sun, 19 May 2024 18:43:11 +0000 Subject: [PATCH 100/159] Translated using Weblate (Serbian) Currently translated at 100.0% (147 of 147 strings) Translation: You Apps/Connect You Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/sr/ --- app/src/main/res/values-sr/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml index d7aaf228..df5029d5 100644 --- a/app/src/main/res/values-sr/strings.xml +++ b/app/src/main/res/values-sr/strings.xml @@ -155,4 +155,6 @@ Деблокирај број Блокирај бројеве Блокирање бројева није подржано, Connect You није подразумевана апликација за SMS/телефон + Претрага контаката + Започни нови разговор \ No newline at end of file From f1377e5613c5b3c371669a85b7d983033528eb23 Mon Sep 17 00:00:00 2001 From: Yamin Siahmargooei Date: Wed, 22 May 2024 03:03:16 +0000 Subject: [PATCH 101/159] Translated using Weblate (Persian) Currently translated at 81.6% (120 of 147 strings) Translation: You Apps/Connect You Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/fa/ --- app/src/main/res/values-fa/strings.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml index 0d6a5f54..97cd4e46 100644 --- a/app/src/main/res/values-fa/strings.xml +++ b/app/src/main/res/values-fa/strings.xml @@ -116,4 +116,7 @@ دیتابیس پیامک خصوصی فروریختن کادر پایینی هنگام پیمایش آیا میخواهید فایل VCF انتخاب شده را درون بری کنید؟ + بلندگوها + امنیت + تماس ها \ No newline at end of file From 0a3212cb70760116f4c9b9fc79f7475545bcdf35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Priit=20J=C3=B5er=C3=BC=C3=BCt?= Date: Wed, 22 May 2024 15:16:36 +0000 Subject: [PATCH 102/159] Translated using Weblate (Estonian) Currently translated at 100.0% (147 of 147 strings) Translation: You Apps/Connect You Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/et/ --- app/src/main/res/values-et/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/values-et/strings.xml b/app/src/main/res/values-et/strings.xml index 6592ca95..0c892c67 100644 --- a/app/src/main/res/values-et/strings.xml +++ b/app/src/main/res/values-et/strings.xml @@ -144,4 +144,6 @@ Muuda telefonihelinat Kuna Connect You pole selles nutiseadmes vaikimisi kasutatav telefoni- ja SMS-sõnumirakendus, siis telefoninumbrite blokeerimine ei ole võimalik Lõpeta telefoninumbri blokeerimine + Otsi kontaktide hulgast + Alusta uut vestlust \ No newline at end of file From 85aaed336d8205cba180b60c159fe74d8abd28fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E0=AE=A4=E0=AE=AE=E0=AE=BF=E0=AE=B4=E0=AF=8D=E0=AE=A8?= =?UTF-8?q?=E0=AF=87=E0=AE=B0=E0=AE=AE=E0=AF=8D?= Date: Thu, 30 May 2024 05:39:57 +0200 Subject: [PATCH 103/159] Added translation using Weblate (Tamil) --- app/src/main/res/values-ta/strings.xml | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 app/src/main/res/values-ta/strings.xml diff --git a/app/src/main/res/values-ta/strings.xml b/app/src/main/res/values-ta/strings.xml new file mode 100644 index 00000000..a6b3daec --- /dev/null +++ b/app/src/main/res/values-ta/strings.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file From 6225bc4083848d6aed109046859bf9413d6c73be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E0=AE=A4=E0=AE=AE=E0=AE=BF=E0=AE=B4=E0=AF=8D=E0=AE=A8?= =?UTF-8?q?=E0=AF=87=E0=AE=B0=E0=AE=AE=E0=AF=8D?= Date: Thu, 30 May 2024 03:45:24 +0000 Subject: [PATCH 104/159] Translated using Weblate (Tamil) Currently translated at 100.0% (147 of 147 strings) Translation: You Apps/Connect You Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/ta/ --- app/src/main/res/values-ta/strings.xml | 149 ++++++++++++++++++++++++- 1 file changed, 148 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/values-ta/strings.xml b/app/src/main/res/values-ta/strings.xml index a6b3daec..9d3f653b 100644 --- a/app/src/main/res/values-ta/strings.xml +++ b/app/src/main/res/values-ta/strings.xml @@ -1,2 +1,149 @@ - \ No newline at end of file + + கடவுச்சொல் + தடுப்பு எண் ஆதரிக்கப்படவில்லை, உங்களை இயல்புநிலை எச்எம்எச்/தொலைபேசி பயன்பாடு அல்ல + எண் தடுக்கப்பட்டது + தேடல் + கிளிப்போர்டுக்கு நகலெடுக்கப்பட்டது + தொடர்பை நீக்கு + நீ சொல்வது உறுதியா? இதை செயல்தவிர்க்க முடியாது! + ரத்துசெய் + சரி + மீட்டமை + தொடர்பை உருவாக்கவும் + மீண்டும் முயற்சிக்கவும் + பங்கு + தொடர்புக்குச் சேர்க்கவும் + புதிய தொடர்பு + குறுக்குவழியை உருவாக்க + டயல் + செய்தி + தொடர்பு + தொடர்புகள் + தொகு + வடிப்பி + மேலும் + மேலும் புலங்களைக் காட்டு + குறைவாகக் காட்டு + தொடர்பைத் தேர்ந்தெடுங்கள் + சேமி + பெயர் காலியாக இருக்க முடியாது! + இறக்குமதி செய்யப்பட்டது. + ஏற்றுமதி. + இங்கு எதுவுமில்லை. + தொலைபேசி + மின்னஞ்சல் + முகவரி + நிகழ்வு + இணையதளம் + புகைப்படம் + தொலைபேசி எண் + வரிசைப்படுத்தும் முறை + கணக்கு வகை + பெயர் + முதல் பெயர் + கடைசி பெயர் + புனைப்பெயர் + அமைப்பு + வகை + வீடு + வேலை + கைபேசி + மற்றொன்று + தனிப்பயன் + கார் + தொலைநகல் வீடு + தொலைநகல் வேலை + உதவியாளர் + பிறந்த நாள் + ஆண்டு + குறிப்பு + முகப்புப்பக்கம் + Ftp + வலைப்பதிவு + குழுக்களை நிர்வகிக்கவும் + அனைவருக்கும் குழுவை நீக்கு + தலைப்பு + புதிய + குழு ஏற்கனவே உள்ளது + குழுக்கள் + சாதனம் + உள்ளக + நகலெடு + நகர்வு + அழி + %1 $ கள் தேர்ந்தெடுக்கப்பட்டன + செய்திகள் + செய்தி அனுப்ப + பதில் + இணைக்க முடியவில்லை. விமானப் பயன்முறை முடக்கப்பட்டுள்ளதா என்பதை உறுதிப்படுத்தவும். + செய்தி மிக நீளமானது! + செய்தியை பல சிறியதாக அனுப்ப விரும்புகிறீர்களா? + தனியார் எச்எம்எச் தரவுத்தளம் + எச்எம்எச் பயன்பாட்டிற்குள் மட்டுமே சேமிக்கப்படும் மற்றும் பிற பயன்பாடுகளில் அணுக முடியாது. + நூலை நீக்கு + செய்தியை நீக்கு + முன்னேற்றத்தில் உள்ளது + ரிங்கிங்… + துண்டித்தல்… + இணைத்தல்… + நீங்கள் %1 $ s ஐ அழைக்க விரும்புகிறீர்களா? + முடக்கு + பேச்சாளர்கள் + VCARD ஐ இறக்குமதி செய்யுங்கள் + ஏற்றுமதி vcard + சிம்மில் இருந்து இறக்குமதி + தேர்ந்தெடுக்கப்பட்ட வி.சி.எஃப் கோப்பை இறக்குமதி செய்ய விரும்புகிறீர்களா? + அமைப்புகள் + தொடக்க தாவல் + கருப்பொருள் + மண்டலம் + ஒளி + இருள் + தானியங்கி காப்புப்பிரதி + காப்பு இடைவெளி + காப்புப்பிரதிகளின் அதிகபட்ச அளவு + அடைவு + இரண்டும் + எதுவுமில்லை + இதர + சுருளில் கீழ் பட்டியை உடைக்கவும் + வண்ணமயமான தொடர்பு சின்னங்கள் + நடத்தை + தோற்றம் + காப்புப்பிரதி + காப்புப்பிரதிகளை சிப் என குறியாக்கவும் + பாதுகாப்பு + பயோமெட்ரிக் ஏற்பு + பயன்பாட்டை அணுகுவதற்கு முன் பயோமெட்ரிக் ஏற்பு (எ.கா. கைரேகை) தேவை. + பற்றி + உரிமம் + நூலாசிரியர் + பதிப்பு + மொழிபெயர்ப்பு + வலைத்தளத்தைச் சேர்க்கவும் + தொலைபேசி எண்ணைச் சேர்க்கவும் + மின்னஞ்சல் சேர்க்கவும் + முகவரியைச் சேர்க்கவும் + குறிப்பு சேர்க்க + நிகழ்வைச் சேர்க்கவும் + திகதி + அமோல்ட் + அழைப்புகள் + வீழ்ச்சி + ஏற்றுக்கொள் + தெரியாத எண் + டயல்பேடு + அழைப்பு முடிந்தது + டயலிங் ... + இருந்து அழைக்கவும் + கீபேட் + தனிப்பயன் ரிங்டோனைத் தேர்ந்தெடுக்கவும் + ரிங்டோனை மாற்றவும் + அண்மைக் கால அழைப்புகள் + தொகுதி எண்கள் + தொகுதி எண் + தடைசெய்யப்பட்ட எண்ணைத் தடைசெய்க + தொடர்புகளைத் தேடுங்கள் + புதிய உரையாடலைத் தொடங்கவும் + \ No newline at end of file From 5e58e8dffcefc41829cef2d920352e3699571ee6 Mon Sep 17 00:00:00 2001 From: Ralf Date: Wed, 12 Jun 2024 05:03:41 +0000 Subject: [PATCH 105/159] Translated using Weblate (German) Currently translated at 99.3% (146 of 147 strings) Translation: You Apps/Connect You Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/de/ --- app/src/main/res/values-de/strings.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index d0075553..12a581a3 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -141,4 +141,10 @@ Nummer blockieren Eine neue Unterhaltung beginnen Kontakte suchen + "Rufnummerblockade wird nicht unterstützt, Connect you ist nicht die Standard SMS/Telefon App" + Rufnummer wieder freigeben + In Bearbeitung + Stumm, lautlos + Nummerneingabe Telefon + Tastatur \ No newline at end of file From 7bf20f6dcc5a9dcaaf1a7c6e46c18fbc294b8487 Mon Sep 17 00:00:00 2001 From: Luna Date: Tue, 11 Jun 2024 19:13:23 +0000 Subject: [PATCH 106/159] Translated using Weblate (Danish) Currently translated at 97.2% (143 of 147 strings) Translation: You Apps/Connect You Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/da/ --- app/src/main/res/values-da/strings.xml | 42 +++++++++++++++++++++----- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index 5e8d443d..00e89104 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -1,7 +1,7 @@ Kopieret til udklipsholder - Annullér + Annuller Nulstil Prøv igen Ny kontakt @@ -9,10 +9,10 @@ Ring Kontakt Kontakter - Filtrér + Filtrer Mere Gem - Navn kan ikke være tomt! + Navn må ikke være tomt! Importeret. Telefon Telefonnummer @@ -24,7 +24,7 @@ Årsdag Note Ny - Gruppe eksisterer allerede + Gruppe findes allerede Grupper Enhed Lokal @@ -48,9 +48,9 @@ Føj til kontakt Vis flere felter Besked - Redigér + Rediger Maks. antal backups - Autor + Udvikler Mappe Diverse Begge @@ -94,7 +94,7 @@ Brugerdefineret Startside FTP - Administrér grupper + Administrer grupper Blog Slet gruppe for alle Titel @@ -114,4 +114,32 @@ Mørk Automatisk backup Start-fane + Vælg brugerdefineret ringetone + Skift ringetone + Seneste opkald + Bloker numre + Blokering af numre understøttes ikke, da Connect You ikke er standardappen for SMS/Telefon + Nummer blokeret + Bloker nummer + Søg i kontakter + Start en ny samtale + Fax hjem + Fax arbejde + Igangværende + Ringer… + Afbryder… + Forbinder… + Sæt på lydløs + Opkald + Afvis + Acceptér + Ukendt Nummer + Vil du ringe til %1$s? + Kræv biometrisk godkendelse (fx fingeraftryk), før appen kan tilgås. + Højtalere + Sikkerhed + Biometrisk Godkendelse + Amoled + Opkald Afsluttet + Opkald fra \ No newline at end of file From 5eb80659c6a7012c205a6f7c48ce423e67e221fa Mon Sep 17 00:00:00 2001 From: ngocanhtve Date: Fri, 28 Jun 2024 05:52:24 +0000 Subject: [PATCH 107/159] Translated using Weblate (Vietnamese) Currently translated at 94.5% (139 of 147 strings) Translation: You Apps/Connect You Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/vi/ --- app/src/main/res/values-vi/strings.xml | 106 +++++++++++++++++++++++-- 1 file changed, 98 insertions(+), 8 deletions(-) diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index e4928585..f51a2a78 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -4,18 +4,18 @@ Di động Khác Tùy chỉnh - Xe + Xe hơi Trợ lí Sinh nhật Kỉ niệm Ghi chú Trang chủ Quản lí các nhóm - Xóa nhóm cho tất cả mọi người + Xóa nhóm cho mọi người Tiêu đề Mới Nhóm đã tồn tại - Nhóm + Các nhóm Thiết bị Sao chép Di chuyển @@ -24,20 +24,20 @@ Nhập vào vCard Xuất ra vCard Cài đặt - Bắt đầu tab + Tab bắt đầu Chủ đề Hệ thống Sáng Tối Tự động sao lưu - Tần số sao lưu + Thời gian sao lưu Số bản sao lưu tối đa Chọn thư mục Cả hai Không Khác - "Thu lại thanh dưới khi cuộn" - Biểu tượng danh bạ nhiều màu + Thu gọn thanh dưới cùng khi cuộn + Biểu tượng liên hệ đầy màu sắc Giấy phép Tác giả Phiên bản @@ -46,8 +46,98 @@ Biệt danh Loại Họ - Tên + Họ Tổ chức Nhà riêng Tìm kiếm + Tin nhắn quá dài! + Hành vi + Diện mạo + Mã hóa các bản sao lưu dưới dạng zip + Mật khẩu + Hình chụp + Tên + Chọn nhạc chuông tùy chỉnh + Chặn số + Chặn số không được hỗ trợ, Connect You không phải là ứng dụng SMS/Điện thoại mặc định + Số bị chặn + Chặn số + Bỏ chặn số + Thay đổi nhạc chuông + Cuộc gọi gần đây + Địa chỉ + Thứ tự sắp xếp + Sự kiện + Tìm danh bạ + Bắt đầu cuộc trò chuyện mới + Chép vào clipboard + Xóa liên hệ + Bạn có chắc không? Hạ thủ là bất quờn! + Hủy + Ừa + Đặt lại + Thử lại + Tạo liên hệ + Thêm vào danh bạ + Chia sẻ + Liên hệ mới + Tạo lối tắt + Liên hệ + Tin nhắn + Chỉnh sửa + Lọc + Hiện ít hơn + Quay số + Nhiều hơn + Hiện nhiều trường hơn + Tin nhắn + Giới thiệu + Sao lưu + Chọn liên hệ + Địa phương + Danh bạ + Tên không thể trống! + Đã nhập. + Đã xuất. + Không có gì ở đây. + Điện thoại + E-mail + Fax tại gia + Fax công việc + Bạn có muốn nhập tệp VCF đã chọn không? + Đổ chuông… + Ngắt kết nối… + Đang kết nối… + Bạn có muốn gọi %1$s không? + Tắt tiếng + Loa ngoài + Nhập từ SIM + Cuộc gọi + Chấp nhận + Số không xác định + Bàn quay số + Từ chối + Thêm sự kiện + Bạn có muốn gửi tin nhắn dưới dạng nhiều tin nhắn nhỏ hơn không? + Cơ sở dữ liệu SMS riêng tư + SMS sẽ chỉ được lưu trữ trong ứng dụng và không thể truy cập được trong các ứng dụng khác. + Xóa chủ đề + Xóa tin nhắn + Lưu + Số điện thoại + Gửi tin nhắn + Không thể kết nối. Hãy bảo đảm chế độ trên máy bay đã tắt. + Hồi đáp + Yêu cầu xác thực sinh trắc học (ví dụ: dấu vân tay) trước khi có thể truy cập ứng dụng. + Bảo vệ + Xác thực sinh trắc học + Thêm số điện thoại + Thêm địa chỉ + Thêm ghi chú + Ngày tháng + Thêm E-mail + Cuộc gọi kết thúc + Đang quay số... + Gọi từ + Bàn phím \ No newline at end of file From 20cc2649337b23781238639c103dc94f233f1989 Mon Sep 17 00:00:00 2001 From: hugoalh Date: Fri, 28 Jun 2024 10:06:02 +0000 Subject: [PATCH 108/159] Translated using Weblate (Chinese (Traditional)) Currently translated at 53.0% (78 of 147 strings) Translation: You Apps/Connect You Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/zh_Hant/ --- app/src/main/res/values-zh-rTW/strings.xml | 37 +++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 7b7fa396..66967542 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -29,7 +29,7 @@ 事件 圖片 顯示更多欄位 - 名稱不能是空的 + 名稱不能為空白! 地址 選擇聯絡人 電話號碼 @@ -42,4 +42,39 @@ 組織 類型 其他 + 排序次序 + 暱稱 + 自訂 + 汽車 + 家庭傳真 + 週年紀念 + 筆記 + 主頁 + FTP + 部落格 + 管理群組 + 為所有人刪除群組 + 標題 + + 群組已經存在 + 群組 + 裝置 + 本地 + 複製 + 移動 + 刪除 + %1$s 已選取 + 訊息 + 傳送訊息 + 回覆 + 私人簡訊資料庫 + 刪除訊息 + 進行中 + 正在響鈴… + 家庭 + 生日 + 流動 + 工作傳真 + 助理 + 工作 \ No newline at end of file From 03ac50d03365781310fe7b4260d7ba84e42dec87 Mon Sep 17 00:00:00 2001 From: Bnyro Date: Wed, 3 Jul 2024 16:15:21 +0200 Subject: [PATCH 109/159] feat: support for contact favorites (closes #401) --- .../contacts/data/database/AppDatabase.kt | 2 +- .../contacts/data/database/DatabaseHolder.kt | 14 +++++-- .../data/database/dao/LocalContactsDao.kt | 3 ++ .../data/database/obj/LocalContact.kt | 3 +- .../contacts/domain/model/ContactData.kt | 3 +- .../contacts/domain/model/FilterOptions.kt | 6 ++- .../domain/repositories/ContactsRepository.kt | 1 + .../repositories/DeviceContactsRepository.kt | 18 ++++++++- .../repositories/LocalContactsRepository.kt | 8 +++- .../presentation/features/FilterDialog.kt | 25 ++++++++++++- .../screens/contact/SingleContactScreen.kt | 37 +++++++++++++------ .../screens/contacts/ContactsScreen.kt | 1 + .../contacts/components/ContactsList.kt | 10 ++--- .../screens/contacts/model/ContactsModel.kt | 7 ++++ .../com/bnyro/contacts/util/Preferences.kt | 1 + .../bnyro/contacts/util/extension/Cursor.kt | 4 ++ app/src/main/res/values/strings.xml | 2 + 17 files changed, 115 insertions(+), 30 deletions(-) diff --git a/app/src/main/java/com/bnyro/contacts/data/database/AppDatabase.kt b/app/src/main/java/com/bnyro/contacts/data/database/AppDatabase.kt index 2e02620d..9399d050 100644 --- a/app/src/main/java/com/bnyro/contacts/data/database/AppDatabase.kt +++ b/app/src/main/java/com/bnyro/contacts/data/database/AppDatabase.kt @@ -16,7 +16,7 @@ import com.bnyro.contacts.data.database.obj.SmsData AutoMigration(3, 4), AutoMigration(4, 5) ], - version = 5 + version = 6 ) abstract class AppDatabase : RoomDatabase() { abstract fun localContactsDao(): LocalContactsDao diff --git a/app/src/main/java/com/bnyro/contacts/data/database/DatabaseHolder.kt b/app/src/main/java/com/bnyro/contacts/data/database/DatabaseHolder.kt index c802b578..9b6784d1 100644 --- a/app/src/main/java/com/bnyro/contacts/data/database/DatabaseHolder.kt +++ b/app/src/main/java/com/bnyro/contacts/data/database/DatabaseHolder.kt @@ -10,23 +10,29 @@ object DatabaseHolder { lateinit var Db: AppDatabase private val MIGRATION_1_2 = object : Migration(1, 2) { - override fun migrate(database: SupportSQLiteDatabase) { - database.execSQL( + override fun migrate(db: SupportSQLiteDatabase) { + db.execSQL( "ALTER TABLE localContacts ADD COLUMN nickName TEXT DEFAULT NULL" ) - database.execSQL( + db.execSQL( "ALTER TABLE localContacts ADD COLUMN organization TEXT DEFAULT NULL" ) } } + private val MIGRATION_5_6 = object : Migration(5, 6) { + override fun migrate(db: SupportSQLiteDatabase) { + db.execSQL("ALTER TABLE localContacts ADD COLUMN favorite INTEGER NOT NULL DEFAULT 0") + } + } + fun init(context: Context) { Db = Room.databaseBuilder( context, AppDatabase::class.java, DB_NAME ) - .addMigrations(MIGRATION_1_2) + .addMigrations(MIGRATION_1_2, MIGRATION_5_6) .build() } } diff --git a/app/src/main/java/com/bnyro/contacts/data/database/dao/LocalContactsDao.kt b/app/src/main/java/com/bnyro/contacts/data/database/dao/LocalContactsDao.kt index e935628c..8970f92c 100644 --- a/app/src/main/java/com/bnyro/contacts/data/database/dao/LocalContactsDao.kt +++ b/app/src/main/java/com/bnyro/contacts/data/database/dao/LocalContactsDao.kt @@ -20,6 +20,9 @@ interface LocalContactsDao { @Insert suspend fun insertData(vararg data: DbDataItem) + @Query("UPDATE localContacts SET favorite = :favorite WHERE id = :id ") + suspend fun setFavorite(id: Long, favorite: Boolean) + @Query("DELETE FROM localContacts WHERE id = :id") suspend fun deleteContactByID(id: Long) diff --git a/app/src/main/java/com/bnyro/contacts/data/database/obj/LocalContact.kt b/app/src/main/java/com/bnyro/contacts/data/database/obj/LocalContact.kt index 5c377696..904b8996 100644 --- a/app/src/main/java/com/bnyro/contacts/data/database/obj/LocalContact.kt +++ b/app/src/main/java/com/bnyro/contacts/data/database/obj/LocalContact.kt @@ -13,5 +13,6 @@ data class LocalContact( @ColumnInfo val surName: String? = null, @ColumnInfo val nickName: String? = null, @ColumnInfo val title: String? = null, - @ColumnInfo val organization: String? = null + @ColumnInfo val organization: String? = null, + @ColumnInfo(defaultValue = "0") val favorite: Boolean = false ) diff --git a/app/src/main/java/com/bnyro/contacts/domain/model/ContactData.kt b/app/src/main/java/com/bnyro/contacts/domain/model/ContactData.kt index 3813548b..47e0f7f5 100644 --- a/app/src/main/java/com/bnyro/contacts/domain/model/ContactData.kt +++ b/app/src/main/java/com/bnyro/contacts/domain/model/ContactData.kt @@ -26,7 +26,8 @@ data class ContactData( var notes: List = listOf(), var groups: List = listOf(), var websites: List = listOf(), - var ringTone: Uri? = null + var ringTone: Uri? = null, + var favorite: Boolean = false ) { val accountIdentifier get() = "$accountType|$accountName" fun getNameBySortOrder(sortOrder: SortOrder): String? { diff --git a/app/src/main/java/com/bnyro/contacts/domain/model/FilterOptions.kt b/app/src/main/java/com/bnyro/contacts/domain/model/FilterOptions.kt index 27ee47ea..fc18bd9b 100644 --- a/app/src/main/java/com/bnyro/contacts/domain/model/FilterOptions.kt +++ b/app/src/main/java/com/bnyro/contacts/domain/model/FilterOptions.kt @@ -6,7 +6,8 @@ import com.bnyro.contacts.util.Preferences data class FilterOptions( var sortOder: SortOrder, var hiddenAccountIdentifiers: List, - var visibleGroups: List + var visibleGroups: List, + var favoritesOnly: Boolean ) { companion object { fun default(): FilterOptions { @@ -15,7 +16,8 @@ data class FilterOptions( Preferences.hiddenAccountsKey, emptySet() )!!.toList() - return FilterOptions(sortOrder, hiddenAccounts, listOf()) + val favoritesOnly = Preferences.getBoolean(Preferences.favoritesOnlyKey, false) + return FilterOptions(sortOrder, hiddenAccounts, listOf(), favoritesOnly) } } } diff --git a/app/src/main/java/com/bnyro/contacts/domain/repositories/ContactsRepository.kt b/app/src/main/java/com/bnyro/contacts/domain/repositories/ContactsRepository.kt index 815bea8a..8fee1ae2 100644 --- a/app/src/main/java/com/bnyro/contacts/domain/repositories/ContactsRepository.kt +++ b/app/src/main/java/com/bnyro/contacts/domain/repositories/ContactsRepository.kt @@ -9,6 +9,7 @@ interface ContactsRepository { suspend fun createContact(contact: ContactData) suspend fun updateContact(contact: ContactData) suspend fun deleteContacts(contacts: List) + suspend fun setFavorite(contact: ContactData, favorite: Boolean) suspend fun getContactList(): List suspend fun loadAdvancedData(contact: ContactData): ContactData fun isAutoBackupEnabled(): Boolean diff --git a/app/src/main/java/com/bnyro/contacts/domain/repositories/DeviceContactsRepository.kt b/app/src/main/java/com/bnyro/contacts/domain/repositories/DeviceContactsRepository.kt index b5cff7fe..12c1164a 100644 --- a/app/src/main/java/com/bnyro/contacts/domain/repositories/DeviceContactsRepository.kt +++ b/app/src/main/java/com/bnyro/contacts/domain/repositories/DeviceContactsRepository.kt @@ -33,6 +33,7 @@ import com.bnyro.contacts.domain.model.ValueWithType import com.bnyro.contacts.util.ContactsHelper import com.bnyro.contacts.util.ImageHelper import com.bnyro.contacts.util.Preferences +import com.bnyro.contacts.util.extension.boolValue import com.bnyro.contacts.util.extension.intValue import com.bnyro.contacts.util.extension.longValue import com.bnyro.contacts.util.extension.notAName @@ -57,7 +58,8 @@ class DeviceContactsRepository(private val context: Context) : ContactsRepositor StructuredName.GIVEN_NAME, StructuredName.FAMILY_NAME, RawContacts.ACCOUNT_TYPE, - RawContacts.ACCOUNT_NAME + RawContacts.ACCOUNT_NAME, + Contacts.STARRED ) private var storedContactGroups: List = emptyList() @@ -104,7 +106,8 @@ class DeviceContactsRepository(private val context: Context) : ContactsRepositor displayName = displayName, alternativeName = alternativeName, firstName = firstName, - surName = surName + surName = surName, + favorite = it.boolValue(Contacts.STARRED) ?: false ) contactList.add(contact) @@ -288,6 +291,17 @@ class DeviceContactsRepository(private val context: Context) : ContactsRepositor } } + override suspend fun setFavorite(contact: ContactData, favorite: Boolean) = withContext(Dispatchers.IO) { + ContentProviderOperation.newUpdate(RawContacts.CONTENT_URI).apply { + val selection = "${RawContacts.CONTACT_ID} = ?" + val selectionArgs = arrayOf(contact.rawContactId.toString()) + withSelection(selection, selectionArgs) + withValue(Contacts.STARRED, if (contact.favorite) 1 else 0) + }.build() + .let { contentResolver.applyBatch(AUTHORITY, arrayListOf(it)) } + return@withContext + } + @SuppressLint("MissingPermission") @RequiresPermission(Manifest.permission.WRITE_CONTACTS) override suspend fun createContact(contact: ContactData) { diff --git a/app/src/main/java/com/bnyro/contacts/domain/repositories/LocalContactsRepository.kt b/app/src/main/java/com/bnyro/contacts/domain/repositories/LocalContactsRepository.kt index 7e0e45e5..7f22832f 100644 --- a/app/src/main/java/com/bnyro/contacts/domain/repositories/LocalContactsRepository.kt +++ b/app/src/main/java/com/bnyro/contacts/domain/repositories/LocalContactsRepository.kt @@ -33,7 +33,8 @@ class LocalContactsRepository(context: Context) : ContactsRepository { surName = contact.surName, nickName = contact.nickName, title = contact.title, - organization = contact.organization + organization = contact.organization, + favorite = contact.favorite ) val contactId = DatabaseHolder.Db.localContactsDao().insertContact(localContact) val dataItems = listOf( @@ -75,6 +76,10 @@ class LocalContactsRepository(context: Context) : ContactsRepository { } } + override suspend fun setFavorite(contact: ContactData, favorite: Boolean) = withContext(Dispatchers.IO) { + DatabaseHolder.Db.localContactsDao().setFavorite(contact.contactId, favorite) + } + override suspend fun getContactList(): List = withContext(Dispatchers.IO) { DatabaseHolder.Db.localContactsDao().getAll().pmap { val profileImage = getProfileImage(it.contact.id) @@ -89,6 +94,7 @@ class LocalContactsRepository(context: Context) : ContactsRepository { nickName = it.contact.nickName, title = it.contact.title, organization = it.contact.organization, + favorite = it.contact.favorite, photo = profileImage, thumbnail = profileImage, numbers = it.dataItems.toValueWithType(DataCategory.NUMBER), diff --git a/app/src/main/java/com/bnyro/contacts/presentation/features/FilterDialog.kt b/app/src/main/java/com/bnyro/contacts/presentation/features/FilterDialog.kt index 0c864af8..9117ecfc 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/features/FilterDialog.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/features/FilterDialog.kt @@ -1,17 +1,23 @@ package com.bnyro.contacts.presentation.features import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.height import androidx.compose.material3.AlertDialog +import androidx.compose.material3.Checkbox +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp import com.bnyro.contacts.R import com.bnyro.contacts.domain.enums.SortOrder import com.bnyro.contacts.domain.model.AccountType @@ -38,12 +44,16 @@ fun FilterDialog( var visibleGroups by remember { mutableStateOf(initialFilters.visibleGroups) } + + var favoritesOnly by remember { + mutableStateOf(initialFilters.favoritesOnly) + } AlertDialog( onDismissRequest = onDismissRequest, confirmButton = { DialogButton(text = stringResource(R.string.okay)) { - val options = FilterOptions(sortOrder, hiddenAccountNames, visibleGroups) + val options = FilterOptions(sortOrder, hiddenAccountNames, visibleGroups, favoritesOnly) onFilterChanged.invoke(options) onDismissRequest.invoke() } @@ -105,6 +115,19 @@ fun FilterDialog( } ) } + Spacer(modifier = Modifier.height(10.dp)) + Row( + verticalAlignment = Alignment.CenterVertically + ) { + Text( + modifier = Modifier.weight(1f), + text = stringResource(id = R.string.favorites_only), + fontSize = 18.sp, + fontWeight = FontWeight.Bold + ) + + Checkbox(checked = favoritesOnly, onCheckedChange = { favoritesOnly = it }) + } } } ) diff --git a/app/src/main/java/com/bnyro/contacts/presentation/screens/contact/SingleContactScreen.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/contact/SingleContactScreen.kt index 2008adb1..d90d30ca 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/screens/contact/SingleContactScreen.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/contact/SingleContactScreen.kt @@ -20,11 +20,11 @@ import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ArrowBack import androidx.compose.material.icons.filled.Call -import androidx.compose.material.icons.filled.Delete 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.filled.Star +import androidx.compose.material.icons.filled.StarOutline import androidx.compose.material.icons.rounded.MoreVert import androidx.compose.material3.DropdownMenu import androidx.compose.material3.DropdownMenuItem @@ -91,6 +91,9 @@ fun SingleContactScreen(contact: ContactData, viewModel: ContactsModel, onClose: var showShareDialog by remember { mutableStateOf(false) } + var isFavorite by remember { + mutableStateOf(contact.favorite) + } val ringtonePicker = rememberLauncherForActivityResult(contract = RingtonePickContract()) { uri -> @@ -114,10 +117,12 @@ fun SingleContactScreen(contact: ContactData, viewModel: ContactsModel, onClose: }, actions = { ClickableIcon( - icon = Icons.Default.Shortcut, - contentDescription = R.string.create_shortcut + icon = if (isFavorite) Icons.Default.Star else Icons.Default.StarOutline, + contentDescription = R.string.favorite ) { - showShortcutDialog = true + isFavorite = !isFavorite + contact.favorite = isFavorite + viewModel.setFavorite(context, contact, isFavorite) } ClickableIcon( icon = Icons.Default.Edit, @@ -125,12 +130,6 @@ fun SingleContactScreen(contact: ContactData, viewModel: ContactsModel, onClose: ) { showEditor = true } - ClickableIcon( - icon = Icons.Default.Delete, - contentDescription = R.string.delete - ) { - showDelete = true - } Box { var showMore by remember { mutableStateOf(false) } ClickableIcon( @@ -153,6 +152,22 @@ fun SingleContactScreen(contact: ContactData, viewModel: ContactsModel, onClose: ringtonePicker.launch() } ) + DropdownMenuItem( + text = { + Text(text = stringResource(R.string.create_shortcut)) + }, + onClick = { + showShortcutDialog = true + } + ) + DropdownMenuItem( + text = { + Text(text = stringResource(R.string.delete_contact)) + }, + onClick = { + showDelete = true + } + ) } } } diff --git a/app/src/main/java/com/bnyro/contacts/presentation/screens/contacts/ContactsScreen.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/contacts/ContactsScreen.kt index 04543b0c..122b9d52 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/screens/contacts/ContactsScreen.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/contacts/ContactsScreen.kt @@ -369,6 +369,7 @@ fun ContactsPage( Preferences.edit { putInt(Preferences.sortOrderKey, it.sortOder.ordinal) putStringSet(Preferences.hiddenAccountsKey, it.hiddenAccountIdentifiers.toSet()) + putBoolean(Preferences.favoritesOnlyKey, it.favoritesOnly) } filterOptions = it }, diff --git a/app/src/main/java/com/bnyro/contacts/presentation/screens/contacts/components/ContactsList.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/contacts/components/ContactsList.kt index 8ce85e76..8dde187b 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/screens/contacts/components/ContactsList.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/contacts/components/ContactsList.kt @@ -32,13 +32,11 @@ fun ContactsList( contacts.asSequence().filter { !filterOptions.hiddenAccountIdentifiers.contains(it.accountIdentifier) }.filter { - if (filterOptions.visibleGroups.isEmpty()) { - true - } else { - filterOptions.visibleGroups.any { group -> - it.groups.contains(group) - } + filterOptions.visibleGroups.isEmpty() || filterOptions.visibleGroups.any { group -> + it.groups.contains(group) } + }.filter { + !filterOptions.favoritesOnly || it.favorite }.sortedBy { it.getNameBySortOrder(filterOptions.sortOder) }.groupBy { diff --git a/app/src/main/java/com/bnyro/contacts/presentation/screens/contacts/model/ContactsModel.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/contacts/model/ContactsModel.kt index f7c13627..9da6a866 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/screens/contacts/model/ContactsModel.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/contacts/model/ContactsModel.kt @@ -156,6 +156,13 @@ class ContactsModel( } } + fun setFavorite(context: Context, contact: ContactData, favorite: Boolean) { + viewModelScope.launch { + contactsRepository.setFavorite(contact, favorite) + loadContacts(context) + } + } + fun copyContacts(context: Context, contacts: List) { val otherHelper = when (contactsRepository) { is DeviceContactsRepository -> localContactsRepository diff --git a/app/src/main/java/com/bnyro/contacts/util/Preferences.kt b/app/src/main/java/com/bnyro/contacts/util/Preferences.kt index a981497d..137b8d45 100644 --- a/app/src/main/java/com/bnyro/contacts/util/Preferences.kt +++ b/app/src/main/java/com/bnyro/contacts/util/Preferences.kt @@ -30,6 +30,7 @@ object Preferences { const val storeSmsLocallyKey = "storeSmsLocally" const val lastChosenAccount = "lastChosenAccount" const val biometricAuthKey = "biometricAuth" + const val favoritesOnlyKey = "favoritesOnly" fun init(context: Context) { preferences = context.getSharedPreferences(prefFile, Context.MODE_PRIVATE) diff --git a/app/src/main/java/com/bnyro/contacts/util/extension/Cursor.kt b/app/src/main/java/com/bnyro/contacts/util/extension/Cursor.kt index a581d52d..efb99d8b 100644 --- a/app/src/main/java/com/bnyro/contacts/util/extension/Cursor.kt +++ b/app/src/main/java/com/bnyro/contacts/util/extension/Cursor.kt @@ -16,3 +16,7 @@ fun Cursor.intValue(index: String): Int? { fun Cursor.longValue(index: String): Long? { return getLongOrNull(getColumnIndex(index)) } + +fun Cursor.boolValue(index: String): Boolean? { + return intValue(index)?.let { it == 1 } +} \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a13222df..62d130da 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -39,6 +39,7 @@ Website Photo Phone number + Favorite Sort order Account type @@ -47,6 +48,7 @@ Last name Nickname Organization + Favorites only Type Home From eec6accae9d3f84bc20bcf8a73cd5a9682244b38 Mon Sep 17 00:00:00 2001 From: Bnyro Date: Wed, 3 Jul 2024 17:31:03 +0200 Subject: [PATCH 110/159] chore: cleanup DeviceContactsRepository.kt --- .../repositories/DeviceContactsRepository.kt | 40 +++++++++---------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/app/src/main/java/com/bnyro/contacts/domain/repositories/DeviceContactsRepository.kt b/app/src/main/java/com/bnyro/contacts/domain/repositories/DeviceContactsRepository.kt index 12c1164a..03fd695c 100644 --- a/app/src/main/java/com/bnyro/contacts/domain/repositories/DeviceContactsRepository.kt +++ b/app/src/main/java/com/bnyro/contacts/domain/repositories/DeviceContactsRepository.kt @@ -6,7 +6,6 @@ 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 @@ -48,8 +47,6 @@ class DeviceContactsRepository(private val context: Context) : ContactsRepositor private val contentResolver = context.contentResolver private val contentUri = Data.CONTENT_URI - private val authority = AUTHORITY - private val projection = arrayOf( Data.RAW_CONTACT_ID, RawContacts.CONTACT_ID, @@ -291,16 +288,17 @@ class DeviceContactsRepository(private val context: Context) : ContactsRepositor } } - override suspend fun setFavorite(contact: ContactData, favorite: Boolean) = withContext(Dispatchers.IO) { - ContentProviderOperation.newUpdate(RawContacts.CONTENT_URI).apply { - val selection = "${RawContacts.CONTACT_ID} = ?" - val selectionArgs = arrayOf(contact.rawContactId.toString()) - withSelection(selection, selectionArgs) - withValue(Contacts.STARRED, if (contact.favorite) 1 else 0) - }.build() - .let { contentResolver.applyBatch(AUTHORITY, arrayListOf(it)) } - return@withContext - } + override suspend fun setFavorite(contact: ContactData, favorite: Boolean): Unit = + withContext(Dispatchers.IO) { + val op = ContentProviderOperation.newUpdate(RawContacts.CONTENT_URI).apply { + val selection = "${RawContacts.CONTACT_ID} = ?" + val selectionArgs = arrayOf(contact.rawContactId.toString()) + withSelection(selection, selectionArgs) + withValue(Contacts.STARRED, if (contact.favorite) 1 else 0) + }.build() + + contentResolver.applyBatch(AUTHORITY, arrayListOf(op)) + } @SuppressLint("MissingPermission") @RequiresPermission(Manifest.permission.WRITE_CONTACTS) @@ -428,10 +426,8 @@ class DeviceContactsRepository(private val context: Context) : ContactsRepositor fun getAccountTypes(): List { 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) } @@ -566,15 +562,15 @@ class DeviceContactsRepository(private val context: Context) : ContactsRepositor }.build() } - suspend fun updateContactRingTone(contactId: String, ringtoneUri: Uri) = + suspend fun updateContactRingTone(contactId: String, ringtoneUri: Uri): Unit = withContext(Dispatchers.IO) { val contactUri = Uri.withAppendedPath(Contacts.CONTENT_URI, contactId) - val values = ContentValues().apply { - put(Contacts.CUSTOM_RINGTONE, ringtoneUri.toString()) - } + val op = ContentProviderOperation.newUpdate(contactUri) + .withValue(Contacts.CUSTOM_RINGTONE, ringtoneUri.toString()) + .build() - contentResolver.update(contactUri, values, null, null) + contentResolver.applyBatch(AUTHORITY, arrayListOf(op)) } companion object { From 682301a5ded42e94c89d2874f18b7c46f181ee2e Mon Sep 17 00:00:00 2001 From: Bnyro Date: Tue, 8 Oct 2024 18:00:55 +0200 Subject: [PATCH 111/159] fix: app resets on orientation change closes #409 closes #410 closes #411 closes #412 --- .idea/compiler.xml | 2 +- .idea/gradle.xml | 1 + .idea/misc.xml | 3 ++- .../contacts/navigation/HomeNavContainer.kt | 10 +++++++++- .../com/bnyro/contacts/navigation/NavRoutes.kt | 2 +- .../presentation/components/TopBarMoreMenu.kt | 4 ++-- .../screens/contact/SingleContactScreen.kt | 13 +++++++------ .../screens/contacts/ContactsScreen.kt | 16 +++++++++------- .../screens/contacts/components/ContactItem.kt | 3 ++- .../presentation/screens/sms/SmsListScreen.kt | 10 ++++++---- 10 files changed, 40 insertions(+), 24 deletions(-) diff --git a/.idea/compiler.xml b/.idea/compiler.xml index b589d56e..b86273d9 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -1,6 +1,6 @@ - + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml index 0897082f..7b3006b6 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -4,6 +4,7 @@ - + diff --git a/app/src/main/java/com/bnyro/contacts/navigation/HomeNavContainer.kt b/app/src/main/java/com/bnyro/contacts/navigation/HomeNavContainer.kt index 5d403168..f90ddc03 100644 --- a/app/src/main/java/com/bnyro/contacts/navigation/HomeNavContainer.kt +++ b/app/src/main/java/com/bnyro/contacts/navigation/HomeNavContainer.kt @@ -13,9 +13,12 @@ import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.MutableState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.saveable.Saver +import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalConfiguration @@ -28,6 +31,11 @@ import com.bnyro.contacts.presentation.screens.contacts.model.ContactsModel import com.bnyro.contacts.presentation.screens.settings.model.ThemeModel import com.bnyro.contacts.presentation.screens.sms.model.SmsModel +val RouteSaver = Saver, Int>( + save = { HomeRoutes.all.indexOfFirst { item -> item.route == it.value } }, + restore = { mutableStateOf(HomeRoutes.all[it].route) } +) + @Composable fun HomeNavContainer( initialTab: HomeRoutes, @@ -38,7 +46,7 @@ fun HomeNavContainer( ) { val navController = rememberNavController() - var selectedRoute by remember { + var selectedRoute by rememberSaveable(saver = RouteSaver) { mutableStateOf(initialTab) } diff --git a/app/src/main/java/com/bnyro/contacts/navigation/NavRoutes.kt b/app/src/main/java/com/bnyro/contacts/navigation/NavRoutes.kt index eb4745e4..07dc2e26 100644 --- a/app/src/main/java/com/bnyro/contacts/navigation/NavRoutes.kt +++ b/app/src/main/java/com/bnyro/contacts/navigation/NavRoutes.kt @@ -61,7 +61,7 @@ sealed class NavRoutes { } @Serializable -sealed class HomeRoutes() { +sealed class HomeRoutes { @Serializable data class Phone( val phoneNumber: String? = null diff --git a/app/src/main/java/com/bnyro/contacts/presentation/components/TopBarMoreMenu.kt b/app/src/main/java/com/bnyro/contacts/presentation/components/TopBarMoreMenu.kt index 2e8be517..92928163 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/components/TopBarMoreMenu.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/components/TopBarMoreMenu.kt @@ -5,7 +5,7 @@ import androidx.compose.material.icons.filled.MoreVert import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember +import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import com.bnyro.contacts.R @@ -14,7 +14,7 @@ fun TopBarMoreMenu( options: List = emptyList(), onOptionClick: (Int) -> Unit = {} ) { - var expandedOptions by remember { + var expandedOptions by rememberSaveable { mutableStateOf(false) } diff --git a/app/src/main/java/com/bnyro/contacts/presentation/screens/contact/SingleContactScreen.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/contact/SingleContactScreen.kt index d90d30ca..7ae0b2c3 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/screens/contact/SingleContactScreen.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/contact/SingleContactScreen.kt @@ -37,6 +37,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -76,22 +77,22 @@ import com.bnyro.contacts.util.RingtonePickContract @Composable fun SingleContactScreen(contact: ContactData, viewModel: ContactsModel, onClose: () -> Unit) { val context = LocalContext.current - var showDelete by remember { + var showDelete by rememberSaveable { mutableStateOf(false) } - var showEditor by remember { + var showEditor by rememberSaveable { mutableStateOf(false) } - var showZoomablePhoto by remember { + var showZoomablePhoto by rememberSaveable { mutableStateOf(false) } - var showShortcutDialog by remember { + var showShortcutDialog by rememberSaveable { mutableStateOf(false) } - var showShareDialog by remember { + var showShareDialog by rememberSaveable { mutableStateOf(false) } - var isFavorite by remember { + var isFavorite by rememberSaveable { mutableStateOf(contact.favorite) } diff --git a/app/src/main/java/com/bnyro/contacts/presentation/screens/contacts/ContactsScreen.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/contacts/ContactsScreen.kt index 122b9d52..2e9c03af 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/screens/contacts/ContactsScreen.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/contacts/ContactsScreen.kt @@ -37,6 +37,7 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -81,11 +82,11 @@ fun ContactsPage( mutableStateOf(viewModel.initialContactData) } - var showEditorScreen by remember { + var showEditorScreen by rememberSaveable { mutableStateOf(false) } - var showDelete by remember { + var showDelete by rememberSaveable { mutableStateOf(false) } @@ -93,15 +94,15 @@ fun ContactsPage( mutableStateOf(FilterOptions.default()) } - var showSearch by remember { + var showSearch by rememberSaveable { mutableStateOf(false) } - var showFilterDialog by remember { + var showFilterDialog by rememberSaveable { mutableStateOf(false) } - var showImportSimDialog by remember { + var showImportSimDialog by rememberSaveable { mutableStateOf(false) } @@ -143,7 +144,8 @@ fun ContactsPage( true -> { TopAppBar( title = { - var expanded by remember { mutableStateOf(false) } + var expanded by rememberSaveable { mutableStateOf(false) } + Row( Modifier .padding(8.dp) @@ -167,7 +169,7 @@ fun ContactsPage( expanded = expanded, onDismissRequest = { expanded = false } ) { - ContactsSource.values().forEachIndexed { index, source -> + ContactsSource.entries.forEachIndexed { index, source -> DropdownMenuItem( text = { Text(stringResource(id = source.stringRes)) }, onClick = { diff --git a/app/src/main/java/com/bnyro/contacts/presentation/screens/contacts/components/ContactItem.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/contacts/components/ContactItem.kt index 989944d3..b555891a 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/screens/contacts/components/ContactItem.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/contacts/components/ContactItem.kt @@ -24,6 +24,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -54,7 +55,7 @@ fun ContactItem( val viewModel: ContactsModel = viewModel(factory = ContactsModel.Factory) val themeModel: ThemeModel = viewModel() - var showContactScreen by remember { + var showContactScreen by rememberSaveable { mutableStateOf(false) } diff --git a/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/SmsListScreen.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/SmsListScreen.kt index d19d9c7d..eb9c6a07 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/SmsListScreen.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/SmsListScreen.kt @@ -37,6 +37,7 @@ import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -77,14 +78,14 @@ fun SmsListScreen( onClickMessage: (address: String, contactData: ContactData?) -> Unit ) { val themeModel: ThemeModel = viewModel() - var showNumberPicker by remember { + var showNumberPicker by rememberSaveable { mutableStateOf(false) } - var showSearch by remember { + var showSearch by rememberSaveable { mutableStateOf(false) } - var selectedThread by remember { + var selectedThread by rememberSaveable { mutableStateOf(null) } Scaffold( @@ -114,7 +115,8 @@ fun SmsListScreen( onNavigate.invoke(NavRoutes.About) } } - }) + } + ) } ) }, From 25eb2305b22c7a7306fee7bb66fd14041a48963e Mon Sep 17 00:00:00 2001 From: Bnyro Date: Tue, 8 Oct 2024 18:33:21 +0200 Subject: [PATCH 112/159] fix: vcard export of partial birthday/anniversary dates (closes #406) --- .../6.json | 178 ++++++++++++++++++ .../com/bnyro/contacts/util/VcardHelper.kt | 27 +-- 2 files changed, 193 insertions(+), 12 deletions(-) create mode 100644 app/schemas/com.bnyro.contacts.data.database.AppDatabase/6.json diff --git a/app/schemas/com.bnyro.contacts.data.database.AppDatabase/6.json b/app/schemas/com.bnyro.contacts.data.database.AppDatabase/6.json new file mode 100644 index 00000000..668ea914 --- /dev/null +++ b/app/schemas/com.bnyro.contacts.data.database.AppDatabase/6.json @@ -0,0 +1,178 @@ +{ + "formatVersion": 1, + "database": { + "version": 6, + "identityHash": "45dcecae89c80128ab3c91537794287f", + "entities": [ + { + "tableName": "localContacts", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `displayName` TEXT, `firstName` TEXT, `surName` TEXT, `nickName` TEXT, `title` TEXT, `organization` TEXT, `favorite` INTEGER NOT NULL DEFAULT 0)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "displayName", + "columnName": "displayName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firstName", + "columnName": "firstName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "surName", + "columnName": "surName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "nickName", + "columnName": "nickName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "organization", + "columnName": "organization", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "favorite", + "columnName": "favorite", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "valuableTypes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `contactId` INTEGER NOT NULL, `category` INTEGER NOT NULL, `value` TEXT NOT NULL, `type` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "contactId", + "columnName": "contactId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "localSms", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `address` TEXT NOT NULL, `body` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, `threadId` INTEGER NOT NULL, `type` INTEGER NOT NULL, `simNumber` INTEGER DEFAULT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "body", + "columnName": "body", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "threadId", + "columnName": "threadId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "simNumber", + "columnName": "simNumber", + "affinity": "INTEGER", + "notNull": false, + "defaultValue": "NULL" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '45dcecae89c80128ab3c91537794287f')" + ] + } +} \ No newline at end of file diff --git a/app/src/main/java/com/bnyro/contacts/util/VcardHelper.kt b/app/src/main/java/com/bnyro/contacts/util/VcardHelper.kt index 65ac0ea0..2877fbfc 100644 --- a/app/src/main/java/com/bnyro/contacts/util/VcardHelper.kt +++ b/app/src/main/java/com/bnyro/contacts/util/VcardHelper.kt @@ -17,7 +17,7 @@ import ezvcard.property.Birthday import ezvcard.property.FormattedName import ezvcard.property.Photo import ezvcard.property.StructuredName -import java.util.Date +import ezvcard.util.PartialDate object VcardHelper { fun exportVcard(contacts: List): String { @@ -77,19 +77,16 @@ object VcardHelper { contact.events .filter { it.value.isNotBlank() } .forEach { event -> - when (event.type) { - ContactsContract.CommonDataKinds.Event.TYPE_BIRTHDAY -> { - CalendarUtils.dateToMillis(event.value)?.let { time -> - birthday = Birthday(Date(time)) + when (event.type) { + ContactsContract.CommonDataKinds.Event.TYPE_BIRTHDAY -> { + birthday = Birthday(PartialDate.parse(event.value)) } - } - ContactsContract.CommonDataKinds.Event.TYPE_ANNIVERSARY -> { - CalendarUtils.dateToMillis(event.value)?.let { time -> - anniversary = Anniversary(Date(time)) + + ContactsContract.CommonDataKinds.Event.TYPE_ANNIVERSARY -> { + anniversary = Anniversary(PartialDate.parse(event.value)) } } } - } (contact.photo ?: contact.thumbnail)?.let { val photo = Photo(ImageHelper.bitmapToByteArray(it), ImageType.PNG) addPhoto(photo) @@ -148,12 +145,18 @@ object VcardHelper { }, events = it.anniversaries.map { anniversary -> ValueWithType( - CalendarUtils.millisToDate(anniversary.date.time, formatter = CalendarUtils.isoDateFormat), + CalendarUtils.millisToDate( + anniversary.date.time, + formatter = CalendarUtils.isoDateFormat + ), ContactsContract.CommonDataKinds.Event.TYPE_BIRTHDAY ) } + it.birthdays.map { birthday -> ValueWithType( - CalendarUtils.millisToDate(birthday.date.time, formatter = CalendarUtils.isoDateFormat), + CalendarUtils.millisToDate( + birthday.date.time, + formatter = CalendarUtils.isoDateFormat + ), ContactsContract.CommonDataKinds.Event.TYPE_ANNIVERSARY ) }, From d0a02186fc5e2bed3d81b61a6cff787d970c8daa Mon Sep 17 00:00:00 2001 From: Bnyro Date: Sun, 20 Oct 2024 13:24:29 +0200 Subject: [PATCH 113/159] feat: support for sms imports/exports (closes #405) --- .../contacts/data/database/obj/SmsData.kt | 12 ++++- .../bnyro/contacts/domain/model/SmsBackup.kt | 28 ++++++++++ .../presentation/screens/sms/SmsListScreen.kt | 54 ++++++++++++++++++- .../com/bnyro/contacts/util/ExportHelper.kt | 31 +++++++++++ .../bnyro/contacts/util/extension/Toast.kt | 6 +++ app/src/main/res/values/strings.xml | 2 + 6 files changed, 131 insertions(+), 2 deletions(-) create mode 100644 app/src/main/java/com/bnyro/contacts/domain/model/SmsBackup.kt diff --git a/app/src/main/java/com/bnyro/contacts/data/database/obj/SmsData.kt b/app/src/main/java/com/bnyro/contacts/data/database/obj/SmsData.kt index 2da0e13d..c15af74c 100644 --- a/app/src/main/java/com/bnyro/contacts/data/database/obj/SmsData.kt +++ b/app/src/main/java/com/bnyro/contacts/data/database/obj/SmsData.kt @@ -3,6 +3,7 @@ package com.bnyro.contacts.data.database.obj import androidx.room.ColumnInfo import androidx.room.Entity import androidx.room.PrimaryKey +import com.bnyro.contacts.domain.model.SmsBackup @Entity(tableName = "localSms") data class SmsData( @@ -13,4 +14,13 @@ data class SmsData( @ColumnInfo var threadId: Long = 0, @ColumnInfo var type: Int = 0, @ColumnInfo(defaultValue = "NULL") var simNumber: Int? = null -) +) { + fun toSmsBackup() = SmsBackup( + subscriptionId = simNumber?.toLong() ?: 0, + address = address, + body = body, + date = timestamp, + dateSent = timestamp, + type = type, + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/bnyro/contacts/domain/model/SmsBackup.kt b/app/src/main/java/com/bnyro/contacts/domain/model/SmsBackup.kt new file mode 100644 index 00000000..35e947b7 --- /dev/null +++ b/app/src/main/java/com/bnyro/contacts/domain/model/SmsBackup.kt @@ -0,0 +1,28 @@ +package com.bnyro.contacts.domain.model + +import com.bnyro.contacts.data.database.obj.SmsData +import kotlinx.serialization.Serializable + +@Serializable +data class SmsBackup( + val subscriptionId: Long, + val address: String, + val body: String?, + val date: Long, + val dateSent: Long, + val type: Int, + val locked: Int = 1, + val protocol: String? = "0", + val read: Int = 0, + val status: Int = -1, + val serviceCenter: String? = null, + val backupType: String = "sms", +) { + fun toSmsData() = SmsData( + address = address, + body = body.orEmpty(), + timestamp = date, + simNumber = subscriptionId.toInt(), + type = type, + ) +} diff --git a/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/SmsListScreen.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/SmsListScreen.kt index eb9c6a07..b844a320 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/SmsListScreen.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/SmsListScreen.kt @@ -3,6 +3,9 @@ package com.bnyro.contacts.presentation.screens.sms import android.annotation.SuppressLint import android.os.Build import android.provider.BlockedNumberContract +import android.util.Log +import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box @@ -37,6 +40,7 @@ import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment @@ -66,7 +70,12 @@ import com.bnyro.contacts.presentation.screens.settings.model.ThemeModel import com.bnyro.contacts.presentation.screens.sms.components.SmsSearchScreen import com.bnyro.contacts.presentation.screens.sms.components.SmsThreadItem import com.bnyro.contacts.presentation.screens.sms.model.SmsModel +import com.bnyro.contacts.util.CalendarUtils +import com.bnyro.contacts.util.ExportHelper import com.bnyro.contacts.util.IntentHelper +import com.bnyro.contacts.util.extension.toast +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -78,6 +87,9 @@ fun SmsListScreen( onClickMessage: (address: String, contactData: ContactData?) -> Unit ) { val themeModel: ThemeModel = viewModel() + val context = LocalContext.current + val scope = rememberCoroutineScope() + var showNumberPicker by rememberSaveable { mutableStateOf(false) } @@ -88,6 +100,35 @@ fun SmsListScreen( var selectedThread by rememberSaveable { mutableStateOf(null) } + + val importSmsLauncher = + rememberLauncherForActivityResult(ActivityResultContracts.OpenDocument()) { uri -> + scope.launch(Dispatchers.IO) { + try { + ExportHelper(context, contactsModel.contactsRepository) + .importSms(uri ?: return@launch, smsModel.app.smsRepo) + context.toast(R.string.import_success) + } catch (e: Exception) { + Log.e("Failed to import SMS", e.stackTraceToString()) + context.toast(e.message.orEmpty()) + } + } + } + + val exportSmsLauncher = + rememberLauncherForActivityResult(ActivityResultContracts.CreateDocument("application/json")) { uri -> + scope.launch(Dispatchers.IO) { + try { + ExportHelper(context, contactsModel.contactsRepository) + .exportSms(uri ?: return@launch, smsModel.smsList.value) + context.toast(R.string.export_success) + } catch (e: Exception) { + Log.e("Failed to export SMS", e.stackTraceToString()) + context.toast(e.message.orEmpty()) + } + } + } + Scaffold( topBar = { TopAppBar( @@ -102,16 +143,27 @@ fun SmsListScreen( showSearch = true } TopBarMoreMenu(options = listOf( + stringResource(R.string.import_sms), + stringResource(R.string.export_sms), stringResource(R.string.settings), stringResource(R.string.about) ), onOptionClick = { index -> when (index) { 0 -> { - onNavigate.invoke(NavRoutes.Settings) + importSmsLauncher.launch(arrayOf("application/json")) } 1 -> { + val dateTime = CalendarUtils.getCurrentDateTime() + exportSmsLauncher.launch("sms-backup-${dateTime}.json") + } + + 2 -> { + onNavigate.invoke(NavRoutes.Settings) + } + + 3 -> { onNavigate.invoke(NavRoutes.About) } } diff --git a/app/src/main/java/com/bnyro/contacts/util/ExportHelper.kt b/app/src/main/java/com/bnyro/contacts/util/ExportHelper.kt index e6d71874..ffb1d2a1 100644 --- a/app/src/main/java/com/bnyro/contacts/util/ExportHelper.kt +++ b/app/src/main/java/com/bnyro/contacts/util/ExportHelper.kt @@ -4,17 +4,30 @@ import android.annotation.SuppressLint import android.content.Context import android.net.Uri import androidx.core.content.FileProvider +import com.bnyro.contacts.data.database.obj.SmsData import com.bnyro.contacts.domain.model.ContactData +import com.bnyro.contacts.domain.model.SmsBackup import com.bnyro.contacts.domain.repositories.ContactsRepository +import com.bnyro.contacts.domain.repositories.SmsRepository import com.bnyro.contacts.util.extension.pmap import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.decodeFromStream +import kotlinx.serialization.json.encodeToStream import java.io.File +import java.io.IOException class ExportHelper( private val context: Context, private val contactsRepository: ContactsRepository ) { + private val json = Json { + ignoreUnknownKeys = true + coerceInputValues = true + encodeDefaults = true + } + private val contentResolver = context.contentResolver private val encryptBackups get() = Preferences.getBoolean(Preferences.encryptBackupsKey, false) private val password get() = Preferences.getString( @@ -81,6 +94,24 @@ class ExportHelper( ) } + suspend fun importSms(uri: Uri, smsRepository: SmsRepository) { + val smsList = context.contentResolver.openInputStream(uri)?.use { inputStream -> + json.decodeFromStream>(inputStream) + } ?: throw IOException() + + for (sms in smsList) { + if (sms.backupType != "sms") continue + + smsRepository.persistSms(context, sms.toSmsData()) + } + } + + suspend fun exportSms(uri: Uri, sms: List) { + context.contentResolver.openOutputStream(uri)?.use { outputStream -> + json.encodeToStream(sms.map(SmsData::toSmsBackup), outputStream) + } + } + companion object { private const val CONTACTS_EXPORT_FILE_SUFFIX = "contacts-export" } diff --git a/app/src/main/java/com/bnyro/contacts/util/extension/Toast.kt b/app/src/main/java/com/bnyro/contacts/util/extension/Toast.kt index e1d23a7c..0e52a239 100644 --- a/app/src/main/java/com/bnyro/contacts/util/extension/Toast.kt +++ b/app/src/main/java/com/bnyro/contacts/util/extension/Toast.kt @@ -11,3 +11,9 @@ fun Context.toast(@StringRes text: Int) { Toast.makeText(this, text, Toast.LENGTH_SHORT).show() } } + +fun Context.toast(text: String) { + Handler(Looper.getMainLooper()).post { + Toast.makeText(this, text, Toast.LENGTH_SHORT).show() + } +} diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 62d130da..26786b55 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -102,6 +102,8 @@ Import vCard Export vCard + Import SMS + Export SMS Import from SIM Do you want to import the selected VCF file? From 59fef5322d4f8668a4a5597e11c6da7a1cb8d0ce Mon Sep 17 00:00:00 2001 From: Bnyro Date: Sun, 20 Oct 2024 13:30:07 +0200 Subject: [PATCH 114/159] fix: prevent users from responding to shortcode sms addresses --- .../presentation/components/ElevatedTextInputField.kt | 1 + .../presentation/screens/sms/SmsThreadScreen.kt | 10 +++++++++- app/src/main/res/values/strings.xml | 1 + 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/bnyro/contacts/presentation/components/ElevatedTextInputField.kt b/app/src/main/java/com/bnyro/contacts/presentation/components/ElevatedTextInputField.kt index 2adfd0da..3a17d939 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/components/ElevatedTextInputField.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/components/ElevatedTextInputField.kt @@ -52,5 +52,6 @@ fun ElevatedTextInputField( ), keyboardOptions = KeyboardOptions(imeAction = imeAction), singleLine = singleLine, + enabled = enabled ) } diff --git a/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/SmsThreadScreen.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/SmsThreadScreen.kt index a1fa4a0a..227eab55 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/SmsThreadScreen.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/SmsThreadScreen.kt @@ -154,6 +154,12 @@ fun SmsThreadScreen( } } } + + // can't respond to address short codes that don't include a number + val showSendButton = remember { + address.any { !it.isLetter() } + } + Row( modifier = Modifier .fillMaxWidth() @@ -172,13 +178,15 @@ fun SmsThreadScreen( modifier = Modifier.weight(1f), query = text, onQueryChange = { text = it }, - placeholder = stringResource(R.string.send) + placeholder = stringResource(if (showSendButton) R.string.send else R.string.cant_respond), + enabled = showSendButton ) Spacer(modifier = Modifier.width(8.dp)) FilledIconButton( modifier = Modifier.size(48.dp), + enabled = showSendButton, onClick = { if (text.isBlank()) return@FilledIconButton if (!SmsUtil.isShortEnoughForSms(text)) { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 26786b55..35402e02 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -83,6 +83,7 @@ Messages Send message + Can\'t respond Reply Could not connect. Please ensure airplane mode is off. Message is too long! From 122798df75e001051d30cd308c513cc8d5f10a73 Mon Sep 17 00:00:00 2001 From: Bnyro Date: Sun, 20 Oct 2024 13:31:47 +0200 Subject: [PATCH 115/159] fix: block number ripple visuals --- .../contacts/presentation/screens/calllog/CallLogsScreen.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/CallLogsScreen.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/CallLogsScreen.kt index a567ab95..636203f1 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/CallLogsScreen.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/CallLogsScreen.kt @@ -389,11 +389,12 @@ fun SheetSettingItem(icon: ImageVector, @StringRes description: Int, onClick: () Row( Modifier .fillMaxWidth() - .padding(8.dp) + .clip(RoundedCornerShape(8.dp)) .clickable { view.playSoundEffect(SoundEffectConstants.CLICK) onClick() } + .padding(8.dp) ) { Icon(imageVector = icon, contentDescription = null) Spacer(Modifier.width(16.dp)) From 9c697b373d6c61fc2f3c372160e4e1755e3bddca Mon Sep 17 00:00:00 2001 From: Bnyro Date: Sun, 20 Oct 2024 14:17:33 +0200 Subject: [PATCH 116/159] feat: auto backup naming scheme preference (closes #404) --- .../screens/contacts/ContactsScreen.kt | 2 +- .../screens/settings/components/BackupPref.kt | 9 ++ .../settings/components/EditTextPreference.kt | 110 ++++++++++++++++++ .../settings/components/EncryptBackupsPref.kt | 77 +----------- .../com/bnyro/contacts/util/BackupHelper.kt | 24 +++- .../com/bnyro/contacts/util/Preferences.kt | 1 + app/src/main/res/values/strings.xml | 2 + 7 files changed, 148 insertions(+), 77 deletions(-) create mode 100644 app/src/main/java/com/bnyro/contacts/presentation/screens/settings/components/EditTextPreference.kt diff --git a/app/src/main/java/com/bnyro/contacts/presentation/screens/contacts/ContactsScreen.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/contacts/ContactsScreen.kt index 2e9c03af..296568a9 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/screens/contacts/ContactsScreen.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/contacts/ContactsScreen.kt @@ -214,7 +214,7 @@ fun ContactsPage( } 1 -> { - exportVcard.launch(BackupHelper.backupFileName) + exportVcard.launch(BackupHelper.defaultBackupFileName) } 2 -> { diff --git a/app/src/main/java/com/bnyro/contacts/presentation/screens/settings/components/BackupPref.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/settings/components/BackupPref.kt index 094a624a..2b3c6b5a 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/screens/settings/components/BackupPref.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/settings/components/BackupPref.kt @@ -14,6 +14,7 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import com.bnyro.contacts.R import com.bnyro.contacts.domain.enums.BackupType +import com.bnyro.contacts.util.BackupHelper import com.bnyro.contacts.util.PickFolderContract import com.bnyro.contacts.util.Preferences import com.bnyro.contacts.util.workers.BackupWorker @@ -44,6 +45,14 @@ fun AutoBackupPref() { ) { backupType = BackupType.fromInt(it) } + + EditTextPreference( + preferenceKey = Preferences.backupNamingSchemeKey, + title = R.string.backup_naming_scheme, + supportingHint = stringResource(R.string.backup_naming_scheme_hint), + defaultValue = BackupHelper.defaultBackupNamingScheme + ) + val backupIntervals = listOf(1, 2, 4, 6, 12, 24, 48) ListPreference( preferenceKey = Preferences.backupIntervalKey, diff --git a/app/src/main/java/com/bnyro/contacts/presentation/screens/settings/components/EditTextPreference.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/settings/components/EditTextPreference.kt new file mode 100644 index 00000000..63e85847 --- /dev/null +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/settings/components/EditTextPreference.kt @@ -0,0 +1,110 @@ +package com.bnyro.contacts.presentation.screens.settings.components + +import androidx.annotation.StringRes +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Visibility +import androidx.compose.material.icons.filled.VisibilityOff +import androidx.compose.material3.AlertDialog +import androidx.compose.material3.Button +import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.text.input.PasswordVisualTransformation +import androidx.compose.ui.text.input.VisualTransformation +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.bnyro.contacts.R +import com.bnyro.contacts.presentation.components.ClickableIcon +import com.bnyro.contacts.presentation.features.DialogButton +import com.bnyro.contacts.util.Preferences + +@Composable +fun EditTextPreference( + preferenceKey: String, + @StringRes title: Int, + defaultValue: String, + isPassword: Boolean = false, + supportingHint: String? = null, + onChange: (value: String) -> Unit = {} +) { + var showDialog by rememberSaveable { + mutableStateOf(false) + } + var preferenceValue by remember { + mutableStateOf(Preferences.getString(preferenceKey, defaultValue) ?: defaultValue) + } + + Column( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 6.dp) + ) { + if (!isPassword) Text(text = stringResource(title), fontSize = 16.sp) + Button( + onClick = { showDialog = true }, modifier = Modifier.padding(vertical = 0.dp) + ) { + if (!isPassword) Text(preferenceValue.ifEmpty { defaultValue }) else Text(stringResource(title)) + } + } + + if (showDialog) { + var passwordVisible by remember { mutableStateOf(false) } + var inputValue by remember { + mutableStateOf(preferenceValue) + } + + AlertDialog( + onDismissRequest = { showDialog = false }, + confirmButton = { + DialogButton(text = stringResource(R.string.okay)) { + Preferences.edit { putString(preferenceKey, inputValue) } + preferenceValue = inputValue + showDialog = false + } + }, dismissButton = { + DialogButton(text = stringResource(R.string.cancel)) { + showDialog = false + } + }, title = { + Text(stringResource(title)) + }, text = { + OutlinedTextField( + value = inputValue, + onValueChange = { inputValue = it }, + visualTransformation = if (passwordVisible || !isPassword) { + VisualTransformation.None + } else { + PasswordVisualTransformation() + }, + supportingText = { + if (supportingHint != null) Text(supportingHint) + }, + keyboardOptions = KeyboardOptions(keyboardType = if (isPassword) KeyboardType.Password else KeyboardType.Text), + trailingIcon = { + if (isPassword) { + val image = if (passwordVisible) { + Icons.Filled.Visibility + } else { + Icons.Filled.VisibilityOff + } + ClickableIcon(icon = image, + onClick = { passwordVisible = !passwordVisible }) + } + } + ) + } + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/bnyro/contacts/presentation/screens/settings/components/EncryptBackupsPref.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/settings/components/EncryptBackupsPref.kt index ee9a3552..b12337d1 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/screens/settings/components/EncryptBackupsPref.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/settings/components/EncryptBackupsPref.kt @@ -1,29 +1,13 @@ package com.bnyro.contacts.presentation.screens.settings.components import androidx.compose.animation.AnimatedVisibility -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Visibility -import androidx.compose.material.icons.filled.VisibilityOff -import androidx.compose.material3.AlertDialog -import androidx.compose.material3.Button -import androidx.compose.material3.OutlinedTextField -import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue -import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.input.KeyboardType -import androidx.compose.ui.text.input.PasswordVisualTransformation -import androidx.compose.ui.text.input.VisualTransformation -import androidx.compose.ui.unit.dp import com.bnyro.contacts.R -import com.bnyro.contacts.presentation.components.ClickableIcon -import com.bnyro.contacts.presentation.features.DialogButton import com.bnyro.contacts.util.BackupHelper import com.bnyro.contacts.util.PasswordUtils import com.bnyro.contacts.util.Preferences @@ -31,7 +15,6 @@ import com.bnyro.contacts.util.Preferences @Composable fun EncryptBackupsPref() { var encryptBackups by remember { mutableStateOf(BackupHelper.encryptBackups) } - var showBackupPasswordDialog by remember { mutableStateOf(false) } SwitchPref( prefKey = Preferences.encryptBackupsKey, @@ -44,61 +27,13 @@ fun EncryptBackupsPref() { } } } - AnimatedVisibility(encryptBackups) { - Button( - onClick = { showBackupPasswordDialog = true }, - modifier = Modifier.padding(vertical = 0.dp) - ) { - Text(stringResource(R.string.backup_password)) - } - } - if (showBackupPasswordDialog) { - var password by remember { - mutableStateOf( - Preferences.getString(Preferences.encryptBackupPasswordKey, "").orEmpty() - ) - } - var passwordVisible by remember { mutableStateOf(false) } - AlertDialog( - onDismissRequest = { showBackupPasswordDialog = false }, - confirmButton = { - DialogButton(text = stringResource(R.string.okay)) { - Preferences.edit { putString(Preferences.encryptBackupPasswordKey, password) } - showBackupPasswordDialog = false - } - }, - dismissButton = { - DialogButton(text = stringResource(R.string.cancel)) { - showBackupPasswordDialog = false - } - }, - title = { - Text(stringResource(R.string.backup_password)) - }, - text = { - OutlinedTextField( - value = password, - onValueChange = { password = it }, - visualTransformation = if (passwordVisible) { - VisualTransformation.None - } else { - PasswordVisualTransformation() - }, - keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password), - trailingIcon = { - val image = if (passwordVisible) { - Icons.Filled.Visibility - } else { - Icons.Filled.VisibilityOff - } - ClickableIcon( - icon = image, - onClick = { passwordVisible = !passwordVisible } - ) - } - ) - } + AnimatedVisibility(encryptBackups) { + EditTextPreference( + preferenceKey = Preferences.encryptBackupPasswordKey, + title = R.string.backup_password, + isPassword = true, + defaultValue = "", ) } } diff --git a/app/src/main/java/com/bnyro/contacts/util/BackupHelper.kt b/app/src/main/java/com/bnyro/contacts/util/BackupHelper.kt index 0532d727..d4ac0dd5 100644 --- a/app/src/main/java/com/bnyro/contacts/util/BackupHelper.kt +++ b/app/src/main/java/com/bnyro/contacts/util/BackupHelper.kt @@ -11,7 +11,8 @@ object BackupHelper { val encryptBackups get() = Preferences.getBoolean(Preferences.encryptBackupsKey, false) val mimeType get() = if (encryptBackups) "application/zip" else "text/vcard" val openMimeTypes get() = if (encryptBackups) arrayOf("application/zip") else vCardMimeTypes - val backupFileName get() = if (encryptBackups) "contacts.zip" else "contacts.vcf" + val defaultBackupFileName get() = if (encryptBackups) "contacts.zip" else "contacts.vcf" + const val defaultBackupNamingScheme = "%s-backup-%d-%t" suspend fun backup(context: Context, contactsRepository: ContactsRepository) { val backupDirPref = Preferences.getString(Preferences.backupDirKey, "").takeIf { @@ -21,10 +22,23 @@ object BackupHelper { val backupDir = DocumentFile.fromTreeUri(context, Uri.parse(backupDirPref)) ?: return val maxBackupAmount = Preferences.getString(Preferences.maxBackupAmountKey, "5")!!.toInt() - val dateTime = CalendarUtils.getCurrentDateTime() + var backupNamingScheme = + Preferences.getString(Preferences.backupNamingSchemeKey, defaultBackupNamingScheme) + ?: defaultBackupNamingScheme + + // these are required for uniqueness and automatic backup deletion + if (!backupNamingScheme.contains("%s") + || !(backupNamingScheme.contains("%d") || backupNamingScheme.contains("%t")) + ) backupNamingScheme = defaultBackupNamingScheme + val backupType = contactsRepository.label.lowercase() + val dateTime = CalendarUtils.getCurrentDateTime() + val (date, time) = dateTime.substring(0, 10) to dateTime.substring(10) val extension = if (encryptBackups) "zip" else "vcf" - val fullName = "${contactsRepository.label.lowercase()}-backup-$dateTime.$extension" + val fileName = backupNamingScheme.replace("%s", backupType) + .replace("%d", date) + .replace("%t", time) + val fullName = "${fileName}.$extension" runCatching { backupDir.findFile(fullName)?.delete() @@ -41,11 +55,11 @@ object BackupHelper { // delete all the old backup files val backupFiles = backupDir.listFiles().filter { - it.name.orEmpty().startsWith(contactsRepository.label.lowercase()) + it.name.orEmpty().contains(backupType) } if (backupFiles.size <= maxBackupAmount) return - backupFiles.sortedBy { it.name.orEmpty() } + backupFiles.sortedBy { it.lastModified() } .take(backupFiles.size - maxBackupAmount) .forEach { it.delete() } } diff --git a/app/src/main/java/com/bnyro/contacts/util/Preferences.kt b/app/src/main/java/com/bnyro/contacts/util/Preferences.kt index 137b8d45..6c232b8c 100644 --- a/app/src/main/java/com/bnyro/contacts/util/Preferences.kt +++ b/app/src/main/java/com/bnyro/contacts/util/Preferences.kt @@ -19,6 +19,7 @@ object Preferences { const val themeKey = "theme" const val backupDirKey = "backupDir" const val backupTypeKey = "backupType" + const val backupNamingSchemeKey = "backupNamingScheme" const val sortOrderKey = "sorting" const val hiddenAccountsKey = "hiddenAccounts" const val backupIntervalKey = "backupInterval" diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 35402e02..50e9a8f7 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -116,6 +116,8 @@ Dark Automatic backup Backup interval + Backup naming scheme + %d: date, %t: time, %s: source (local, device), source is required and either date or time Max amount of backups Directory Both From a9ce5176ca4af8c25628bc59f5614d0aeec3dadf Mon Sep 17 00:00:00 2001 From: Bnyro Date: Sun, 20 Oct 2024 14:24:13 +0200 Subject: [PATCH 117/159] fix: date and time format in backup naming scheme --- .../java/com/bnyro/contacts/util/BackupHelper.kt | 3 +-- .../java/com/bnyro/contacts/util/CalendarUtils.kt | 13 +++++++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/bnyro/contacts/util/BackupHelper.kt b/app/src/main/java/com/bnyro/contacts/util/BackupHelper.kt index d4ac0dd5..78bccc2f 100644 --- a/app/src/main/java/com/bnyro/contacts/util/BackupHelper.kt +++ b/app/src/main/java/com/bnyro/contacts/util/BackupHelper.kt @@ -32,8 +32,7 @@ object BackupHelper { ) backupNamingScheme = defaultBackupNamingScheme val backupType = contactsRepository.label.lowercase() - val dateTime = CalendarUtils.getCurrentDateTime() - val (date, time) = dateTime.substring(0, 10) to dateTime.substring(10) + val (date, time) = CalendarUtils.getCurrentDateAndTime() val extension = if (encryptBackups) "zip" else "vcf" val fileName = backupNamingScheme.replace("%s", backupType) .replace("%d", date) diff --git a/app/src/main/java/com/bnyro/contacts/util/CalendarUtils.kt b/app/src/main/java/com/bnyro/contacts/util/CalendarUtils.kt index 2ec82731..94195ed1 100644 --- a/app/src/main/java/com/bnyro/contacts/util/CalendarUtils.kt +++ b/app/src/main/java/com/bnyro/contacts/util/CalendarUtils.kt @@ -9,11 +9,14 @@ object CalendarUtils { @SuppressLint("SimpleDateFormat") val isoDateFormat = SimpleDateFormat("yyyy-MM-dd") + @SuppressLint("SimpleDateFormat") + val isoTimeFormat = SimpleDateFormat("HH:mm:ss") + @SuppressLint("SimpleDateFormat") val isoDateFormatWithoutYear = SimpleDateFormat("MM-dd") @SuppressLint("SimpleDateFormat") - val isoTimeFormat = SimpleDateFormat("yyyy-MM-dd-HH:mm:ss") + val isoDateTimeFormat = SimpleDateFormat("yyyy-MM-dd-HH:mm:ss") private val localizedFormat get() = DateFormat.getDateInstance() fun millisToDate(milliSeconds: Long, formatter: DateFormat = localizedFormat): String { @@ -46,6 +49,12 @@ object CalendarUtils { fun getCurrentDateTime(): String { val calendar = Calendar.getInstance() - return isoTimeFormat.format(calendar.time) + return isoDateTimeFormat.format(calendar.time) + } + + fun getCurrentDateAndTime(): Pair { + val calendar = Calendar.getInstance() + + return isoDateFormat.format(calendar.time) to isoTimeFormat.format(calendar.time) } } From 73b7e370a8095d79af33bc345e92de5d58fc1b9c Mon Sep 17 00:00:00 2001 From: ulracte Date: Wed, 10 Jul 2024 08:45:21 +0000 Subject: [PATCH 118/159] Translated using Weblate (Persian) Currently translated at 98.6% (145 of 147 strings) Translation: You Apps/Connect You Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/fa/ --- app/src/main/res/values-fa/strings.xml | 63 ++++++++++++++++++-------- 1 file changed, 44 insertions(+), 19 deletions(-) diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml index 97cd4e46..8d4e045a 100644 --- a/app/src/main/res/values-fa/strings.xml +++ b/app/src/main/res/values-fa/strings.xml @@ -3,18 +3,18 @@ بازنشانی مخاطب جدید رونوشت - حذف مخاطب + پاک‌کردن مخاطب ایجاد مخاطب تلاش دوباره درباره ما - انصراف - آیا مطمئن هستید؟ این کار برگشت‌ناپذیر است! - باشه + وازدن + آیا آسوده دل هستید؟ این کار برگشت‌ناپذیر است! + باشد جستجو هم‌رسانی پوسته روشن - حذف + پاک‌کردن در بریده‌دان کپی شد ترجمه تیره @@ -23,16 +23,16 @@ سامانه پیام شما بسیار بلند است! پشتیبان گیری - نوع - تصویر + سردگ + فرتور نام - بیشینه تعداد پشتیبان گیری ها - مسیر + بیشینه شمار پشتیبان‌گیری‌ها + پوشه نسخه ظاهر رمز افزودن به مخاطبین - ایجاد میان بر + ساختن میان‌بر تماس پیام مخاطب @@ -40,15 +40,15 @@ پالایه بیشتر نمایش جستار های بیشتر - نام نمی تواند خالی باشد! - برون بُرد شد. - درون بُرد شد. + نام نمی‌تواند تهی باشد! + برون‌بُرد شد. + درون‌بُرد شد. خانه پیشه شماره تلفن دیگر برزنی - انتخاب مخاطب + گزیدن مخاطب وبسایت فاکس محل کار دستیار @@ -78,21 +78,21 @@ نو پاسخ دستگاه - پشتیبان گیری (بکاپ) خودکار + پشتیبان‌گیری (زاپاس) خودکار متفرقه (بقیه) رفتار رمزگذاری پشتیبان گیری به عنوان فایل زیپ افزودن نشانی درون بردن vCard برون بردن vCard - آغاز برگه - فاصله پشتیبان گیری ها + برگه‌ی آغازین + فاصله پشتیبان‌گیری‌ها هیچ کدام رگارنگ کردن پرتره مخاطبین وبلاگ مدیریت گروه ها پاک کردن گروه برای همه - سر تیتر + سرنویس گروه از پیش وجود دارد گروه ها اینجا چیزی نیست. @@ -100,7 +100,7 @@ ایمیل نشانی شماره تلفن - نوع حساب + سردگ شناسه نام نام خانوادگی آوازه @@ -119,4 +119,29 @@ بلندگوها امنیت تماس ها + گزیدن صدای زنگ سفارشی + دگرش صدای زنگ + تماس‌های تازه + شماره‌های بندآمده + بندآوردن شماره پشتیبانی نمی‌شود، کانتکت‌یو برنامه‌ی پیش‌گزیده‌ی پیامک/تلفن نیست + شماره بندآمد + بندآوردن شماره + بازگشودن شماره + جستجو مخاطبین + مشکی ناب + گسستن… + پیوستن… + آیا می‌خواهید به %1$s زنگ بزنید؟ + اُستوانش کیستی با بایومتریک + پذیرش + پس‌زدن + شماره‌ی ناشناس + شماره‌گیر + در جریان + زنگ خوردن… + بی‌صدا + نیاز به اُستوانش کیستی با بایومتریک (مانند اثرانگشت) پیش از دسترسی به برنامه. + تماس پایان یافت + زنگ زدن... + تماس از \ No newline at end of file From a9e5157a6244dc956d907a1537b41fccb2cbfed0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?O=C4=9Fuz=20Ersen?= Date: Thu, 11 Jul 2024 16:41:29 +0000 Subject: [PATCH 119/159] Translated using Weblate (Turkish) Currently translated at 100.0% (149 of 149 strings) Translation: You Apps/Connect You Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/tr/ --- app/src/main/res/values-tr/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index c90c4e59..e9c13f46 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -147,4 +147,6 @@ Numara engelleme desteklenmiyor, Connect You öntanımlı SMS/Telefon uygulaması değil Kişileri ara Yeni görüşme başlat + Yalnızca sık kullanılanlar + Sık Kullanılan \ No newline at end of file From 293f7cb4540970bb0bc18348cc44224a7c16145e Mon Sep 17 00:00:00 2001 From: Pascal Dietrich Date: Thu, 11 Jul 2024 10:13:56 +0000 Subject: [PATCH 120/159] Translated using Weblate (German) Currently translated at 99.3% (148 of 149 strings) Translation: You Apps/Connect You Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/de/ --- app/src/main/res/values-de/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 12a581a3..fac59ec6 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -147,4 +147,6 @@ Stumm, lautlos Nummerneingabe Telefon Tastatur + Favorit + Nur Favoriten \ No newline at end of file From 260a14c3f5fc5eaa9139565cb08a4dc893ad0a47 Mon Sep 17 00:00:00 2001 From: Rex_sa Date: Thu, 11 Jul 2024 08:52:09 +0000 Subject: [PATCH 121/159] Translated using Weblate (Arabic) Currently translated at 100.0% (149 of 149 strings) Translation: You Apps/Connect You Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/ar/ --- app/src/main/res/values-ar/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index a3532a62..b8033924 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -146,4 +146,6 @@ تغيير نغمة الرنين بحث في جهات الاتصال بدء محادثة جديدة + المفضلة + المفضّلات فقط \ No newline at end of file From 61de88c61ffe1f741f009ce6deebf63a51c5e151 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=A7=E7=8E=8B=E5=8F=AB=E6=88=91=E6=9D=A5=E5=B7=A1?= =?UTF-8?q?=E5=B1=B1?= Date: Thu, 11 Jul 2024 10:53:06 +0000 Subject: [PATCH 122/159] Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (149 of 149 strings) Translation: You Apps/Connect You Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/zh_Hans/ --- app/src/main/res/values-zh-rCN/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 50e3bdd2..b14a3b20 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -146,4 +146,6 @@ 封禁号码 搜索联系人 开始新对话 + 收藏 + 仅收藏 \ No newline at end of file From 56b82213f941accc8ecd9a20c8548e42b34a0937 Mon Sep 17 00:00:00 2001 From: gallegonovato Date: Thu, 11 Jul 2024 11:02:03 +0000 Subject: [PATCH 123/159] Translated using Weblate (Spanish) Currently translated at 100.0% (149 of 149 strings) Translation: You Apps/Connect You Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/es/ --- app/src/main/res/values-es/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index c61919f9..1eef21e2 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -146,4 +146,6 @@ Desbloquear número Iniciar una nueva conversión Buscar contactos + Solo favoritos + Favorito \ No newline at end of file From 1a1329204a20685c72d2921ab0637fc44e514bb9 Mon Sep 17 00:00:00 2001 From: ulracte Date: Thu, 11 Jul 2024 09:32:13 +0000 Subject: [PATCH 124/159] Translated using Weblate (Persian) Currently translated at 100.0% (149 of 149 strings) Translation: You Apps/Connect You Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/fa/ --- app/src/main/res/values-fa/strings.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml index 8d4e045a..c0f3ed9c 100644 --- a/app/src/main/res/values-fa/strings.xml +++ b/app/src/main/res/values-fa/strings.xml @@ -144,4 +144,8 @@ تماس پایان یافت زنگ زدن... تماس از + آغاز گفتگو‌یی نو + برگزیدن + تنها برگزیدگان + برگه کلید \ No newline at end of file From 198baa9290c4f88357bcf02c1d63a938adec8c40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Priit=20J=C3=B5er=C3=BC=C3=BCt?= Date: Thu, 11 Jul 2024 20:00:36 +0000 Subject: [PATCH 125/159] Translated using Weblate (Estonian) Currently translated at 100.0% (149 of 149 strings) Translation: You Apps/Connect You Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/et/ --- app/src/main/res/values-et/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/values-et/strings.xml b/app/src/main/res/values-et/strings.xml index 0c892c67..790199c7 100644 --- a/app/src/main/res/values-et/strings.xml +++ b/app/src/main/res/values-et/strings.xml @@ -146,4 +146,6 @@ Lõpeta telefoninumbri blokeerimine Otsi kontaktide hulgast Alusta uut vestlust + Ainult lemmikud + Lemmik \ No newline at end of file From c6ffbd11222741c4438fdbbcc9c96a8970a87950 Mon Sep 17 00:00:00 2001 From: Linerly Date: Thu, 11 Jul 2024 14:36:21 +0000 Subject: [PATCH 126/159] Translated using Weblate (Indonesian) Currently translated at 100.0% (149 of 149 strings) Translation: You Apps/Connect You Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/id/ --- app/src/main/res/values-in/strings.xml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index 08ffab2d..fda96152 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -136,4 +136,16 @@ Memanggil... Panggilan Dari Papan tombol + Pilih nada dering khusus + Ubah nada dering + Panggilan terkini + Blokir nomor + Pemblokiran nomor tidak didukung, Connect You bukan aplikasi SMS/Telepon bawaan + Nomor diblokir + Blokir nomor + Buka blokir nomor + Cari kontak + Mulai percakapan baru + Favorit + Hanya favorit \ No newline at end of file From a276e9951c12a5741cbc7d489cfcf08ca6329197 Mon Sep 17 00:00:00 2001 From: Dan Date: Fri, 12 Jul 2024 13:36:13 +0000 Subject: [PATCH 127/159] Translated using Weblate (Ukrainian) Currently translated at 100.0% (149 of 149 strings) Translation: You Apps/Connect You Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/uk/ --- app/src/main/res/values-uk/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 3b0461dc..bd9c73c8 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -147,4 +147,6 @@ Розблокувати номер Почати нову розмову Пошук контактів + Тільки улюблене + Улюблене \ No newline at end of file From d6a2b7feda73eb5e13719f6d7a72f3e501667b09 Mon Sep 17 00:00:00 2001 From: Fjuro Date: Fri, 12 Jul 2024 10:22:46 +0000 Subject: [PATCH 128/159] Translated using Weblate (Czech) Currently translated at 100.0% (149 of 149 strings) Translation: You Apps/Connect You Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/cs/ --- app/src/main/res/values-cs/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index c38dd058..bc8e0c5a 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -146,4 +146,6 @@ Odblokovat číslo Hledat kontakty Začít novou konverzaci + Pouze oblíbené + Oblíbené \ No newline at end of file From 2877656bc88f1d3b859eace8d542f6780351feee Mon Sep 17 00:00:00 2001 From: kaajjo Date: Sat, 13 Jul 2024 12:06:05 +0000 Subject: [PATCH 129/159] Translated using Weblate (Russian) Currently translated at 100.0% (149 of 149 strings) Translation: You Apps/Connect You Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/ru/ --- app/src/main/res/values-ru/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 8d18966a..949559e4 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -146,4 +146,6 @@ Разблокировать номер Поиск контактов Начать беседу + Избранное + Только избранные \ No newline at end of file From 3f2fae028479aae6fc7324f44917be39750abe6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?jos=C3=A9=20m?= Date: Sun, 14 Jul 2024 07:34:31 +0000 Subject: [PATCH 130/159] Translated using Weblate (Galician) Currently translated at 100.0% (149 of 149 strings) Translation: You Apps/Connect You Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/gl/ --- app/src/main/res/values-gl/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml index 39014fe4..e64eed24 100644 --- a/app/src/main/res/values-gl/strings.xml +++ b/app/src/main/res/values-gl/strings.xml @@ -146,4 +146,6 @@ Desbloquear número Buscar contactos Comezar nova conversa + Favorito + Só favoritos \ No newline at end of file From a8d559aacf7bd18916cb3d9770a1698eb5bf1e69 Mon Sep 17 00:00:00 2001 From: NEXI Date: Tue, 16 Jul 2024 05:02:09 +0000 Subject: [PATCH 131/159] Translated using Weblate (Serbian) Currently translated at 100.0% (149 of 149 strings) Translation: You Apps/Connect You Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/sr/ --- app/src/main/res/values-sr/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml index df5029d5..0678b12a 100644 --- a/app/src/main/res/values-sr/strings.xml +++ b/app/src/main/res/values-sr/strings.xml @@ -157,4 +157,6 @@ Блокирање бројева није подржано, Connect You није подразумевана апликација за SMS/телефон Претрага контаката Започни нови разговор + Омиљено + Само омиљени \ No newline at end of file From 36d86ae7c5702e53f262f82d1124cc24c232a82a Mon Sep 17 00:00:00 2001 From: ulracte Date: Mon, 15 Jul 2024 19:42:50 +0000 Subject: [PATCH 132/159] Translated using Weblate (Persian) Currently translated at 100.0% (149 of 149 strings) Translation: You Apps/Connect You Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/fa/ --- app/src/main/res/values-fa/strings.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml index c0f3ed9c..5d0aee5f 100644 --- a/app/src/main/res/values-fa/strings.xml +++ b/app/src/main/res/values-fa/strings.xml @@ -16,7 +16,7 @@ روشن پاک‌کردن در بریده‌دان کپی شد - ترجمه + ترزبانی تیره تنظیمات نویسنده @@ -28,7 +28,7 @@ نام بیشینه شمار پشتیبان‌گیری‌ها پوشه - نسخه + نگارش ظاهر رمز افزودن به مخاطبین From a8512013f4f8e9b8a5237c4438751c3ab1843ddc Mon Sep 17 00:00:00 2001 From: Scrambled777 Date: Fri, 19 Jul 2024 05:37:33 +0000 Subject: [PATCH 133/159] Translated using Weblate (Hindi) Currently translated at 100.0% (149 of 149 strings) Translation: You Apps/Connect You Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/hi/ --- app/src/main/res/values-hi/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/values-hi/strings.xml b/app/src/main/res/values-hi/strings.xml index a9c9c1e2..2abcae88 100644 --- a/app/src/main/res/values-hi/strings.xml +++ b/app/src/main/res/values-hi/strings.xml @@ -146,4 +146,6 @@ रिंगटोन बदलें संपर्क खोजें एक नई बातचीत शुरू करें + पसंदीदा + केवल पसंदीदा \ No newline at end of file From 99b61a920449be2556ebc07eaa6b3904b28e52d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jiri=20Gr=C3=B6nroos?= Date: Tue, 23 Jul 2024 09:50:36 +0000 Subject: [PATCH 134/159] Translated using Weblate (Finnish) Currently translated at 99.3% (148 of 149 strings) Translation: You Apps/Connect You Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/fi/ --- app/src/main/res/values-fi/strings.xml | 31 ++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index e7d2110c..3b6ef22f 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -116,4 +116,35 @@ Päiväys Lisää muistiinpano Haluatko tuoda valitun VCF-tiedoston? + Numero estetty + Estä numero + Poista numeron esto + Numeron esto ei ole tuettu. Connect You ei ole oletusarvoinen tekstiviesti-/puhelinsovellus + Etsi yhteystietoja + Aloita uusi keskustelu + Hyväksy + Kieltäydy + Tuntematon numero + Vaihda soittoääni + Soitetaan... + Viimeisimmät puhelut + Amoled + Katkaistaan yhteys… + Suosikki + Vain suosikit + Meneillään + Soitetaan… + Yhdistetään… + Haluatko soittaa %1$s? + Mykistä + Kaiuttimet + Turvallisuus + Biometrinen todennus + Vaadi biometrinen todennus (esim. sormenjälki), ennen kuin sovellusta voi käyttää. + Puhelut + Numeronäppäimistö + Puhelu päättyi + Näppäimistö + Valitse mukautettu soittoääni + Estä numerot \ No newline at end of file From fdb175758c5945206127211929926ee985fd29dc Mon Sep 17 00:00:00 2001 From: Oasis Tri Date: Mon, 5 Aug 2024 11:36:48 +0000 Subject: [PATCH 135/159] Translated using Weblate (French) Currently translated at 97.3% (145 of 149 strings) Translation: You Apps/Connect You Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/fr/ --- app/src/main/res/values-fr/strings.xml | 50 +++++++++++++++++++++----- 1 file changed, 41 insertions(+), 9 deletions(-) diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index c2e5c541..418da0fc 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -7,7 +7,7 @@ Évènement Prénom Nom de Famille - Auteur·e + Auteurs Version Traduction Connect You @@ -38,7 +38,7 @@ Êtes vous sûr \? Cela ne peut pas être annulé ! Créer un contact Réinitialiser - D\'accord + Ok Appareil Local Rien ici. @@ -62,7 +62,7 @@ Filtre Plus Afficher plus de champs - Montrer moins + Afficher moins Site web Surnom Organisation @@ -71,13 +71,13 @@ Blog Nouveau %1$s sélectionné - Intervalle de Sauvegarde + Intervalle de sauvegarde Nombre maximal de sauvegardes Message Nouveau contact Créer un raccourci Numérotation - Le groupe existe déjà + Ce groupe existe déjà Groupes Gérer les groupes Supprimer le groupe pour tout le monde @@ -98,17 +98,17 @@ Effacer le message Apparence Chiffrer les sauvegardes sous format zip - Le message est trop long ! + Le message est trop long ! Contacts Send message Sauvegarde - Les SMS ne seront stockés que dans l\'application et ne seront pas accessibles dans d\'autres applications. - Choisir le contact + Les SMS ne seront stockés que dans l\'application et ne seront pas accessible par les autres applications. + Choisissez un contact Messages N° de téléphone Mot de passe Impossible de se connecter. Veuillez vous assurer que le mode avion est désactivé. - Souhaitez-vous envoyer le message sous la forme de plusieurs petits messages ? + Souhaitez-vous envoyer le message sous la forme de plusieurs petits messages ? Voulez-vous importer le fichier VCF sélectionné ? Ajouter un site Web Ajouter un évènement @@ -117,4 +117,36 @@ Ajouter une adresse mail Ajouter une adresse Ajouter une note + Bloquer des numéros + Numéro bloqué + Le blocage de numéros n\'est pas supporté, Connect You n\'est pas l\'appli de SMS/Téléphone par défaut + Bloquer le numéro + Débloquer le numéro + Démarrer une nouvelle conversation + Rechercher des contacts + Noir pur + Exiger une authentification biométrique (par ex. empreinte digitale) pour accéder à l\'application. + Favori + Seulement les favoris + Sonne… + Déconnexion… + Connexion… + En cours + Voulez-vous appeler %1$s ? + Silencieux + Hautparleurs + Sécurité + Authentification biométrique + Appels + Refuser + Accepter + Pavé de numérotation + Sélectionner une sonnerie personnalisée + Changer la sonnerie + Appels récents + Numéro inconnu + Appel terminé + Numérotation… + Appel de + Clavier numérique \ No newline at end of file From 901fea35b2e442195e9ba108322429458951ba5f Mon Sep 17 00:00:00 2001 From: 109247019824 Date: Mon, 12 Aug 2024 04:06:22 +0000 Subject: [PATCH 136/159] Translated using Weblate (Bulgarian) Currently translated at 2.0% (3 of 149 strings) Translation: You Apps/Connect You Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/bg/ --- app/src/main/res/values-bg/strings.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml index 6c5eb6fa..83548231 100644 --- a/app/src/main/res/values-bg/strings.xml +++ b/app/src/main/res/values-bg/strings.xml @@ -2,4 +2,5 @@ Копиране Сигурни ли сте\? Действието е необратимо. + Запазване \ No newline at end of file From cee7dfee055dba1ec922993122e03746586ee515 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=B5=E5=8F=AF=E6=8F=9A?= Date: Thu, 29 Aug 2024 15:36:36 +0000 Subject: [PATCH 137/159] Translated using Weblate (Chinese (Traditional)) Currently translated at 53.0% (79 of 149 strings) Translation: You Apps/Connect You Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/zh_Hant/ --- app/src/main/res/values-zh-rTW/strings.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 66967542..8d7e62b5 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -77,4 +77,5 @@ 工作傳真 助理 工作 + 最愛 \ No newline at end of file From 3db0c512d510893463d77a7947bdebfa1945bb57 Mon Sep 17 00:00:00 2001 From: NEXI Date: Thu, 19 Sep 2024 05:18:13 +0000 Subject: [PATCH 138/159] Translated using Weblate (Serbian) Currently translated at 100.0% (149 of 149 strings) Translation: You Apps/Connect You Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/sr/ --- app/src/main/res/values-sr/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml index 0678b12a..c9c8fd44 100644 --- a/app/src/main/res/values-sr/strings.xml +++ b/app/src/main/res/values-sr/strings.xml @@ -89,7 +89,7 @@ Оба Ниједно Остало - Скупи доњу траку при скроловању + Скупи доњу траку при превлачењу Шарене иконице контаката О апликацији From e4249e391c8172a9834f0c6ff26ffce18ff0d556 Mon Sep 17 00:00:00 2001 From: Luna Date: Sat, 5 Oct 2024 12:30:18 +0000 Subject: [PATCH 139/159] Translated using Weblate (Danish) Currently translated at 96.6% (144 of 149 strings) Translation: You Apps/Connect You Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/da/ --- app/src/main/res/values-da/strings.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index 00e89104..1e5fafd8 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -142,4 +142,5 @@ Amoled Opkald Afsluttet Opkald fra + Kun favoritter \ No newline at end of file From b894c5ad24b09eaa3b52e93d7ab6ee882d78b7bd Mon Sep 17 00:00:00 2001 From: Femini Date: Fri, 18 Oct 2024 17:20:34 +0000 Subject: [PATCH 140/159] Translated using Weblate (Azerbaijani) Currently translated at 100.0% (149 of 149 strings) Translation: You Apps/Connect You Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/az/ --- app/src/main/res/values-az/strings.xml | 60 +++++++++++++++++++++++++- 1 file changed, 58 insertions(+), 2 deletions(-) diff --git a/app/src/main/res/values-az/strings.xml b/app/src/main/res/values-az/strings.xml index 2d11943f..2b270774 100644 --- a/app/src/main/res/values-az/strings.xml +++ b/app/src/main/res/values-az/strings.xml @@ -55,7 +55,7 @@ İşıqlı Qara Avtomatik nüsxələmə - Kataloq seç + Kataloq Hər ikisi Heç biri Qrupları idarə et @@ -73,7 +73,7 @@ Sürüşdürdükdə alt çubuğu yığcamlaşdır Qısayol Yarat Məktub - Nömrə yaz + Zəng et Redaktə et Filtr Daha çox @@ -93,4 +93,60 @@ Əlaqə seç Saxla Telefon nömrəsi + Davranış + Görünüş + Fərdi zəng səsi seç + Zəng səsin dəyişdir + Nömrələri əngəllə + Son zənglər + Nömrə bloklanması dəstəklənmir, Connect You ilkin SMS/Telefon tətbiqi deyil + Nömrəni əngəllə + Nömrə əngəlin sil + Zəng edilir… + Mesaj göndər + Seçilmiş VCF faylını idxal etmək istəyirsiniz? + Yalnız sevimlilər + Mesaj çox uzundur! + Əlaqələri axtar + Yeni söhbətə başla + Amoled + Bir neçə kiçik mesaj kimi göndərmək istəyirsiniz? + Telefon nömrəsi əlavə et + E-poçt Əlavə Et + Ünvan Əlavə Et + Qeyd Əlavə Et + Hadisə Əlavə Et + Sevimli + Mesajlar + Cavab ver + Qoşulmaq alınmadı. Təyyarə rejiminin söndürüldüyünə əmin olun. + Şəxsi SMS Məlumat Bazası + SMS yalnız tətbiqdə saxlanılacaq və digər tətbiqlərdə əlçatan olmayacaq. + Mətni sil + Mesajı silin + Davam edir + Bağlantı kəsilir… + Qoşulur… + %1$s-a zəng etmək istəyirsiniz? + Səssiz + Dinamiklər + SIM-dən idxal et + Nüsxələmə + Nüsxələmələri zip kimi şifrələ + Parol + Təhlükəsizlik + Biometrik Təsdiqləmə + Tətbiqə daxil olmaq üçün biometrik təsdiqləmə (məsələn, barmaq izi) tələb edin. + Veb Sayt Əlavə Et + Tarix + Zənglər + İmtina et + Qəbul et + Gizli Nömrə + Zəng lövhəsi + Zəng bitdi + Zəng edilir... + Zəng edən + Nömrə əngəllənib + Klaviatura \ No newline at end of file From e35eec6092266761bafcb3e5bda7aa58884e7d64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jiri=20Gr=C3=B6nroos?= Date: Sun, 20 Oct 2024 17:58:07 +0000 Subject: [PATCH 141/159] Translated using Weblate (Finnish) Currently translated at 100.0% (149 of 149 strings) Translation: You Apps/Connect You Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/fi/ --- app/src/main/res/values-fi/strings.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index 3b6ef22f..a7ca3f57 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -147,4 +147,5 @@ Näppäimistö Valitse mukautettu soittoääni Estä numerot + Puhelu soittajalta \ No newline at end of file From c67d112f493fa261e9d05a3ef4a15070d63e701a Mon Sep 17 00:00:00 2001 From: Pascal Dietrich Date: Sun, 20 Oct 2024 20:46:08 +0000 Subject: [PATCH 142/159] Translated using Weblate (German) Currently translated at 97.4% (150 of 154 strings) Translation: You Apps/Connect You Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/de/ --- app/src/main/res/values-de/strings.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index fac59ec6..c3ecb7b7 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -149,4 +149,7 @@ Tastatur Favorit Nur Favoriten + SMS importieren + SMS exportieren + %d: Datum, %t: Zeit. %s: Quelle (lokal, Gerät), die Quelle und entweder Datum oder Zeit müssen angegeben werden \ No newline at end of file From 24cbdeb9ea6635ecaa9aa80d2b3f8ab8e8916ec5 Mon Sep 17 00:00:00 2001 From: Rex_sa Date: Mon, 21 Oct 2024 03:32:29 +0000 Subject: [PATCH 143/159] Translated using Weblate (Arabic) Currently translated at 100.0% (154 of 154 strings) Translation: You Apps/Connect You Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/ar/ --- app/src/main/res/values-ar/strings.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index b8033924..e34765fb 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -148,4 +148,9 @@ بدء محادثة جديدة المفضلة المفضّلات فقط + استيراد رسالة نصية قصيرة + لا يمكن الرد + تصدير الرسائل + نظام التسمية لنسخ الاحتياطية + %d: التاريخ، %t: الوقت، %s: المصدر (المحلي، الجهاز)، المصدر يتطلب التاريخ أو الوقت \ No newline at end of file From 3e1f464aaca57c4df446fb74c0574e9c54dc16e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=A7=E7=8E=8B=E5=8F=AB=E6=88=91=E6=9D=A5=E5=B7=A1?= =?UTF-8?q?=E5=B1=B1?= Date: Mon, 21 Oct 2024 00:03:18 +0000 Subject: [PATCH 144/159] Translated using Weblate (Chinese (Simplified Han script)) Currently translated at 100.0% (154 of 154 strings) Translation: You Apps/Connect You Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/zh_Hans/ --- app/src/main/res/values-zh-rCN/strings.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index b14a3b20..50446edd 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -148,4 +148,9 @@ 开始新对话 收藏 仅收藏 + 无法响应 + 导入短信 + 导出短信 + 备份命名方案 + %d: 日期, %t: 时间, %s: 源 (本地、设备), 来源以及日期或时间是必需的 \ No newline at end of file From 7e2a01a5be8bb22834076524ef78a7b4006c44a7 Mon Sep 17 00:00:00 2001 From: Fjuro Date: Mon, 21 Oct 2024 06:09:19 +0000 Subject: [PATCH 145/159] Translated using Weblate (Czech) Currently translated at 100.0% (154 of 154 strings) Translation: You Apps/Connect You Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/cs/ --- app/src/main/res/values-cs/strings.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index bc8e0c5a..7c6b95cd 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -148,4 +148,9 @@ Začít novou konverzaci Pouze oblíbené Oblíbené + Nelze odpovědět + Importovat SMS + Exportovat SMS + Schéma názvů záloh + %d: datum, %t: čas, %s: zdroj (local, device), je vyžadován zdroj a buď datum nebo čas \ No newline at end of file From b0bea24c9f93d1bf1f757153dacf253dfab912f3 Mon Sep 17 00:00:00 2001 From: gallegonovato Date: Sun, 20 Oct 2024 22:53:38 +0000 Subject: [PATCH 146/159] Translated using Weblate (Spanish) Currently translated at 100.0% (154 of 154 strings) Translation: You Apps/Connect You Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/es/ --- app/src/main/res/values-es/strings.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 1eef21e2..74eab847 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -148,4 +148,9 @@ Buscar contactos Solo favoritos Favorito + Exportar SMS + Esquema de nombres de la copia de seguridad + No puedo responder + Importar SMS + %d: fecha, %t: hora, %s: fuente (local, dispositivo), se requiere la fuente y la fecha o la hora \ No newline at end of file From 1d5a89bf754f79ed65bd119842fdfef57a886f48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?jos=C3=A9=20m?= Date: Mon, 21 Oct 2024 03:52:37 +0000 Subject: [PATCH 147/159] Translated using Weblate (Galician) Currently translated at 100.0% (154 of 154 strings) Translation: You Apps/Connect You Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/gl/ --- app/src/main/res/values-gl/strings.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml index e64eed24..ee35b389 100644 --- a/app/src/main/res/values-gl/strings.xml +++ b/app/src/main/res/values-gl/strings.xml @@ -148,4 +148,9 @@ Comezar nova conversa Favorito Só favoritos + Formato do nome da copia + %d: data, %t: hora, %s: orixe (local, dispositivo), a orixe é requerida mais a data ou a hora + Non pode responder + Exportar SMS + Importar SMS \ No newline at end of file From fadffff2754979240974407b90af89b32035667d Mon Sep 17 00:00:00 2001 From: Edgars Andersons Date: Mon, 21 Oct 2024 09:15:10 +0000 Subject: [PATCH 148/159] Translated using Weblate (Latvian) Currently translated at 100.0% (154 of 154 strings) Translation: You Apps/Connect You Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/lv/ --- app/src/main/res/values-lv/strings.xml | 64 ++++++++++++++++++++++++-- 1 file changed, 61 insertions(+), 3 deletions(-) diff --git a/app/src/main/res/values-lv/strings.xml b/app/src/main/res/values-lv/strings.xml index 9a3df7ce..4574924f 100644 --- a/app/src/main/res/values-lv/strings.xml +++ b/app/src/main/res/values-lv/strings.xml @@ -3,7 +3,7 @@ Meklēt Ievietots starpliktuvē Izdzēst kontaktu - Vai tiešām\? Tas būs neatgriezeniski. + Vai tiešām? Tas būs neatgriezeniski. Atcelt Labi Atkārtot @@ -71,7 +71,7 @@ Automātiskas rezerves kopijas Laika posms starp rezerves kopijām Lielākais rezerves kopiju skaits - Izvēlēties mapi + Mape Abi Neviens Dažādi @@ -80,7 +80,7 @@ Uzvedība Rezerves kopija Šifrēt rezerves kopijas kā zip - Rezerves kopiju parole + Parole Par Licence Autors @@ -95,4 +95,62 @@ Konta veids Izskats Versija + Ziņojums ir pārāk garš. + Atlasīt pielāgotu zvanīšanas skaņu + Atbilde + Privāta īsziņu datubāze + Skaļruņi + Ievietot īsziņas + Izgūt īsziņas + Rezerves kopiju nosaukuma veidošana + Pievienot tīmekļvietni + Pievienot tālruņa numuru + Tastatūra + Mainīt zvanīšanas skaņu + Nesenie zvani + Liegt numurus + Numuru liegšana netiek atbalstīta, Connect You nav noklusējuma īsziņu/tālruņa lietotne + Numurs liegts + Liegt numuru + Atcelt numura liegumu + Meklēt kontaktpersonas + Uzsākt jaunu sarunu + Nevar atbildēt + %d: datums, %t: laiks, %s: avots (vietējs, ierīce), ir nepieciešams avots un vai nu datums, vai laiks + Drošība + Autentificēšanās ar biometriju + Pieprasīt autentificēšanos ar biometriju (piemēram, pirkstu nospiedumu), pirms lietotnei var piekļūt. + Kontaktpersonas + Vai ievietot atlasīto VCF datni? + Nevarēja savienoties. Lūgums nodrošināt, ka lidmašīnas režīms ir izslēgts. + Vai nosūtīt ziņojumu kā vairākus mazākus? + Īsziņa tiks glabāta tikai lietotnē un nebūs pieejama citās lietotnēs. + Izdzēst pavedienu + Izdzēst ziņojumu + Saglabāt + Tālruņa numurs + Ziņojumi + Nosūtīt ziņojumu + Izlasē + Savienojas… + Tikai izlasē esošie + Notiek + Zvana… + Pārtrauc savienojumu… + Vai zvanīt %1$s? + Apklusināt + Noraidīt + Pieņemt + Nezināms numurs + Ciparnīca + Zvans ir beidzies + Zvana... + Zvans no + Pievienot e-pasta adresi + Pievienot adresi + Pievienot piezīmi + Pievienot notikumu + Datums + AMOLED + Zvani \ No newline at end of file From ea5aff2cee7adad021a8d8e5799ea68b15b345b7 Mon Sep 17 00:00:00 2001 From: trunars Date: Mon, 21 Oct 2024 10:22:36 +0000 Subject: [PATCH 149/159] Translated using Weblate (Bulgarian) Currently translated at 2.5% (4 of 154 strings) Translation: You Apps/Connect You Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/bg/ --- app/src/main/res/values-bg/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml index 83548231..c0479092 100644 --- a/app/src/main/res/values-bg/strings.xml +++ b/app/src/main/res/values-bg/strings.xml @@ -1,6 +1,6 @@ Копиране - Сигурни ли сте\? Действието е необратимо. + Сигурни ли сте? Това не може да бъде отменено! Запазване \ No newline at end of file From 3f4fe379552e6d236c4a0a94beca0e574953ea0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Priit=20J=C3=B5er=C3=BC=C3=BCt?= Date: Sun, 20 Oct 2024 21:10:38 +0000 Subject: [PATCH 150/159] Translated using Weblate (Estonian) Currently translated at 100.0% (154 of 154 strings) Translation: You Apps/Connect You Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/et/ --- app/src/main/res/values-et/strings.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/src/main/res/values-et/strings.xml b/app/src/main/res/values-et/strings.xml index 790199c7..6297b6aa 100644 --- a/app/src/main/res/values-et/strings.xml +++ b/app/src/main/res/values-et/strings.xml @@ -148,4 +148,9 @@ Alusta uut vestlust Ainult lemmikud Lemmik + Ma ei saa vastata + Impordi SMS\'id + Ekspordi SMD\'id + Varukoopiate nimeloogika + %d: kuupäev, %t: kellaaeg, %s: allikas (kohalik, seade), nõutav on allikas ja kas kuupäev või kellaaeg \ No newline at end of file From 55f4c0168feca2e5359da2fcba1ae37eabe62e67 Mon Sep 17 00:00:00 2001 From: Linerly Date: Sun, 20 Oct 2024 23:55:00 +0000 Subject: [PATCH 151/159] Translated using Weblate (Indonesian) Currently translated at 100.0% (154 of 154 strings) Translation: You Apps/Connect You Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/id/ --- app/src/main/res/values-in/strings.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index fda96152..9b641e25 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -148,4 +148,9 @@ Mulai percakapan baru Favorit Hanya favorit + Impor SMS + Ekspor SMS + Skema nama cadangan + %d: tanggal, %t: waktu, %s: sumber (lokal, perangkat), sumber diperlukan untuk tanggal atau waktu + Tidak dapat membalas \ No newline at end of file From 0f15e13e080c788d30b2448643fab834974b1fdd Mon Sep 17 00:00:00 2001 From: Femini Date: Wed, 23 Oct 2024 04:59:00 +0000 Subject: [PATCH 152/159] Translated using Weblate (Azerbaijani) Currently translated at 100.0% (154 of 154 strings) Translation: You Apps/Connect You Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/az/ --- app/src/main/res/values-az/strings.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/src/main/res/values-az/strings.xml b/app/src/main/res/values-az/strings.xml index 2b270774..b291c50a 100644 --- a/app/src/main/res/values-az/strings.xml +++ b/app/src/main/res/values-az/strings.xml @@ -149,4 +149,9 @@ Zəng edən Nömrə əngəllənib Klaviatura + Cavab verə bilmir + Nüsxə adlandırma təsviri + %d: tarix, %t: vaxt, %s: mənbə (yerli, cihaz), mənbə tələb olunur və tarix və ya vaxt + SMS İxrac Et + SMS İdxal Et \ No newline at end of file From 00a166d55d21c1d0e37ed7ccbf0fb5b4bce5e1b1 Mon Sep 17 00:00:00 2001 From: Dan Date: Wed, 23 Oct 2024 08:29:58 +0000 Subject: [PATCH 153/159] Translated using Weblate (Ukrainian) Currently translated at 100.0% (154 of 154 strings) Translation: You Apps/Connect You Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/uk/ --- app/src/main/res/values-uk/strings.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index bd9c73c8..bd8deb97 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -149,4 +149,9 @@ Пошук контактів Тільки улюблене Улюблене + Не можу відповісти + Імпортувати SMS + Схема іменування резервних копій + Експортувати SMS + %d: дата, %t: час, %s: джерело (локально, пристрій), потрібно вказати джерело та дату або час \ No newline at end of file From 855ced7b6a861fdae75f3bf5e64daf92b50528f8 Mon Sep 17 00:00:00 2001 From: qtm Date: Wed, 23 Oct 2024 17:36:08 +0000 Subject: [PATCH 154/159] Translated using Weblate (Russian) Currently translated at 100.0% (154 of 154 strings) Translation: You Apps/Connect You Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/ru/ --- app/src/main/res/values-ru/strings.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 949559e4..1f519816 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -148,4 +148,9 @@ Начать беседу Избранное Только избранные + Невозможно ответить + Импорт SMS + Экспорт SMS + Шаблон наименований рез. копий + %d: дата, %t: время, %s: база (приложения или общая). Требуется база и хотя бы дата или время \ No newline at end of file From f27711e81f5191b973bd354df488396bc8100935 Mon Sep 17 00:00:00 2001 From: NEXI Date: Thu, 24 Oct 2024 00:16:34 +0000 Subject: [PATCH 155/159] Translated using Weblate (Serbian) Currently translated at 100.0% (154 of 154 strings) Translation: You Apps/Connect You Translate-URL: https://hosted.weblate.org/projects/you-apps/connect-you/sr/ --- app/src/main/res/values-sr/strings.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml index c9c8fd44..70eddcda 100644 --- a/app/src/main/res/values-sr/strings.xml +++ b/app/src/main/res/values-sr/strings.xml @@ -159,4 +159,9 @@ Започни нови разговор Омиљено Само омиљени + Није могуће одговорити + Увоз SMS-а + Извоз SMS-а + Шема именовања резервне копије + %d: датум, %t: време, %s: извор (локални, уређај), извор је обавезан и датум или време \ No newline at end of file From 6109c34c9076b16f882a9560980fa65b2d95c37d Mon Sep 17 00:00:00 2001 From: Bnyro Date: Tue, 29 Oct 2024 12:53:23 +0100 Subject: [PATCH 156/159] feat: clear dialpad input at long press (closes #419) --- .../screens/calllog/CallLogsScreen.kt | 1 + .../screens/calllog/components/NumberInput.kt | 77 ++++++++++--------- .../screens/calllog/model/CallModel.kt | 7 ++ 3 files changed, 50 insertions(+), 35 deletions(-) diff --git a/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/CallLogsScreen.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/CallLogsScreen.kt index 636203f1..40856c42 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/CallLogsScreen.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/CallLogsScreen.kt @@ -266,6 +266,7 @@ fun CallLogsScreen( NumberInput( onNumberInput = callModel::onNumberInput, onDelete = callModel::onBackSpace, + onClear = callModel::onClearNumberInput, onDial = { callModel.callNumber() }, diff --git a/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/components/NumberInput.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/components/NumberInput.kt index ee23b3a3..edd99616 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/components/NumberInput.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/components/NumberInput.kt @@ -72,6 +72,7 @@ val keypadNumbers: Array>> = arrayOf( fun NumberInput( onNumberInput: (String) -> Unit, onDelete: () -> Unit, + onClear: () -> Unit, onDial: () -> Unit, subscriptions: List?, onSubscriptionIndexChange: (Int) -> Unit @@ -89,44 +90,49 @@ fun NumberInput( horizontalArrangement = Arrangement.spacedBy(buttonSpacing) ) { col.forEach { - val subcontent = it.second - if (subcontent is String) { - NumpadButtonWithSubcontent( - aspectRatio = 1.5f, - text = it.first, - onClick = { - onNumberInput(it.first) + when (val subContent = it.second) { + is String -> { + NumpadButtonWithSubcontent( + aspectRatio = 1.5f, + text = it.first, + onClick = { + onNumberInput(it.first) + } + ) { + Text( + text = subContent, + color = MaterialTheme.colorScheme.onSurface, + style = MaterialTheme.typography.bodyMedium + ) } - ) { - Text( - text = subcontent, - color = MaterialTheme.colorScheme.onSurface, - style = MaterialTheme.typography.bodyMedium - ) } - } else if (subcontent is ImageVector) { - NumpadButtonWithSubcontent( - aspectRatio = 1.5f, - text = it.first, - onClick = { - onNumberInput(it.first) + + is ImageVector -> { + NumpadButtonWithSubcontent( + aspectRatio = 1.5f, + text = it.first, + onClick = { + onNumberInput(it.first) + } + ) { + Icon( + modifier = Modifier.size(16.dp), + imageVector = subContent, + contentDescription = null, + tint = MaterialTheme.colorScheme.onSurface + ) } - ) { - Icon( - modifier = Modifier.size(16.dp), - imageVector = subcontent, - contentDescription = null, - tint = MaterialTheme.colorScheme.onSurface + } + + else -> { + NumpadButton( + aspectRatio = 1.5f, + text = it.first, + onClick = { + onNumberInput(it.first) + } ) } - } else { - NumpadButton( - aspectRatio = 1.5f, - text = it.first, - onClick = { - onNumberInput(it.first) - } - ) } } } @@ -137,7 +143,7 @@ fun NumberInput( ) { Spacer(modifier = Modifier.weight(1f)) NumpadButton( - onClick = { onDial.invoke() }, + onClick = onDial, backgroundColor = MaterialTheme.colorScheme.secondaryContainer ) { Icon( @@ -147,7 +153,8 @@ fun NumberInput( ) } NumpadButton( - onClick = { onDelete.invoke() }, + onClick = onDelete, + onLongClick = onClear, backgroundColor = MaterialTheme.colorScheme.tertiaryContainer ) { Icon( diff --git a/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/model/CallModel.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/model/CallModel.kt index e8e17567..e18fc5b5 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/model/CallModel.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/model/CallModel.kt @@ -103,6 +103,13 @@ class CallModel(private val application: Application, savedStateHandle: SavedSta } } + fun onClearNumberInput() { + numberToCall = "" + _contacts.update { + emptyList() + } + } + fun callNumber(number: String = numberToCall) { if (!PermissionHelper.hasPermission(application.applicationContext, *phonePerms)) return From a5b090f60fef59e3a6a6c1ef2adc2738f1578906 Mon Sep 17 00:00:00 2001 From: Bnyro Date: Tue, 29 Oct 2024 13:01:51 +0100 Subject: [PATCH 157/159] fix: create contact button for sms stops working after first use (closes #417) --- .../features/AddToContactDialog.kt | 19 ++++++++----------- .../screens/sms/SmsThreadScreen.kt | 4 +++- .../contacts/ui/activities/MainActivity.kt | 12 +++++++++++- 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/com/bnyro/contacts/presentation/features/AddToContactDialog.kt b/app/src/main/java/com/bnyro/contacts/presentation/features/AddToContactDialog.kt index 55317e19..fe86ed41 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/features/AddToContactDialog.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/features/AddToContactDialog.kt @@ -28,14 +28,10 @@ import com.bnyro.contacts.presentation.screens.editor.EditorScreen import kotlinx.coroutines.launch @Composable -fun AddToContactDialog(newNumber: String) { +fun AddToContactDialog(newNumber: String, onDismissRequest: () -> Unit) { val context = LocalContext.current val contactsModel: ContactsModel = viewModel(factory = ContactsModel.Factory) - var showDialog by remember { - mutableStateOf(true) - } - var contactToEdit by remember { mutableStateOf(null) } @@ -46,23 +42,22 @@ fun AddToContactDialog(newNumber: String) { val scope = rememberCoroutineScope() - if (showDialog) { + if (contactToEdit == null) { var searchQuery by remember { mutableStateOf("") } AlertDialog( - onDismissRequest = { showDialog = false }, + onDismissRequest = { onDismissRequest() }, confirmButton = { DialogButton(text = stringResource(R.string.cancel)) { - showDialog = false + onDismissRequest() } }, dismissButton = { DialogButton(text = stringResource(R.string.new_contact)) { isNewContact = true contactToEdit = ContactData() - showDialog = false } }, title = { @@ -95,7 +90,6 @@ fun AddToContactDialog(newNumber: String) { scope.launch { contactToEdit = contactsModel.loadAdvancedContactData(it) } - showDialog = false } } } @@ -110,7 +104,9 @@ fun AddToContactDialog(newNumber: String) { } EditorScreen( contact = it, - onClose = { contactToEdit = null } + onClose = { + contactToEdit = null + } ) { contact -> if (isNewContact) { contactsModel.createContact(context, contact) @@ -118,6 +114,7 @@ fun AddToContactDialog(newNumber: String) { contactsModel.updateContact(context, contact) } contactToEdit = null + onDismissRequest() } } } diff --git a/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/SmsThreadScreen.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/SmsThreadScreen.kt index 227eab55..a2e5d847 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/SmsThreadScreen.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/SmsThreadScreen.kt @@ -229,6 +229,8 @@ fun SmsThreadScreen( } if (showAddToContactDialog) { - AddToContactDialog(newNumber = address) + AddToContactDialog(newNumber = address) { + showAddToContactDialog = false + } } } diff --git a/app/src/main/java/com/bnyro/contacts/ui/activities/MainActivity.kt b/app/src/main/java/com/bnyro/contacts/ui/activities/MainActivity.kt index c217cb0d..1ec8e0d0 100644 --- a/app/src/main/java/com/bnyro/contacts/ui/activities/MainActivity.kt +++ b/app/src/main/java/com/bnyro/contacts/ui/activities/MainActivity.kt @@ -12,6 +12,7 @@ import androidx.activity.compose.setContent import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.platform.LocalContext @@ -54,8 +55,17 @@ class MainActivity : BaseActivity() { if (authSuccess) { NavContainer(HomeRoutes.all[initialTabIndex.coerceIn(0, 2)].route) + getInsertOrEditNumber()?.let { - AddToContactDialog(it) + var showAddToContactDialog by remember { + mutableStateOf(true) + } + + if (showAddToContactDialog) { + AddToContactDialog(it) { + showAddToContactDialog = false + } + } } getSharedVcfUri()?.let { ConfirmImportContactsDialog(contactsModel, it) From 85664b6841812da927558385766830015c47bd3d Mon Sep 17 00:00:00 2001 From: Bnyro Date: Tue, 29 Oct 2024 13:10:29 +0100 Subject: [PATCH 158/159] fix: crash when trying to block numbers (closes #415) --- .../presentation/screens/calllog/CallLogsScreen.kt | 3 ++- .../presentation/screens/sms/SmsListScreen.kt | 3 ++- .../com/bnyro/contacts/util/PermissionHelper.kt | 14 ++++++++++++++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/CallLogsScreen.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/CallLogsScreen.kt index 40856c42..3c0bdaae 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/CallLogsScreen.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/calllog/CallLogsScreen.kt @@ -79,6 +79,7 @@ import com.bnyro.contacts.presentation.screens.calllog.model.CallModel import com.bnyro.contacts.presentation.screens.contacts.model.ContactsModel import com.bnyro.contacts.presentation.screens.settings.model.ThemeModel import com.bnyro.contacts.util.IntentHelper +import com.bnyro.contacts.util.PermissionHelper @OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class) @@ -305,7 +306,7 @@ fun CallLogOptionsSheet( } LaunchedEffect(Unit) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - if (!BlockedNumberContract.canCurrentUserBlockNumbers(context)) return@LaunchedEffect + if (!PermissionHelper.canBlockNumbers(context)) return@LaunchedEffect isBlocked = BlockedNumberContract.isBlocked(context, log.phoneNumber) } } diff --git a/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/SmsListScreen.kt b/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/SmsListScreen.kt index b844a320..1f38d17c 100644 --- a/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/SmsListScreen.kt +++ b/app/src/main/java/com/bnyro/contacts/presentation/screens/sms/SmsListScreen.kt @@ -73,6 +73,7 @@ import com.bnyro.contacts.presentation.screens.sms.model.SmsModel import com.bnyro.contacts.util.CalendarUtils import com.bnyro.contacts.util.ExportHelper import com.bnyro.contacts.util.IntentHelper +import com.bnyro.contacts.util.PermissionHelper import com.bnyro.contacts.util.extension.toast import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -247,7 +248,7 @@ fun SMSThreadOptionsSheet( } LaunchedEffect(Unit) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - if (!BlockedNumberContract.canCurrentUserBlockNumbers(context)) return@LaunchedEffect + if (!PermissionHelper.canBlockNumbers(context)) return@LaunchedEffect isBlocked = BlockedNumberContract.isBlocked(context, thread.address) } } diff --git a/app/src/main/java/com/bnyro/contacts/util/PermissionHelper.kt b/app/src/main/java/com/bnyro/contacts/util/PermissionHelper.kt index 05dc952a..899c144f 100644 --- a/app/src/main/java/com/bnyro/contacts/util/PermissionHelper.kt +++ b/app/src/main/java/com/bnyro/contacts/util/PermissionHelper.kt @@ -3,7 +3,13 @@ package com.bnyro.contacts.util import android.app.Activity import android.content.Context import android.content.pm.PackageManager +import android.os.Build +import android.provider.BlockedNumberContract +import android.provider.Telephony +import android.telecom.TelecomManager +import androidx.annotation.RequiresApi import androidx.core.app.ActivityCompat +import androidx.core.content.getSystemService object PermissionHelper { fun checkPermissions(activity: Activity, permissions: Array): Boolean { @@ -25,4 +31,12 @@ object PermissionHelper { ) == PackageManager.PERMISSION_GRANTED } } + + @RequiresApi(Build.VERSION_CODES.N) + fun canBlockNumbers(context: Context): Boolean { + if (!BlockedNumberContract.canCurrentUserBlockNumbers(context)) return false + + return Telephony.Sms.getDefaultSmsPackage(context) == context.packageName || + context.getSystemService()?.defaultDialerPackage == context.packageName + } } From d0cf31c22d2723d708c6be1ff52c738963b715c6 Mon Sep 17 00:00:00 2001 From: Bnyro Date: Tue, 29 Oct 2024 15:25:30 +0100 Subject: [PATCH 159/159] ci: add release action --- .github/workflows/build-release.yml | 54 ++++++++++++++++++++ .idea/inspectionProfiles/Project_Default.xml | 27 ++++++++++ 2 files changed, 81 insertions(+) create mode 100644 .github/workflows/build-release.yml diff --git a/.github/workflows/build-release.yml b/.github/workflows/build-release.yml new file mode 100644 index 00000000..4bd6c97d --- /dev/null +++ b/.github/workflows/build-release.yml @@ -0,0 +1,54 @@ +name: Build and release app + +on: + workflow_dispatch: + push: + tags: + - 'v*.*' + +jobs: + build: + name: Build, sign and release app + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - uses: actions/checkout@v4 + + - name: Set up JDK + uses: actions/setup-java@v4 + with: + distribution: "zulu" + java-version: "17" + cache: "gradle" + + - name: Build APK + run: ./gradlew assembleRelease --no-daemon + + - name: Sign APK + uses: ilharp/sign-android-release@v1 + id: sign + with: + signingKey: ${{ secrets.KEYSTORE }} + keyAlias: ${{ secrets.SIGNING_KEY_ALIAS }} + keyStorePassword: ${{ secrets.SIGNING_STORE_PASSWORD }} + keyPassword: ${{ secrets.SIGNING_KEY_PASSWORD }} + + - name: Rename signed APK + run: | + mv "${{ steps.sign.outputs.signedFile }}" "app-release.apk" + + - name: Create changelog + id: changelog + uses: requarks/changelog-action@v1 + with: + token: ${{ github.token }} + tag: ${{ github.ref_name }} + + - name: Create release + uses: softprops/action-gh-release@v2 + with: + body: ${{ steps.changelog.outputs.changes }} + files: "app-release.apk" + fail_on_unmatched_files: true + make_latest: true diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml index 44ca2d9b..6195b36e 100644 --- a/.idea/inspectionProfiles/Project_Default.xml +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -1,6 +1,30 @@