Auto enable Telecom Manager feature when app starts, asking for permissions if needed

This commit is contained in:
Sylvain Berfini 2021-11-12 10:34:56 +01:00
parent 701c464882
commit 5d2c1cb5d1
11 changed files with 126 additions and 28 deletions

View file

@ -17,7 +17,7 @@ Group changes to describe their impact on the project, as follows:
- Voice recordings in chat feature - Voice recordings in chat feature
- Allow video recording in chat file sharing - Allow video recording in chat file sharing
- Unread messages indicator in chat conversation that separates read & unread messages - Unread messages indicator in chat conversation that separates read & unread messages
- Notify incoming/outgoing calls on bluetooth devices using self-managed connections from telecom manager API - Notify incoming/outgoing calls on bluetooth devices using self-managed connections from telecom manager API (disables SDK audio focus)
- New video call UI on foldable device like Galaxy Z Fold - New video call UI on foldable device like Galaxy Z Fold
- Setting to automatically record all calls - Setting to automatically record all calls

View file

@ -23,6 +23,7 @@ import android.os.Bundle
import androidx.coordinatorlayout.widget.CoordinatorLayout import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import org.linphone.LinphoneApplication.Companion.corePreferences
import org.linphone.R import org.linphone.R
import org.linphone.activities.GenericActivity import org.linphone.activities.GenericActivity
import org.linphone.activities.SnackBarActivity import org.linphone.activities.SnackBarActivity
@ -40,6 +41,8 @@ class AssistantActivity : GenericActivity(), SnackBarActivity {
sharedViewModel = ViewModelProvider(this)[SharedAssistantViewModel::class.java] sharedViewModel = ViewModelProvider(this)[SharedAssistantViewModel::class.java]
coordinator = findViewById(R.id.coordinator) coordinator = findViewById(R.id.coordinator)
corePreferences.firstStart = false
} }
override fun showSnackBar(resourceId: Int) { override fun showSnackBar(resourceId: Int) {

View file

@ -148,7 +148,6 @@ class MainActivity : GenericActivity(), SnackBarActivity, NavController.OnDestin
if (coreContext.core.accountList.isEmpty()) { if (coreContext.core.accountList.isEmpty()) {
if (corePreferences.firstStart) { if (corePreferences.firstStart) {
corePreferences.firstStart = false
startActivity(Intent(this, AssistantActivity::class.java)) startActivity(Intent(this, AssistantActivity::class.java))
} }
} }

View file

@ -45,9 +45,11 @@ import org.linphone.activities.main.viewmodels.DialogViewModel
import org.linphone.activities.main.viewmodels.SharedMainViewModel import org.linphone.activities.main.viewmodels.SharedMainViewModel
import org.linphone.activities.navigateToConfigFileViewer import org.linphone.activities.navigateToConfigFileViewer
import org.linphone.activities.navigateToContacts import org.linphone.activities.navigateToContacts
import org.linphone.compatibility.Compatibility
import org.linphone.core.tools.Log import org.linphone.core.tools.Log
import org.linphone.databinding.DialerFragmentBinding import org.linphone.databinding.DialerFragmentBinding
import org.linphone.mediastream.Version import org.linphone.mediastream.Version
import org.linphone.telecom.TelecomHelper
import org.linphone.utils.AppUtils import org.linphone.utils.AppUtils
import org.linphone.utils.DialogUtils import org.linphone.utils.DialogUtils
import org.linphone.utils.Event import org.linphone.utils.Event
@ -108,25 +110,6 @@ class DialerFragment : SecureFragment<DialerFragmentBinding>() {
} }
} }
if (arguments?.containsKey("Transfer") == true) {
sharedViewModel.pendingCallTransfer = arguments?.getBoolean("Transfer") ?: false
Log.i("[Dialer] Is pending call transfer: ${sharedViewModel.pendingCallTransfer}")
}
if (arguments?.containsKey("URI") == true) {
val address = arguments?.getString("URI") ?: ""
Log.i("[Dialer] Found URI to call: $address")
val skipAutoCall = arguments?.getBoolean("SkipAutoCallStart") ?: false
if (corePreferences.callRightAway && !skipAutoCall) {
Log.i("[Dialer] Call right away setting is enabled, start the call to $address")
viewModel.directCall(address)
} else {
sharedViewModel.dialerUri = address
}
}
arguments?.clear()
viewModel.enteredUri.observe( viewModel.enteredUri.observe(
viewLifecycleOwner, viewLifecycleOwner,
{ {
@ -166,6 +149,30 @@ class DialerFragment : SecureFragment<DialerFragmentBinding>() {
} }
) )
if (corePreferences.firstStart) {
Log.w("[Dialer] First start detected, wait for assistant to be finished to check for update & request permissions")
return
}
if (arguments?.containsKey("Transfer") == true) {
sharedViewModel.pendingCallTransfer = arguments?.getBoolean("Transfer") ?: false
Log.i("[Dialer] Is pending call transfer: ${sharedViewModel.pendingCallTransfer}")
}
if (arguments?.containsKey("URI") == true) {
val address = arguments?.getString("URI") ?: ""
Log.i("[Dialer] Found URI to call: $address")
val skipAutoCall = arguments?.getBoolean("SkipAutoCallStart") ?: false
if (corePreferences.callRightAway && !skipAutoCall) {
Log.i("[Dialer] Call right away setting is enabled, start the call to $address")
viewModel.directCall(address)
} else {
sharedViewModel.dialerUri = address
}
}
arguments?.clear()
Log.i("[Dialer] Pending call transfer mode = ${sharedViewModel.pendingCallTransfer}") Log.i("[Dialer] Pending call transfer mode = ${sharedViewModel.pendingCallTransfer}")
viewModel.transferVisibility.value = sharedViewModel.pendingCallTransfer viewModel.transferVisibility.value = sharedViewModel.pendingCallTransfer
@ -205,18 +212,72 @@ class DialerFragment : SecureFragment<DialerFragmentBinding>() {
Log.i("[Dialer] READ_PHONE_STATE permission has been granted") Log.i("[Dialer] READ_PHONE_STATE permission has been granted")
coreContext.initPhoneStateListener() coreContext.initPhoneStateListener()
} }
checkTelecomManagerPermissions()
} else if (requestCode == 1) {
var allGranted = true
for (result in grantResults) {
if (result != PackageManager.PERMISSION_GRANTED) {
allGranted = false
}
}
if (allGranted) {
Log.i("[Dialer] Telecom Manager permission have been granted")
enableTelecomManager()
} else {
Log.w("[Dialer] Telecom Manager permission have been denied (at least one of them)")
}
} }
super.onRequestPermissionsResult(requestCode, permissions, grantResults) super.onRequestPermissionsResult(requestCode, permissions, grantResults)
} }
@TargetApi(Version.API23_MARSHMALLOW_60) @TargetApi(Version.API23_MARSHMALLOW_60)
private fun checkPermissions() { private fun checkPermissions() {
checkReadPhoneStatePermission()
if (Version.sdkAboveOrEqual(Version.API26_O_80) && PermissionHelper.get().hasReadPhoneStatePermission()) {
// Don't check the following the previous permission is being asked
checkTelecomManagerPermissions()
}
}
@TargetApi(Version.API23_MARSHMALLOW_60)
private fun checkReadPhoneStatePermission() {
if (!PermissionHelper.get().hasReadPhoneStatePermission()) { if (!PermissionHelper.get().hasReadPhoneStatePermission()) {
Log.i("[Dialer] Asking for READ_PHONE_STATE permission") Log.i("[Dialer] Asking for READ_PHONE_STATE permission")
requestPermissions(arrayOf(Manifest.permission.READ_PHONE_STATE), 0) requestPermissions(arrayOf(Manifest.permission.READ_PHONE_STATE), 0)
} }
} }
@TargetApi(Version.API26_O_80)
private fun checkTelecomManagerPermissions() {
if (!corePreferences.useTelecomManager) {
Log.i("[Dialer] Telecom Manager feature is disabled")
if (corePreferences.manuallyDisabledTelecomManager) {
Log.w("[Dialer] User has manually disabled Telecom Manager feature")
} else {
if (PermissionHelper.get().hasTelecomManagerPermissions()) {
enableTelecomManager()
} else {
Log.i("[Dialer] Asking for Telecom Manager permissions")
Compatibility.requestTelecomManagerPermission(requireActivity(), 1)
}
}
} else {
Log.i("[Dialer] Telecom Manager feature is already enabled")
}
}
@TargetApi(Version.API26_O_80)
private fun enableTelecomManager() {
Log.i("[Dialer] Telecom Manager permissions granted")
if (!TelecomHelper.exists()) {
Log.i("[Dialer] Creating Telecom Helper")
TelecomHelper.create(requireContext())
} else {
Log.e("[Dialer] Telecom Manager was already created ?!")
}
corePreferences.useTelecomManager = true
}
private fun displayDebugPopup() { private fun displayDebugPopup() {
val alertDialog = MaterialAlertDialogBuilder(requireContext()) val alertDialog = MaterialAlertDialogBuilder(requireContext())
alertDialog.setTitle(getString(R.string.debug_popup_title)) alertDialog.setTitle(getString(R.string.debug_popup_title))

View file

@ -19,7 +19,6 @@
*/ */
package org.linphone.activities.main.settings.fragments package org.linphone.activities.main.settings.fragments
import android.Manifest
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.net.Uri import android.net.Uri
@ -95,11 +94,7 @@ class CallSettingsFragment : GenericSettingFragment<SettingsCallFragmentBinding>
{ {
it.consume { it.consume {
if (!PermissionHelper.get().hasTelecomManagerPermissions()) { if (!PermissionHelper.get().hasTelecomManagerPermissions()) {
val permissions = arrayOf( Compatibility.requestTelecomManagerPermission(requireActivity(), 1)
Manifest.permission.READ_PHONE_NUMBERS,
Manifest.permission.MANAGE_OWN_CALLS
)
requestPermissions(permissions, 1)
} else if (!TelecomHelper.exists()) { } else if (!TelecomHelper.exists()) {
corePreferences.useTelecomManager = true corePreferences.useTelecomManager = true
Log.w("[Telecom Helper] Doesn't exists yet, creating it") Log.w("[Telecom Helper] Doesn't exists yet, creating it")

View file

@ -73,6 +73,9 @@ class CallSettingsViewModel : GenericSettingsViewModel() {
TelecomHelper.get().removeAccount() TelecomHelper.get().removeAccount()
TelecomHelper.get().destroy() TelecomHelper.get().destroy()
TelecomHelper.destroy() TelecomHelper.destroy()
Log.w("[Call Settings] Disabling Telecom Manager auto-enable")
prefs.manuallyDisabledTelecomManager = true
} }
prefs.useTelecomManager = newValue prefs.useTelecomManager = newValue
} }

View file

@ -19,6 +19,7 @@
*/ */
package org.linphone.compatibility package org.linphone.compatibility
import android.Manifest
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.annotation.TargetApi import android.annotation.TargetApi
import android.app.Activity import android.app.Activity
@ -138,5 +139,15 @@ class Api26Compatibility {
fun changeAudioRouteForTelecomManager(connection: NativeCallWrapper, route: Int) { fun changeAudioRouteForTelecomManager(connection: NativeCallWrapper, route: Int) {
connection.setAudioRoute(route) connection.setAudioRoute(route)
} }
fun requestTelecomManagerPermission(activity: Activity, code: Int) {
activity.requestPermissions(
arrayOf(
Manifest.permission.READ_PHONE_STATE,
Manifest.permission.MANAGE_OWN_CALLS
),
code
)
}
} }
} }

View file

@ -45,6 +45,16 @@ class Api30Compatibility {
activity.requestPermissions(arrayOf(Manifest.permission.READ_PHONE_NUMBERS), code) activity.requestPermissions(arrayOf(Manifest.permission.READ_PHONE_NUMBERS), code)
} }
fun requestTelecomManagerPermission(activity: Activity, code: Int) {
activity.requestPermissions(
arrayOf(
Manifest.permission.READ_PHONE_NUMBERS,
Manifest.permission.MANAGE_OWN_CALLS
),
code
)
}
fun removeChatRoomShortcut(context: Context, chatRoom: ChatRoom) { fun removeChatRoomShortcut(context: Context, chatRoom: ChatRoom) {
val shortcutManager = context.getSystemService(ShortcutManager::class.java) val shortcutManager = context.getSystemService(ShortcutManager::class.java)
val id = LinphoneUtils.getChatRoomId(chatRoom.localAddress, chatRoom.peerAddress) val id = LinphoneUtils.getChatRoomId(chatRoom.localAddress, chatRoom.peerAddress)

View file

@ -62,6 +62,14 @@ class Compatibility {
Api29Compatibility.requestReadPhoneStatePermission(activity, code) Api29Compatibility.requestReadPhoneStatePermission(activity, code)
} }
} }
// See https://developer.android.com/about/versions/11/privacy/permissions#phone-numbers
fun requestTelecomManagerPermission(activity: Activity, code: Int) {
if (Version.sdkAboveOrEqual(Version.API30_ANDROID_11)) {
Api30Compatibility.requestTelecomManagerPermission(activity, code)
} else {
Api26Compatibility.requestTelecomManagerPermission(activity, code)
}
}
fun getDeviceName(context: Context): String { fun getDeviceName(context: Context): String {
return when (Version.sdkAboveOrEqual(Version.API25_NOUGAT_71)) { return when (Version.sdkAboveOrEqual(Version.API25_NOUGAT_71)) {

View file

@ -317,6 +317,13 @@ class CorePreferences constructor(private val context: Context) {
config.setBool("audio", "android_disable_audio_focus_requests", value) config.setBool("audio", "android_disable_audio_focus_requests", value)
} }
// We will try to auto enable Telecom Manager feature, but in case user disables it don't try again
var manuallyDisabledTelecomManager: Boolean
get() = config.getBool("app", "user_disabled_self_managed_telecom_manager", false)
set(value) {
config.setBool("app", "user_disabled_self_managed_telecom_manager", value)
}
var fullScreenCallUI: Boolean var fullScreenCallUI: Boolean
get() = config.getBool("app", "full_screen_call", true) get() = config.getBool("app", "full_screen_call", true)
set(value) { set(value) {

View file

@ -204,7 +204,8 @@
layout="@layout/settings_widget_switch" layout="@layout/settings_widget_switch"
linphone:title="@{@string/call_settings_pause_calls_lost_audio_focus_title}" linphone:title="@{@string/call_settings_pause_calls_lost_audio_focus_title}"
linphone:listener="@{viewModel.pauseCallsWhenAudioFocusIsLostListener}" linphone:listener="@{viewModel.pauseCallsWhenAudioFocusIsLostListener}"
linphone:checked="@={viewModel.pauseCallsWhenAudioFocusIsLost}"/> linphone:checked="@={viewModel.pauseCallsWhenAudioFocusIsLost}"
linphone:enabled="@{!viewModel.useTelecomManager}"/>
<include <include
layout="@layout/settings_widget_basic" layout="@layout/settings_widget_basic"