Skip to content

Commit

Permalink
feat: in call dialpad
Browse files Browse the repository at this point in the history
  • Loading branch information
SuhasDissa committed Apr 25, 2024
1 parent 3dac001 commit 64d6ed9
Show file tree
Hide file tree
Showing 6 changed files with 180 additions and 6 deletions.
28 changes: 28 additions & 0 deletions app/src/main/java/com/bnyro/contacts/services/CallService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import android.content.ContentResolver
import android.content.Intent
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
Expand All @@ -25,15 +27,19 @@ import com.bnyro.contacts.util.ContactsHelper
import com.bnyro.contacts.util.NotificationHelper
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext


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

override fun onCreate() {
super.onCreate()
Expand Down Expand Up @@ -61,6 +67,7 @@ class CallService : InCallService() {
super.onCallAdded(call)
call.registerCallback(callCallback)
CallManager.setCall(call)
currentCall = call

scope.launch {
val callerNumber = CallManager.callerDisplayNumber
Expand Down Expand Up @@ -109,6 +116,7 @@ class CallService : InCallService() {

override fun onCallRemoved(call: Call) {
super.onCallRemoved(call)
currentCall = null
NotificationManagerCompat.from(this).cancel(CALL_NOTIFICATION_ID)
call.unregisterCallback(callCallback)
CallManager.setCall(null)
Expand Down Expand Up @@ -159,7 +167,27 @@ class CallService : InCallService() {
.build()
}

fun playDtmfTone(digit: Char) {
scope.launch {
currentCall?.playDtmfTone(digit)
delay(200)
currentCall?.stopDtmfTone()
}
}

override fun onBind(intent: Intent): IBinder? {
if (intent.action == CUSTOM_BIND_ACTION) {
return binder;
}
return super.onBind(intent)
}

inner class LocalBinder : Binder() {
fun getService() = this@CallService
}

companion object {
const val CALL_NOTIFICATION_ID = 10
const val CUSTOM_BIND_ACTION = "custom_bind"
}
}
34 changes: 34 additions & 0 deletions app/src/main/java/com/bnyro/contacts/ui/activities/CallActivity.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
package com.bnyro.contacts.ui.activities

import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.os.Bundle
import android.os.IBinder
import android.view.Window
import android.view.WindowManager
import androidx.activity.compose.setContent
Expand All @@ -11,11 +16,27 @@ 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.ui.theme.ConnectYouTheme

class CallActivity : BaseActivity() {

lateinit var callService: CallService

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
}

override fun onServiceDisconnected(p0: ComponentName?) {
dialerModel.playDtmfTone = {}
}
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
requestWindowFeature(Window.FEATURE_NO_TITLE)
Expand All @@ -41,6 +62,19 @@ class CallActivity : BaseActivity() {
}
}

override fun onStart() {
super.onStart()
Intent(this, CallService::class.java).setAction(CallService.CUSTOM_BIND_ACTION)
.also { intent ->
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE)
}
}

override fun onStop() {
super.onStop()
unbindService(serviceConnection)
}

companion object {
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
Expand Down
59 changes: 59 additions & 0 deletions app/src/main/java/com/bnyro/contacts/ui/components/NumberInput.kt
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,36 @@ fun NumberInput(
}
}

@SuppressLint("NewApi")
@Composable
fun NumberInput(
onNumberInput: (String) -> Unit
) {
val buttonSpacing = 8.dp
Column(
modifier = Modifier
.padding(8.dp)
.fillMaxWidth(),
verticalArrangement = Arrangement.spacedBy(buttonSpacing)
) {
keypadNumbers.forEach { col ->
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(buttonSpacing)
) {
col.forEach {
NumpadButton(
text = it,
onClick = {
onNumberInput(it)
}
)
}
}
}
}
}

@Composable
fun RowScope.NumpadButton(
text: String,
Expand Down Expand Up @@ -228,3 +258,32 @@ fun ColumnScope.PhoneNumberDisplay(displayText: String) {
}
}
}

