• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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