1 /* 2 * Copyright 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package androidx.core.telecom 18 19 import android.content.Intent 20 import android.os.Build 21 import android.os.IBinder 22 import android.telecom.Call 23 import android.telecom.InCallService 24 import android.util.Log 25 import androidx.annotation.CallSuper 26 import androidx.annotation.RequiresApi 27 import androidx.core.telecom.extensions.CallExtensionScope 28 import androidx.core.telecom.extensions.CallExtensionScopeImpl 29 import androidx.core.telecom.extensions.CallExtensions 30 import androidx.core.telecom.util.ExperimentalAppActions 31 import androidx.lifecycle.Lifecycle 32 import androidx.lifecycle.LifecycleOwner 33 import androidx.lifecycle.ServiceLifecycleDispatcher 34 import androidx.lifecycle.lifecycleScope 35 import kotlinx.coroutines.CoroutineScope 36 import kotlinx.coroutines.SupervisorJob 37 import kotlinx.coroutines.launch 38 39 /** 40 * This class defines the Jetpack InCallService with the additional ability of defining a 41 * [LifecycleOwner] 42 */ 43 @RequiresApi(Build.VERSION_CODES.O) 44 @Suppress("ContextNameSuffix") 45 public open class InCallServiceCompat : InCallService(), LifecycleOwner, CallExtensions { 46 // Since we define this service as a LifecycleOwner, we need to implement this dispatcher as 47 // well. See [LifecycleService] for the example used to implement [LifecycleOwner]. 48 private val dispatcher = ServiceLifecycleDispatcher(this) 49 50 private companion object { 51 private val TAG = InCallService::class.simpleName 52 } 53 54 override val lifecycle: Lifecycle 55 get() = dispatcher.lifecycle 56 57 @CallSuper onCreatenull58 override fun onCreate() { 59 dispatcher.onServicePreSuperOnCreate() 60 super.onCreate() 61 } 62 63 @CallSuper 64 @Suppress("InvalidNullabilityOverride") onBindnull65 override fun onBind(intent: Intent?): IBinder? { 66 dispatcher.onServicePreSuperOnBind() 67 return super.onBind(intent) 68 } 69 70 // We do not use onStart, but if the client does for some reason, we still want to override to 71 // ensure the lifecycle events are consistent. 72 @Deprecated("Deprecated in Java") 73 @Suppress("DEPRECATION") 74 @CallSuper onStartnull75 override fun onStart(intent: Intent?, startId: Int) { 76 dispatcher.onServicePreSuperOnStart() 77 super.onStart(intent, startId) 78 } 79 80 // We do not use onStartCommand, but if the client does for some reason, we still want to ensure 81 // that the super is called (this command internally calls onStart) 82 @CallSuper onStartCommandnull83 override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { 84 return super.onStartCommand(intent, flags, startId) 85 } 86 87 @CallSuper onDestroynull88 override fun onDestroy() { 89 dispatcher.onServicePreSuperOnDestroy() 90 super.onDestroy() 91 } 92 93 /** 94 * Connects extensions to the provided [Call], allowing the call to support additional optional 95 * behaviors beyond the traditional call state management. 96 * 97 * @param call The Call to connect extensions on. 98 * @param init The scope used to initialize and manage extensions in the scope of the Call. 99 * @see CallExtensions.connectExtensions 100 */ 101 @ExperimentalAppActions connectExtensionsnull102 override suspend fun connectExtensions(call: Call, init: CallExtensionScope.() -> Unit) { 103 // Attach this to the scope of the InCallService so it does not outlive its lifecycle 104 // Use a supervisor job to ensure that any exceptions that are encountered here do not kill 105 // the lifecycleScope. 106 CoroutineScope(lifecycleScope.coroutineContext + SupervisorJob()) 107 .launch { 108 val scope = CallExtensionScopeImpl(applicationContext, this, call) 109 Log.v(TAG, "connectExtensions: calling init") 110 scope.init() 111 Log.v(TAG, "connectExtensions: connecting extensions") 112 scope.connectExtensionSession() 113 } 114 .join() 115 Log.d(TAG, "connectExtensions: complete") 116 } 117 } 118