Skip to content

Commit

Permalink
feat: inbuilt dialer including call history
Browse files Browse the repository at this point in the history
  • Loading branch information
Bnyro committed Nov 19, 2023
1 parent adcf6f3 commit cb09caf
Show file tree
Hide file tree
Showing 18 changed files with 740 additions and 56 deletions.
2 changes: 1 addition & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ android {

defaultConfig {
applicationId = "com.bnyro.contacts"
minSdk = 21
minSdk = 23
targetSdk = 33
versionCode = 27
versionName = "8.1"
Expand Down
48 changes: 48 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,18 @@
<!-- Sms notifications -->
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />

<!-- Dialing support -->
<uses-permission android:name="android.permission.MANAGE_OWN_CALLS" />
<uses-permission android:name="android.permission.ANSWER_PHONE_CALLS" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.READ_CALL_LOG" />
<uses-permission android:name="android.permission.WRITE_CALL_LOG" />
<uses-permission
android:name="android.permission.MODIFY_PHONE_STATE"
tools:ignore="ProtectedPermissions" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />

<!-- Opt out from network permissions declared by whatever dependency -->
<uses-permission
android:name="android.permission.ACCESS_NETWORK_STATE"
Expand Down Expand Up @@ -134,6 +146,24 @@
<data android:scheme="mms" />
<data android:scheme="mmsto" />
</intent-filter>

<!-- Receive phone calls -->
<intent-filter>
<action android:name="android.intent.action.DIAL" />

<category android:name="android.intent.category.DEFAULT" />
</intent-filter>

<intent-filter>
<action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.DIAL" />

<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />

<data android:scheme="tel" />
</intent-filter>

</activity>

<activity
Expand Down Expand Up @@ -173,6 +203,10 @@

</activity>

<activity
android:name=".ui.activities.CallActivity"
android:exported="false" />

<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"
Expand Down Expand Up @@ -229,6 +263,20 @@
<data android:scheme="mmsto" />
</intent-filter>
</service>

<service
android:name=".services.CallService"
android:exported="true"
android:foregroundServiceType="phoneCall"
android:permission="android.permission.BIND_INCALL_SERVICE">
<meta-data
android:name="android.telecom.IN_CALL_SERVICE_UI"
android:value="true" />

<intent-filter>
<action android:name="android.telecom.InCallService" />
</intent-filter>
</service>
</application>

</manifest>
6 changes: 6 additions & 0 deletions app/src/main/java/com/bnyro/contacts/ext/String.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.bnyro.contacts.ext

fun String.removeLastChar(): String {
return if (isEmpty()) this
else substring(0, length - 1)
}
8 changes: 8 additions & 0 deletions app/src/main/java/com/bnyro/contacts/obj/CallLogEntry.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.bnyro.contacts.obj

data class CallLogEntry(
val phoneNumber: String,
val type: Int,
val time: Long,
val duration: Long
)
30 changes: 30 additions & 0 deletions app/src/main/java/com/bnyro/contacts/services/CallService.kt
Original file line number Diff line number Diff line change
@@ -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.flags = Intent.FLAG_ACTIVITY_NEW_TASK
startActivity(intent)
CallManager.setCall(call)
}

override fun onCallRemoved(call: Call) {
super.onCallRemoved(call)
call.unregisterCallback(callCallback)
CallManager.setCall(null)
}
}
38 changes: 38 additions & 0 deletions app/src/main/java/com/bnyro/contacts/ui/activities/CallActivity.kt
Original file line number Diff line number Diff line change
@@ -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) {
finish()
}
}
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
}
Expand Down Expand Up @@ -79,6 +83,7 @@ class MainActivity : BaseActivity() {
)
)
}

intent?.getStringExtra("action") == "create" -> ContactData()
else -> null
}
Expand Down Expand Up @@ -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) {
Expand All @@ -133,5 +145,6 @@ class MainActivity : BaseActivity() {

companion object {
var smsModel: SmsModel? = null
var dialerModel: DialerModel? = null
}
}
Original file line number Diff line number Diff line change
@@ -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
)
}
}
78 changes: 32 additions & 46 deletions app/src/main/java/com/bnyro/contacts/ui/components/ContactItem.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
Loading

0 comments on commit cb09caf

Please sign in to comment.