@Composable
fun PhoneNumberOnlyDisplay(displayText: String) {
Column(
modifier = Modifier
.fillMaxWidth()
) {
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
)
}
}
}
11 changes: 11 additions & 0 deletions app/src/main/java/com/bnyro/contacts/ui/models/DialerModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ class DialerModel : ViewModel() {
var currentMuteState by mutableStateOf(false)
var currentSpeakerState by mutableStateOf(false)

var playDtmfTone: (digit: Char) -> Unit = {}

var dialpadNumber by mutableStateOf("")
private set

fun requestDefaultDialerApp(context: Context) {
val telecomManager = context.getSystemService(Context.TELECOM_SERVICE) as TelecomManager?
val isAlreadyDefaultDialer =
Expand All @@ -29,6 +34,12 @@ class DialerModel : ViewModel() {
}
}

fun onDialpadButtonPress(digit: String) {
dialpadNumber += digit
playDtmfTone(digit.first())

}

fun toggleMute(context: Context) {
val audioManager = context.getSystemService(AudioManager::class.java)!!
audioManager.isMicrophoneMute = !currentMuteState
Expand Down
53 changes: 47 additions & 6 deletions app/src/main/java/com/bnyro/contacts/ui/screens/DialerScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,30 @@ 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.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Call
import androidx.compose.material.icons.filled.CallEnd
import androidx.compose.material.icons.filled.MicOff
import androidx.compose.material.icons.filled.VolumeUp
import androidx.compose.material.icons.rounded.Dialpad
import androidx.compose.material.icons.rounded.MicOff
import androidx.compose.material.icons.rounded.VolumeUp
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExtendedFloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
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.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
Expand All @@ -37,10 +44,13 @@ 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.util.CallManager

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun DialerScreen(
contactsModel: ContactsModel,
Expand All @@ -65,6 +75,8 @@ fun DialerScreen(
handler.postDelayed(::updateTime, 1000L)
}

var showDialPad by remember { mutableStateOf(false) }

DisposableEffect(Unit) {
val listener: (Int) -> Unit = {
callState = it
Expand Down Expand Up @@ -117,7 +129,7 @@ fun DialerScreen(
item {
DialerButton(
isEnabled = dialerModel.currentMuteState,
icon = Icons.Default.MicOff,
icon = Icons.Rounded.MicOff,
hint = stringResource(R.string.mute)
) {
dialerModel.toggleMute(context)
Expand All @@ -126,20 +138,31 @@ fun DialerScreen(
item {
DialerButton(
isEnabled = dialerModel.currentSpeakerState,
icon = Icons.Default.VolumeUp,
icon = Icons.Rounded.VolumeUp,
hint = stringResource(R.string.speakers)
) {
dialerModel.toggleSpeakers(context)
}
}
item {
DialerButton(
isEnabled = showDialPad,
icon = Icons.Rounded.Dialpad,
hint = stringResource(R.string.dial_pad)
) {
showDialPad = true
}
}

}
Spacer(modifier = Modifier.weight(1f))
Row {
if (callState == Call.STATE_RINGING) {
ExtendedFloatingActionButton(
onClick = { CallManager.acceptCall() },
containerColor = MaterialTheme.colorScheme.primary,
contentColor = MaterialTheme.colorScheme.onPrimary
contentColor = MaterialTheme.colorScheme.onPrimary,
shape = RoundedCornerShape(50)
) {
Icon(Icons.Default.Call, contentDescription = null)
}
Expand All @@ -150,11 +173,29 @@ fun DialerScreen(
ExtendedFloatingActionButton(
onClick = { CallManager.cancelCall() },
containerColor = MaterialTheme.colorScheme.error,
contentColor = MaterialTheme.colorScheme.onError
contentColor = MaterialTheme.colorScheme.onError,
shape = RoundedCornerShape(50)
) {
Icon(Icons.Default.CallEnd, contentDescription = null)
}
}
Spacer(modifier = Modifier.height(50.dp))
}

if (showDialPad) {
val state = rememberModalBottomSheetState(skipPartiallyExpanded = true)
ModalBottomSheet(onDismissRequest = { showDialPad = false }, sheetState = state) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 8.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
PhoneNumberOnlyDisplay(displayText = dialerModel.dialpadNumber)
NumberInput(
onNumberInput = dialerModel::onDialpadButtonPress
)
}
}
}
}
1 change: 1 addition & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -141,4 +141,5 @@
<string name="decline">Decline</string>
<string name="accept">Accept</string>
<string name="unknown_number">Unknown Number</string>
<string name="dial_pad">Dialpad</string>
</resources>

0 comments on commit 64d6ed9

Please sign in to comment.