1. ConnectionService와 InCallService의 차이
InCallService
대부분의 스펙은 구현이 되어있으며, 구현된 항목을 선언하고 요청만 해주면 구현할 수 있다.
ConnectionService는 VOIP개발에 더 적합하다.
gsm통화를 개발하기 위해 각종 편의사항이 제공되는 InCallService와는 다르게,
연결을 직접 받아 등록하고 통화의 시작, 종료까지 모두 구현해주어야 한다.
그래서 VOIP구현엔 오픈소스 라이브러리를 주로 사용한다 (kakao talk도 보이스톡도 그렇다!)
2. InCallService 사용을 위한 사전 준비
기본 통화앱이 되도록 Manifest 및 userpermission 수정
ㄱ. Manifest - ues permission tags
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.CALL_LOG"/>
<uses-permission android:name="android.permission.CALL_PHONE" />
//OUT_GOING_CALL -> CALL_LOG : 명확환 permission 관리를 위해 세분화되었음
매니패스트에 선언 후에 사용자에게 권한을 요청합니다.
var permissions =
arrayOf(
Manifest.permission.READ_PHONE_STATE,
Manifest.permission.CALL_LOG,
Manifest.permission.CALL_PHONE
)
val permissionLauncher = registerForActivityResult(ActivityResultContracts.RequestPermission()) {
isGranted: Boolean ->
if(!isGranted){
Toast.makeText(this,"권한이 없다면 앱이 정상적으로 동작하지 않습니다.", Toast.LENGTH_SHORT).show()
} else {
//허용시 수행할 작업 입력
}
}
}
for (permission in permissions){
permissionLauncher.launch(permission)
}
ㄴ. Manifest - addtional Activity tag
<activity android:name="your.package.YourDialerActivity"
android:label="@string/yourDialerActivityLabel">
<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.DIAL" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="tel" />
</intent-filter>
</activity>
기본 통화 앱 설정은 manifest의 추가와 더불어 Activity에서 사용자에게 요청을 해야 합니다.
API 30부터는 RoleManager 및 registerForActivityResult를 사용해 요청해야 합니다.
registerForActivityResult에 대한 포스팅도 있으니, 여기를 참조하세요.
a. less API 29 use ActivityForResult
private void offerReplacingDefaultDialer() {
val telecomManager = getSystemService(TELECOM_SERVICE) as TelecomManager
lateinit var defualtAppIntent: Intent
if (!getPackageName().equals(telecomManager.getDefaultDialerPackage())) {
Intent intent = new Intent(ACTION_CHANGE_DEFAULT_DIALER)
.putExtra(EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME, getPackageName())
startActivity(intent)
}
}
b. more than API 30 use RoleManager
lateinit var defualtAppIntent: Intent
val telecomManager = getSystemService(TELECOM_SERVICE) as TelecomManager
lateinit var defualtAppIntent: Intent
val roleManager = getSystemService(ROLE_SERVICE) as RoleManager
val intent = roleManager.createRequestRoleIntent(RoleManager.ROLE_DIALER)
defualtAppIntent = intent
var requireDefualtDialerLauncher =
registerForActivityResult(StartActivityForResult()){
val thisDefaultDialer = packageName == telecomManager.defaultDialerPackage
if(!thisDefaultDialer){
Toast.makeText(this,"기본 전화 앱 설정은 필수입니다.", Toast.LENGTH_SHORT).show()
finish()
}
}
if(packageName != telecomManager.defaultDialerPackage){
requireDefualtDialerLauncher.launch(defualtAppIntent)
}
3. InCallService 사용을 위한 등록 및 구현
<service android:name="your.package.YourInCallServiceImplementation"
android:permission="android.permission.BIND_INCALL_SERVICE">
<meta-data android:name="android.telecom.IN_CALL_SERVICE_UI" android:value="true" />
<meta-data android:name="android.telecom.IN_CALL_SERVICE_RINGING"
android:value="true" />
<intent-filter>
<action android:name="android.telecom.InCallService"/>
</intent-filter>
</service>
class YourInCallServiceImplementation: InCallService() {
override fun onCallAdded(call: Call?) {
super.onCallAdded(call)
}
}
4. 상세 구현
지금까지 InCallService를 구축하기 위한 기본적인 준비였습니다.
준비가 좀 길었다구요? 구현은 간단합니다.
YourInCallServiceImplementation에서 override한 onCallAdded 메서드 안에서, 매개변수로 받은 call 객체만 다뤄주면 됩니다.
class YourInCallServiceImplementation: InCallService() {
static lastState = ""
override fun onCallAdded(call: Call?) {
val callState = call.detail.state
when (callState) {
STATE_RINGING -> println("ringing")
STATE_DIALING -> println("dialing")
STATE_ACTIVE -> println("active")
STATE_DISCONNECTED -> println("disconnected")
}
}
}
위에서 순서대로 각각 수신중, 발신중, 통화중, call 종료의 상태를 나타냅니다.
call의 상태가 변경될때마다 호출되므로, 각 상태에 대한 분기작업을 실행하면 됩니다.
각 상황에 맞는 activity를 팝업하면서 call 객체를 넘겨주면 handling이 되겠죠?
여기서 observe 패턴을 RxJava나 Corutine으로 구현해주면 더욱더 짜임새 높은 코드가 될 겁니다.
'IDE & Framework > Android' 카테고리의 다른 글
[Kotlin] Firebase Crashlytics 적용시키기 (간단!) (0) | 2022.01.09 |
---|---|
[JAVA] Base64 incoding, decoding (0) | 2022.01.06 |
[Kotlin]ActivityForResult 및 checkSelfPerission 을 permissionLauncher로 대체하기 (0) | 2021.12.19 |
[Kotlin] Fragment (2) - 기초 예제 (0) | 2021.12.19 |
[Kotlin] Fragment (1) - Introduce (0) | 2021.12.12 |