1 /* <lambda>null2 * Copyright (C) 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 com.android.settings.network.telephony 18 19 import android.content.Context 20 import android.telephony.SubscriptionManager 21 import android.telephony.TelephonyManager 22 import android.telephony.ims.ImsManager 23 import android.util.Log 24 import androidx.annotation.VisibleForTesting 25 import androidx.lifecycle.LifecycleOwner 26 import androidx.preference.Preference 27 import androidx.preference.PreferenceScreen 28 import androidx.preference.TwoStatePreference 29 import com.android.settings.R 30 import com.android.settings.core.TogglePreferenceController 31 import com.android.settings.network.ims.VolteQueryImsState 32 import com.android.settings.network.ims.VtQueryImsState 33 import com.android.settings.network.telephony.Enhanced4gBasePreferenceController.On4gLteUpdateListener 34 import com.android.settings.network.telephony.MobileNetworkSettingsSearchIndex.MobileNetworkSettingsSearchItem 35 import com.android.settings.network.telephony.MobileNetworkSettingsSearchIndex.MobileNetworkSettingsSearchResult 36 import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle 37 import kotlinx.coroutines.flow.first 38 import kotlinx.coroutines.runBlocking 39 40 /** Preference controller for "Video Calling" */ 41 class VideoCallingPreferenceController 42 @JvmOverloads 43 constructor( 44 context: Context, 45 key: String, 46 private val callStateRepository: CallStateRepository = CallStateRepository(context), 47 private val videoCallingRepository: VideoCallingRepository = VideoCallingRepository(context), 48 ) : TogglePreferenceController(context, key), On4gLteUpdateListener { 49 50 private var subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID 51 private var preference: TwoStatePreference? = null 52 private var callingPreferenceCategoryController: CallingPreferenceCategoryController? = null 53 54 private var videoCallEditable = false 55 private var isInCall = false 56 57 /** Init instance of VideoCallingPreferenceController. */ 58 fun init( 59 subId: Int, 60 callingPreferenceCategoryController: CallingPreferenceCategoryController?, 61 ): VideoCallingPreferenceController { 62 this.subId = subId 63 this.callingPreferenceCategoryController = callingPreferenceCategoryController 64 65 return this 66 } 67 68 // Availability is controlled in onViewCreated() and VideoCallingSearchItem. 69 override fun getAvailabilityStatus() = AVAILABLE 70 71 override fun displayPreference(screen: PreferenceScreen) { 72 super.displayPreference(screen) 73 preference = screen.findPreference(preferenceKey) 74 Log.d(TAG, "init ui") 75 preference?.isVisible = false 76 callingPreferenceCategoryController?.updateChildVisible(preferenceKey, false) 77 } 78 79 override fun onViewCreated(viewLifecycleOwner: LifecycleOwner) { 80 videoCallingRepository.isVideoCallReadyFlow(subId) 81 .collectLatestWithLifecycle(viewLifecycleOwner) { isReady -> 82 Log.d(TAG, "isVideoCallReadyFlow: update visible") 83 preference?.isVisible = isReady 84 callingPreferenceCategoryController?.updateChildVisible(preferenceKey, isReady) 85 } 86 callStateRepository.callStateFlow(subId).collectLatestWithLifecycle(viewLifecycleOwner) { 87 callState -> 88 isInCall = callState != TelephonyManager.CALL_STATE_IDLE 89 updatePreference() 90 } 91 } 92 93 override fun updateState(preference: Preference) { 94 super.updateState(preference) 95 videoCallEditable = 96 queryVoLteState(subId).isEnabledByUser && queryImsState(subId).isAllowUserControl 97 updatePreference() 98 } 99 100 private fun updatePreference() { 101 preference?.isEnabled = videoCallEditable && !isInCall 102 preference?.isChecked = videoCallEditable && isChecked 103 } 104 105 override fun getSliceHighlightMenuRes() = NO_RES 106 107 override fun setChecked(isChecked: Boolean): Boolean { 108 if (!SubscriptionManager.isValidSubscriptionId(subId)) { 109 return false 110 } 111 val imsMmTelManager = ImsManager(mContext).getImsMmTelManager(subId) 112 try { 113 imsMmTelManager.isVtSettingEnabled = isChecked 114 return true 115 } catch (exception: IllegalArgumentException) { 116 Log.w(TAG, "[$subId] Unable to set VT status $isChecked", exception) 117 } 118 return false 119 } 120 121 override fun isChecked(): Boolean = queryImsState(subId).isEnabledByUser 122 123 override fun on4gLteUpdated() { 124 preference?.let { updateState(it) } 125 } 126 127 @VisibleForTesting fun queryImsState(subId: Int) = VtQueryImsState(mContext, subId) 128 129 @VisibleForTesting fun queryVoLteState(subId: Int) = VolteQueryImsState(mContext, subId) 130 131 companion object { 132 private const val TAG = "VideoCallingPreferenceController" 133 134 class VideoCallingSearchItem(private val context: Context) : 135 MobileNetworkSettingsSearchItem { 136 private val videoCallingRepository = VideoCallingRepository(context) 137 138 private fun isAvailable(subId: Int): Boolean = runBlocking { 139 videoCallingRepository.isVideoCallReadyFlow(subId).first() 140 } 141 142 override fun getSearchResult(subId: Int): MobileNetworkSettingsSearchResult? { 143 if (!isAvailable(subId)) return null 144 return MobileNetworkSettingsSearchResult( 145 key = "video_calling_key", 146 title = context.getString(R.string.video_calling_settings_title), 147 ) 148 } 149 } 150 } 151 } 152