Bumped dependencies & improved gradle files + new bullseye based clean docker image using JDK 17

This commit is contained in:
Sylvain Berfini 2023-04-14 10:43:10 +02:00
parent b80f2fc9a2
commit 33b4c09ffd
158 changed files with 3285 additions and 996 deletions

View file

@ -2,15 +2,15 @@ job-android:
stage: build
tags: [ "docker-android" ]
image: gitlab.linphone.org:4567/bc/public/linphone-android/bc-dev-android:20220609_android_33
image: gitlab.linphone.org:4567/bc/public/linphone-android/bc-dev-android:20230414_bullseye_jdk_17_cleaned
before_script:
- if ! [ -z ${SCP_PRIVATE_KEY+x} ]; then eval $(ssh-agent -s); fi
- if ! [ -z ${SCP_PRIVATE_KEY+x} ]; then echo "$SCP_PRIVATE_KEY" | tr -d '\r' | ssh-add - > /dev/null; fi
- echo "$ANDROID_SETTINGS_GRADLE" > settings.gradle
- if ! [ -z ${ANDROID_SETTINGS_GRADLE+x} ]; then echo "$ANDROID_SETTINGS_GRADLE" > settings.gradle; fi
- git config --global --add safe.directory /builds/BC/public/linphone-android
script:
- sdkmanager
- scp -oStrictHostKeyChecking=no $DEPLOY_SERVER:$ANDROID_KEYSTORE_PATH app/
- scp -oStrictHostKeyChecking=no $DEPLOY_SERVER:$ANDROID_GOOGLE_SERVICES_PATH app/
- echo storePassword=$ANDROID_KEYSTORE_PASSWORD > keystore.properties

View file

@ -2,7 +2,7 @@ plugins {
id 'com.android.application'
id 'kotlin-android'
id 'kotlin-kapt'
id 'org.jlleitschuh.gradle.ktlint'
id 'org.jlleitschuh.gradle.ktlint' version '11.3.1'
id 'org.jetbrains.kotlin.android'
}
@ -78,8 +78,12 @@ project.tasks['preBuild'].dependsOn 'getGitVersion'
project.tasks['preBuild'].dependsOn 'linphoneSdkSource'
android {
compileOptions {
sourceCompatibility = 17
targetCompatibility = 17
}
compileSdkVersion 33
buildToolsVersion '33.0.0'
defaultConfig {
minSdkVersion 23
targetSdkVersion 33
@ -228,7 +232,7 @@ dependencies {
// https://github.com/Baseflow/PhotoView/blob/master/LICENSE Apache v2.0
implementation 'com.github.chrisbanes:PhotoView:2.3.0'
implementation platform('com.google.firebase:firebase-bom:31.2.3')
implementation platform('com.google.firebase:firebase-bom:31.5.0')
if (crashlyticsAvailable) {
debugImplementation 'com.google.firebase:firebase-crashlytics-ndk'
releaseWithCrashlyticsImplementation 'com.google.firebase:firebase-crashlytics-ndk'

View file

@ -22,3 +22,20 @@
-keep public class * extends androidx.fragment.app.Fragment { *; }
-dontwarn com.google.errorprone.annotations.Immutable
# To prevent following errors:
#ERROR: Missing classes detected while running R8. Please add the missing classes or apply additional keep rules that are generated in /builds/BC/public/linphone-android/app/build/outputs/mapping/release/missing_rules.txt.
#ERROR: R8: Missing class org.bouncycastle.jsse.BCSSLParameters (referenced from: void okhttp3.internal.platform.BouncyCastlePlatform.configureTlsExtensions(javax.net.ssl.SSLSocket, java.lang.String, java.util.List) and 1 other context)
#Missing class org.bouncycastle.jsse.BCSSLSocket (referenced from: void okhttp3.internal.platform.BouncyCastlePlatform.configureTlsExtensions(javax.net.ssl.SSLSocket, java.lang.String, java.util.List) and 5 other contexts)
#Missing class org.bouncycastle.jsse.provider.BouncyCastleJsseProvider (referenced from: void okhttp3.internal.platform.BouncyCastlePlatform.<init>())
#Missing class org.conscrypt.Conscrypt$Version (referenced from: boolean okhttp3.internal.platform.ConscryptPlatform$Companion.atLeastVersion(int, int, int))
#Missing class org.conscrypt.Conscrypt (referenced from: boolean okhttp3.internal.platform.ConscryptPlatform$Companion.atLeastVersion(int, int, int) and 4 other contexts)
#Missing class org.conscrypt.ConscryptHostnameVerifier (referenced from: okhttp3.internal.platform.ConscryptPlatform$DisabledHostnameVerifier)
#Missing class org.openjsse.javax.net.ssl.SSLParameters (referenced from: void okhttp3.internal.platform.OpenJSSEPlatform.configureTlsExtensions(javax.net.ssl.SSLSocket, java.lang.String, java.util.List))
#Missing class org.openjsse.javax.net.ssl.SSLSocket (referenced from: void okhttp3.internal.platform.OpenJSSEPlatform.configureTlsExtensions(javax.net.ssl.SSLSocket, java.lang.String, java.util.List) and 1 other context)
#Missing class org.openjsse.net.ssl.OpenJSSE (referenced from: void okhttp3.internal.platform.OpenJSSEPlatform.<init>())
#> Task :app:lintVitalAnalyzeRelease
#FAILURE: Build failed with an exception.
-dontwarn org.conscrypt.**
-dontwarn org.bouncycastle.**
-dontwarn org.openjsse.**

View file

@ -38,6 +38,7 @@ class LinphoneApplication : Application(), ImageLoaderFactory {
companion object {
@SuppressLint("StaticFieldLeak")
lateinit var corePreferences: CorePreferences
@SuppressLint("StaticFieldLeak")
lateinit var coreContext: CoreContext
@ -59,7 +60,10 @@ class LinphoneApplication : Application(), ImageLoaderFactory {
CoreContext.activateVFS()
}
val config = Factory.instance().createConfigWithFactory(corePreferences.configPath, corePreferences.factoryConfigPath)
val config = Factory.instance().createConfigWithFactory(
corePreferences.configPath,
corePreferences.factoryConfigPath
)
corePreferences.config = config
val appName = context.getString(R.string.app_name)
@ -83,8 +87,15 @@ class LinphoneApplication : Application(), ImageLoaderFactory {
return false
}
Log.i("[Application] Core context is being created ${if (pushReceived) "from push" else ""}")
coreContext = CoreContext(context, corePreferences.config, service, useAutoStartDescription)
Log.i(
"[Application] Core context is being created ${if (pushReceived) "from push" else ""}"
)
coreContext = CoreContext(
context,
corePreferences.config,
service,
useAutoStartDescription
)
coreContext.start()
return true
}

View file

@ -74,7 +74,9 @@ abstract class GenericFragment<T : ViewDataBinding> : Fragment() {
if (!navController.popBackStack()) {
Log.d("[Generic Fragment] ${getFragmentRealClassName()} couldn't pop")
if (!navController.navigateUp()) {
Log.d("[Generic Fragment] ${getFragmentRealClassName()} couldn't navigate up")
Log.d(
"[Generic Fragment] ${getFragmentRealClassName()} couldn't navigate up"
)
// Disable this callback & start a new back press event
isEnabled = false
goBack()
@ -98,7 +100,9 @@ abstract class GenericFragment<T : ViewDataBinding> : Fragment() {
}
sharedViewModel.isSlidingPaneSlideable.observe(viewLifecycleOwner) {
Log.d("[Generic Fragment] ${getFragmentRealClassName()} shared main VM sliding pane has changed")
Log.d(
"[Generic Fragment] ${getFragmentRealClassName()} shared main VM sliding pane has changed"
)
onBackPressedCallback.isEnabled = backPressedCallBackEnabled()
}
@ -151,7 +155,10 @@ abstract class GenericFragment<T : ViewDataBinding> : Fragment() {
onBackPressedCallback.isEnabled = false
}
requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner, onBackPressedCallback)
requireActivity().onBackPressedDispatcher.addCallback(
viewLifecycleOwner,
onBackPressedCallback
)
}
private fun backPressedCallBackEnabled(): Boolean {
@ -161,9 +168,13 @@ abstract class GenericFragment<T : ViewDataBinding> : Fragment() {
if (findNavController().graph.id == R.id.main_nav_graph_xml) return false
val isSlidingPaneFlat = sharedViewModel.isSlidingPaneSlideable.value == false
Log.d("[Generic Fragment] ${getFragmentRealClassName()} isSlidingPaneFlat ? $isSlidingPaneFlat")
Log.d(
"[Generic Fragment] ${getFragmentRealClassName()} isSlidingPaneFlat ? $isSlidingPaneFlat"
)
val isPreviousFragmentEmpty = findNavController().previousBackStackEntry?.destination?.id in emptyFragmentsIds
Log.d("[Generic Fragment] ${getFragmentRealClassName()} isPreviousFragmentEmpty ? $isPreviousFragmentEmpty")
Log.d(
"[Generic Fragment] ${getFragmentRealClassName()} isPreviousFragmentEmpty ? $isPreviousFragmentEmpty"
)
val popBackStack = isSlidingPaneFlat || !isPreviousFragmentEmpty
Log.d("[Generic Fragment] ${getFragmentRealClassName()} popBackStack ? $popBackStack")
return popBackStack

View file

@ -34,7 +34,9 @@ abstract class ProximitySensorActivity : GenericActivity() {
val powerManager = getSystemService(Context.POWER_SERVICE) as PowerManager
if (!powerManager.isWakeLockLevelSupported(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK)) {
Log.w("[Proximity Sensor Activity] PROXIMITY_SCREEN_OFF_WAKE_LOCK isn't supported on this device!")
Log.w(
"[Proximity Sensor Activity] PROXIMITY_SCREEN_OFF_WAKE_LOCK isn't supported on this device!"
)
}
proximityWakeLock = powerManager.newWakeLock(
@ -58,7 +60,9 @@ abstract class ProximitySensorActivity : GenericActivity() {
protected fun enableProximitySensor(enable: Boolean) {
if (enable) {
if (!proximitySensorEnabled) {
Log.i("[Proximity Sensor Activity] Enabling proximity sensor (turning screen OFF when wake lock is acquired)")
Log.i(
"[Proximity Sensor Activity] Enabling proximity sensor (turning screen OFF when wake lock is acquired)"
)
if (!proximityWakeLock.isHeld) {
Log.i("[Proximity Sensor Activity] Acquiring PROXIMITY_SCREEN_OFF_WAKE_LOCK")
proximityWakeLock.acquire()
@ -67,9 +71,13 @@ abstract class ProximitySensorActivity : GenericActivity() {
}
} else {
if (proximitySensorEnabled) {
Log.i("[Proximity Sensor Activity] Disabling proximity sensor (turning screen ON when wake lock is released)")
Log.i(
"[Proximity Sensor Activity] Disabling proximity sensor (turning screen ON when wake lock is released)"
)
if (proximityWakeLock.isHeld) {
Log.i("[Proximity Sensor Activity] Asking to release PROXIMITY_SCREEN_OFF_WAKE_LOCK next time sensor detects no proximity")
Log.i(
"[Proximity Sensor Activity] Asking to release PROXIMITY_SCREEN_OFF_WAKE_LOCK next time sensor detects no proximity"
)
proximityWakeLock.release(RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY)
}
proximitySensorEnabled = false

View file

@ -41,7 +41,11 @@ class CountryPickerAdapter : BaseAdapter(), Filterable {
}
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val view: View = convertView ?: LayoutInflater.from(parent.context).inflate(R.layout.assistant_country_picker_cell, parent, false)
val view: View = convertView ?: LayoutInflater.from(parent.context).inflate(
R.layout.assistant_country_picker_cell,
parent,
false
)
val dialPlan: DialPlan = countries[position]
val name = view.findViewById<TextView>(R.id.country_name)

View file

@ -58,7 +58,10 @@ abstract class AbstractPhoneFragment<T : ViewDataBinding> : GenericFragment<T>()
if (!resources.getBoolean(R.bool.isTablet)) {
if (!PermissionHelper.get().hasReadPhoneStateOrPhoneNumbersPermission()) {
Log.i("[Assistant] Asking for READ_PHONE_STATE/READ_PHONE_NUMBERS permission")
Compatibility.requestReadPhoneStateOrNumbersPermission(this, READ_PHONE_STATE_PERMISSION_REQUEST_CODE)
Compatibility.requestReadPhoneStateOrNumbersPermission(
this,
READ_PHONE_STATE_PERMISSION_REQUEST_CODE
)
} else {
updateFromDeviceInfo()
}

View file

@ -57,7 +57,10 @@ class EchoCancellerCalibrationFragment : GenericFragment<AssistantEchoCancellerC
if (!PermissionHelper.required(requireContext()).hasRecordAudioPermission()) {
Log.i("[Echo Canceller Calibration] Asking for RECORD_AUDIO permission")
requestPermissions(arrayOf(android.Manifest.permission.RECORD_AUDIO), RECORD_AUDIO_PERMISSION_REQUEST_CODE)
requestPermissions(
arrayOf(android.Manifest.permission.RECORD_AUDIO),
RECORD_AUDIO_PERMISSION_REQUEST_CODE
)
} else {
viewModel.startEchoCancellerCalibration()
}

View file

@ -46,7 +46,10 @@ class EmailAccountCreationFragment : GenericFragment<AssistantEmailAccountCreati
ViewModelProvider(this)[SharedAssistantViewModel::class.java]
}
viewModel = ViewModelProvider(this, EmailAccountCreationViewModelFactory(sharedAssistantViewModel.getAccountCreator()))[EmailAccountCreationViewModel::class.java]
viewModel = ViewModelProvider(
this,
EmailAccountCreationViewModelFactory(sharedAssistantViewModel.getAccountCreator())
)[EmailAccountCreationViewModel::class.java]
binding.viewModel = viewModel
viewModel.goToEmailValidationEvent.observe(

View file

@ -46,7 +46,10 @@ class EmailAccountValidationFragment : GenericFragment<AssistantEmailAccountVali
ViewModelProvider(this)[SharedAssistantViewModel::class.java]
}
viewModel = ViewModelProvider(this, EmailAccountValidationViewModelFactory(sharedAssistantViewModel.getAccountCreator()))[EmailAccountValidationViewModel::class.java]
viewModel = ViewModelProvider(
this,
EmailAccountValidationViewModelFactory(sharedAssistantViewModel.getAccountCreator())
)[EmailAccountValidationViewModel::class.java]
binding.viewModel = viewModel
viewModel.leaveAssistantEvent.observe(

View file

@ -51,7 +51,10 @@ class GenericAccountLoginFragment : GenericFragment<AssistantGenericAccountLogin
ViewModelProvider(this)[SharedAssistantViewModel::class.java]
}
viewModel = ViewModelProvider(this, GenericLoginViewModelFactory(sharedAssistantViewModel.getAccountCreator(true)))[GenericLoginViewModel::class.java]
viewModel = ViewModelProvider(
this,
GenericLoginViewModelFactory(sharedAssistantViewModel.getAccountCreator(true))
)[GenericLoginViewModel::class.java]
binding.viewModel = viewModel
viewModel.leaveAssistantEvent.observe(

View file

@ -52,7 +52,10 @@ class PhoneAccountValidationFragment : GenericFragment<AssistantPhoneAccountVali
ViewModelProvider(this)[SharedAssistantViewModel::class.java]
}
viewModel = ViewModelProvider(this, PhoneAccountValidationViewModelFactory(sharedAssistantViewModel.getAccountCreator()))[PhoneAccountValidationViewModel::class.java]
viewModel = ViewModelProvider(
this,
PhoneAccountValidationViewModelFactory(sharedAssistantViewModel.getAccountCreator())
)[PhoneAccountValidationViewModel::class.java]
binding.viewModel = viewModel
viewModel.phoneNumber.value = arguments?.getString("PhoneNumber")
@ -106,7 +109,9 @@ class PhoneAccountValidationFragment : GenericFragment<AssistantPhoneAccountVali
if (data != null && data.itemCount > 0) {
val clip = data.getItemAt(0).text.toString()
if (clip.length == 4) {
Log.i("[Assistant] [Phone Account Validation] Found 4 digits as primary clip in clipboard, using it and clear it")
Log.i(
"[Assistant] [Phone Account Validation] Found 4 digits as primary clip in clipboard, using it and clear it"
)
viewModel.code.value = clip
clipboard.clearPrimaryClip()
}

View file

@ -67,7 +67,10 @@ class QrCodeFragment : GenericFragment<AssistantQrCodeFragmentBinding>() {
if (!PermissionHelper.required(requireContext()).hasCameraPermission()) {
Log.i("[QR Code] Asking for CAMERA permission")
requestPermissions(arrayOf(android.Manifest.permission.CAMERA), CAMERA_PERMISSION_REQUEST_CODE)
requestPermissions(
arrayOf(android.Manifest.permission.CAMERA),
CAMERA_PERMISSION_REQUEST_CODE
)
}
}

View file

@ -107,7 +107,12 @@ class WelcomeFragment : GenericFragment<AssistantWelcomeFragmentBinding>() {
}
}
}
spannable.setSpan(clickableSpan, termsMatcher.start(0), termsMatcher.end(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
spannable.setSpan(
clickableSpan,
termsMatcher.start(0),
termsMatcher.end(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
)
}
val policyMatcher = Pattern.compile(privacy).matcher(label)
@ -125,7 +130,12 @@ class WelcomeFragment : GenericFragment<AssistantWelcomeFragmentBinding>() {
}
}
}
spannable.setSpan(clickableSpan, policyMatcher.start(0), policyMatcher.end(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
spannable.setSpan(
clickableSpan,
policyMatcher.start(0),
policyMatcher.end(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
)
}
binding.termsAndPrivacy.text = spannable

View file

@ -159,7 +159,9 @@ class AccountLoginViewModel(accountCreator: AccountCreator) : AbstractPhoneViewM
if (loginWithUsernamePassword.value == true) {
val result = accountCreator.setUsername(username.value)
if (result != AccountCreator.UsernameStatus.Ok) {
Log.e("[Assistant] [Account Login] Error [${result.name}] setting the username: ${username.value}")
Log.e(
"[Assistant] [Account Login] Error [${result.name}] setting the username: ${username.value}"
)
usernameError.value = result.name
return
}
@ -180,9 +182,13 @@ class AccountLoginViewModel(accountCreator: AccountCreator) : AbstractPhoneViewM
onErrorEvent.value = Event("Error: Failed to create account object")
}
} else {
val result = AccountCreator.PhoneNumberStatus.fromInt(accountCreator.setPhoneNumber(phoneNumber.value, prefix.value))
val result = AccountCreator.PhoneNumberStatus.fromInt(
accountCreator.setPhoneNumber(phoneNumber.value, prefix.value)
)
if (result != AccountCreator.PhoneNumberStatus.Ok) {
Log.e("[Assistant] [Account Login] Error [$result] setting the phone number: ${phoneNumber.value} with prefix: ${prefix.value}")
Log.e(
"[Assistant] [Account Login] Error [$result] setting the phone number: ${phoneNumber.value} with prefix: ${prefix.value}"
)
phoneNumberError.value = result.name
return
}
@ -190,7 +196,9 @@ class AccountLoginViewModel(accountCreator: AccountCreator) : AbstractPhoneViewM
val result2 = accountCreator.setUsername(accountCreator.phoneNumber)
if (result2 != AccountCreator.UsernameStatus.Ok) {
Log.e("[Assistant] [Account Login] Error [${result2.name}] setting the username: ${accountCreator.phoneNumber}")
Log.e(
"[Assistant] [Account Login] Error [${result2.name}] setting the username: ${accountCreator.phoneNumber}"
)
usernameError.value = result2.name
return
}
@ -229,7 +237,9 @@ class AccountLoginViewModel(accountCreator: AccountCreator) : AbstractPhoneViewM
if (proxyConfig.dialPrefix.isNullOrEmpty()) {
val dialPlan = PhoneNumberUtils.getDialPlanForCurrentCountry(coreContext.context)
if (dialPlan != null) {
Log.i("[Assistant] [Account Login] Found dial plan country ${dialPlan.country} with international prefix ${dialPlan.countryCallingCode}")
Log.i(
"[Assistant] [Account Login] Found dial plan country ${dialPlan.country} with international prefix ${dialPlan.countryCallingCode}"
)
proxyConfig.edit()
proxyConfig.dialPrefix = dialPlan.countryCallingCode
proxyConfig.done()

View file

@ -74,7 +74,9 @@ class EmailAccountCreationViewModel(val accountCreator: AccountCreator) : ViewMo
when (status) {
AccountCreator.Status.AccountExist, AccountCreator.Status.AccountExistWithAlias -> {
waitForServerAnswer.value = false
usernameError.value = AppUtils.getString(R.string.assistant_error_username_already_exists)
usernameError.value = AppUtils.getString(
R.string.assistant_error_username_already_exists
)
}
AccountCreator.Status.AccountNotExist -> {
val createAccountStatus = creator.createAccount()

View file

@ -109,9 +109,13 @@ class EmailAccountValidationViewModel(val accountCreator: AccountCreator) : View
proxyConfig.isPushNotificationAllowed = true
if (proxyConfig.dialPrefix.isNullOrEmpty()) {
val dialPlan = PhoneNumberUtils.getDialPlanForCurrentCountry(LinphoneApplication.coreContext.context)
val dialPlan = PhoneNumberUtils.getDialPlanForCurrentCountry(
LinphoneApplication.coreContext.context
)
if (dialPlan != null) {
Log.i("[Assistant] [Account Validation] Found dial plan country ${dialPlan.country} with international prefix ${dialPlan.countryCallingCode}")
Log.i(
"[Assistant] [Account Validation] Found dial plan country ${dialPlan.country} with international prefix ${dialPlan.countryCallingCode}"
)
proxyConfig.edit()
proxyConfig.dialPrefix = dialPlan.countryCallingCode
proxyConfig.done()

View file

@ -41,7 +41,9 @@ class PhoneAccountCreationViewModelFactory(private val accountCreator: AccountCr
}
}
class PhoneAccountCreationViewModel(accountCreator: AccountCreator) : AbstractPhoneViewModel(accountCreator) {
class PhoneAccountCreationViewModel(accountCreator: AccountCreator) : AbstractPhoneViewModel(
accountCreator
) {
val username = MutableLiveData<String>()
val useUsername = MutableLiveData<Boolean>()
val usernameError = MutableLiveData<String>()
@ -71,9 +73,13 @@ class PhoneAccountCreationViewModel(accountCreator: AccountCreator) : AbstractPh
AccountCreator.Status.AccountExist, AccountCreator.Status.AccountExistWithAlias -> {
waitForServerAnswer.value = false
if (useUsername.value == true) {
usernameError.value = AppUtils.getString(R.string.assistant_error_username_already_exists)
usernameError.value = AppUtils.getString(
R.string.assistant_error_username_already_exists
)
} else {
phoneNumberError.value = AppUtils.getString(R.string.assistant_error_phone_number_already_exists)
phoneNumberError.value = AppUtils.getString(
R.string.assistant_error_phone_number_already_exists
)
}
}
AccountCreator.Status.AccountNotExist -> {
@ -103,7 +109,9 @@ class PhoneAccountCreationViewModel(accountCreator: AccountCreator) : AbstractPh
goToSmsValidationEvent.value = Event(true)
}
AccountCreator.Status.AccountExistWithAlias -> {
phoneNumberError.value = AppUtils.getString(R.string.assistant_error_phone_number_already_exists)
phoneNumberError.value = AppUtils.getString(
R.string.assistant_error_phone_number_already_exists
)
}
else -> {
onErrorEvent.value = Event("Error: ${status.name}")
@ -161,7 +169,11 @@ class PhoneAccountCreationViewModel(accountCreator: AccountCreator) : AbstractPh
}
private fun isCreateButtonEnabled(): Boolean {
val usernameRegexp = corePreferences.config.getString("assistant", "username_regex", "^[a-z0-9+_.\\-]*\$")
val usernameRegexp = corePreferences.config.getString(
"assistant",
"username_regex",
"^[a-z0-9+_.\\-]*\$"
)
return isPhoneNumberOk() && usernameRegexp != null &&
(
useUsername.value == false ||

View file

@ -35,7 +35,9 @@ class PhoneAccountLinkingViewModelFactory(private val accountCreator: AccountCre
}
}
class PhoneAccountLinkingViewModel(accountCreator: AccountCreator) : AbstractPhoneViewModel(accountCreator) {
class PhoneAccountLinkingViewModel(accountCreator: AccountCreator) : AbstractPhoneViewModel(
accountCreator
) {
val username = MutableLiveData<String>()
val allowSkip = MutableLiveData<Boolean>()

View file

@ -125,7 +125,9 @@ class PhoneAccountValidationViewModel(val accountCreator: AccountCreator) : View
fun finish() {
accountCreator.activationCode = code.value.orEmpty()
Log.i("[Assistant] [Phone Account Validation] Phone number is ${accountCreator.phoneNumber} and activation code is ${accountCreator.activationCode}")
Log.i(
"[Assistant] [Phone Account Validation] Phone number is ${accountCreator.phoneNumber} and activation code is ${accountCreator.activationCode}"
)
waitForServerAnswer.value = true
val status = when {
@ -145,7 +147,9 @@ class PhoneAccountValidationViewModel(val accountCreator: AccountCreator) : View
val proxyConfig: ProxyConfig? = accountCreator.createProxyConfig()
if (proxyConfig == null) {
Log.e("[Assistant] [Phone Account Validation] Account creator couldn't create proxy config")
Log.e(
"[Assistant] [Phone Account Validation] Account creator couldn't create proxy config"
)
return false
}

View file

@ -77,11 +77,15 @@ class ChatBubbleActivity : GenericActivity() {
var chatRoom: ChatRoom? = null
if (localSipUri != null && remoteSipUri != null) {
Log.i("[Chat Bubble] Found local [$localSipUri] & remote [$remoteSipUri] addresses in arguments")
Log.i(
"[Chat Bubble] Found local [$localSipUri] & remote [$remoteSipUri] addresses in arguments"
)
val localAddress = Factory.instance().createAddress(localSipUri)
val remoteSipAddress = Factory.instance().createAddress(remoteSipUri)
chatRoom = coreContext.core.searchChatRoom(
null, localAddress, remoteSipAddress,
null,
localAddress,
remoteSipAddress,
arrayOfNulls(
0
)
@ -155,7 +159,10 @@ class ChatBubbleActivity : GenericActivity() {
binding.setOpenAppClickListener {
coreContext.notificationsManager.currentlyDisplayedChatRoomAddress = null
coreContext.notificationsManager.changeDismissNotificationUponReadForChatRoom(viewModel.chatRoom, false)
coreContext.notificationsManager.changeDismissNotificationUponReadForChatRoom(
viewModel.chatRoom,
false
)
val intent = Intent(this, MainActivity::class.java)
intent.putExtra("RemoteSipUri", remoteSipUri)
@ -181,7 +188,10 @@ class ChatBubbleActivity : GenericActivity() {
viewModel.chatRoom.addListener(listener)
// Workaround for the removed notification when a chat room is marked as read
coreContext.notificationsManager.changeDismissNotificationUponReadForChatRoom(viewModel.chatRoom, true)
coreContext.notificationsManager.changeDismissNotificationUponReadForChatRoom(
viewModel.chatRoom,
true
)
viewModel.chatRoom.markAsRead()
val peerAddress = viewModel.chatRoom.peerAddress.asStringUriOnly()
@ -199,7 +209,10 @@ class ChatBubbleActivity : GenericActivity() {
viewModel.chatRoom.removeListener(listener)
coreContext.notificationsManager.currentlyDisplayedChatRoomAddress = null
coreContext.notificationsManager.changeDismissNotificationUponReadForChatRoom(viewModel.chatRoom, false)
coreContext.notificationsManager.changeDismissNotificationUponReadForChatRoom(
viewModel.chatRoom,
false
)
super.onPause()
}

View file

@ -205,7 +205,9 @@ class MainActivity : GenericActivity(), SnackBarActivity, NavController.OnDestin
binding.rootCoordinatorLayout.addKeyboardInsetListener { keyboardVisible ->
val portraitOrientation = resources.configuration.orientation != Configuration.ORIENTATION_LANDSCAPE
Log.i("[Main Activity] Keyboard is ${if (keyboardVisible) "visible" else "invisible"}, orientation is ${if (portraitOrientation) "portrait" else "landscape"}")
Log.i(
"[Main Activity] Keyboard is ${if (keyboardVisible) "visible" else "invisible"}, orientation is ${if (portraitOrientation) "portrait" else "landscape"}"
)
shouldTabsBeVisibleDueToOrientationAndKeyboard = !portraitOrientation || !keyboardVisible
updateTabsFragmentVisibility()
}
@ -255,7 +257,9 @@ class MainActivity : GenericActivity(), SnackBarActivity, NavController.OnDestin
}
private fun handleIntentParams(intent: Intent) {
Log.i("[Main Activity] Handling intent with action [${intent.action}], type [${intent.type}] and data [${intent.data}]")
Log.i(
"[Main Activity] Handling intent with action [${intent.action}], type [${intent.type}] and data [${intent.data}]"
)
when (intent.action) {
Intent.ACTION_MAIN -> handleMainIntent(intent)
@ -291,10 +295,14 @@ class MainActivity : GenericActivity(), SnackBarActivity, NavController.OnDestin
if (stringUri.startsWith("linphone-config:")) {
val remoteConfigUri = stringUri.substring("linphone-config:".length)
if (corePreferences.autoRemoteProvisioningOnConfigUriHandler) {
Log.w("[Main Activity] Remote provisioning URL set to [$remoteConfigUri], restarting Core now")
Log.w(
"[Main Activity] Remote provisioning URL set to [$remoteConfigUri], restarting Core now"
)
applyRemoteProvisioning(remoteConfigUri)
} else {
Log.i("[Main Activity] Remote provisioning URL found [$remoteConfigUri], asking for user validation")
Log.i(
"[Main Activity] Remote provisioning URL found [$remoteConfigUri], asking for user validation"
)
showAcceptRemoteConfigurationDialog(remoteConfigUri)
}
} else {
@ -339,7 +347,9 @@ class MainActivity : GenericActivity(), SnackBarActivity, NavController.OnDestin
if (intent.hasExtra("RemoteSipUri") && intent.hasExtra("LocalSipUri")) {
val peerAddress = intent.getStringExtra("RemoteSipUri")
val localAddress = intent.getStringExtra("LocalSipUri")
Log.i("[Main Activity] Found chat room intent extra: local SIP URI=[$localAddress], peer SIP URI=[$peerAddress]")
Log.i(
"[Main Activity] Found chat room intent extra: local SIP URI=[$localAddress], peer SIP URI=[$peerAddress]"
)
navigateToChatRoom(localAddress, peerAddress)
} else {
Log.i("[Main Activity] Found chat intent extra, go to chat rooms list")
@ -362,9 +372,16 @@ class MainActivity : GenericActivity(), SnackBarActivity, NavController.OnDestin
val core = coreContext.core
val call = core.currentCall ?: core.calls.firstOrNull()
if (call != null) {
Log.i("[Main Activity] Launcher clicked while there is at least one active call, go to CallActivity")
val callIntent = Intent(this, org.linphone.activities.voip.CallActivity::class.java)
callIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_REORDER_TO_FRONT)
Log.i(
"[Main Activity] Launcher clicked while there is at least one active call, go to CallActivity"
)
val callIntent = Intent(
this,
org.linphone.activities.voip.CallActivity::class.java
)
callIntent.addFlags(
Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_REORDER_TO_FRONT
)
startActivity(callIntent)
}
}
@ -391,7 +408,10 @@ class MainActivity : GenericActivity(), SnackBarActivity, NavController.OnDestin
}
}
val address = coreContext.core.interpretUrl(addressToCall, LinphoneUtils.applyInternationalPrefix())
val address = coreContext.core.interpretUrl(
addressToCall,
LinphoneUtils.applyInternationalPrefix()
)
if (address != null) {
addressToCall = address.asStringUriOnly()
}
@ -490,8 +510,13 @@ class MainActivity : GenericActivity(), SnackBarActivity, NavController.OnDestin
val localAddress =
coreContext.core.defaultAccount?.params?.identityAddress?.asStringUriOnly()
val peerAddress = coreContext.core.interpretUrl(addressToIM, LinphoneUtils.applyInternationalPrefix())?.asStringUriOnly()
Log.i("[Main Activity] Navigating to chat room with local [$localAddress] and peer [$peerAddress] addresses")
val peerAddress = coreContext.core.interpretUrl(
addressToIM,
LinphoneUtils.applyInternationalPrefix()
)?.asStringUriOnly()
Log.i(
"[Main Activity] Navigating to chat room with local [$localAddress] and peer [$peerAddress] addresses"
)
navigateToChatRoom(localAddress, peerAddress)
} else {
val shortcutId = intent.getStringExtra("android.intent.extra.shortcut.ID") // Intent.EXTRA_SHORTCUT_ID
@ -510,10 +535,14 @@ class MainActivity : GenericActivity(), SnackBarActivity, NavController.OnDestin
if (split.size == 2) {
val localAddress = split[0]
val peerAddress = split[1]
Log.i("[Main Activity] Navigating to chat room with local [$localAddress] and peer [$peerAddress] addresses, computed from shortcut/locus id")
Log.i(
"[Main Activity] Navigating to chat room with local [$localAddress] and peer [$peerAddress] addresses, computed from shortcut/locus id"
)
navigateToChatRoom(localAddress, peerAddress)
} else {
Log.e("[Main Activity] Failed to parse shortcut/locus id: $id, going to chat rooms list")
Log.e(
"[Main Activity] Failed to parse shortcut/locus id: $id, going to chat rooms list"
)
navigateToChatRooms()
}
}
@ -562,7 +591,10 @@ class MainActivity : GenericActivity(), SnackBarActivity, NavController.OnDestin
}
private fun showAcceptRemoteConfigurationDialog(remoteConfigUri: String) {
val dialogViewModel = DialogViewModel(remoteConfigUri, getString(R.string.dialog_apply_remote_provisioning_title))
val dialogViewModel = DialogViewModel(
remoteConfigUri,
getString(R.string.dialog_apply_remote_provisioning_title)
)
val dialog = DialogUtils.getDialog(this, dialogViewModel)
dialogViewModel.showCancelButton {
@ -575,7 +607,9 @@ class MainActivity : GenericActivity(), SnackBarActivity, NavController.OnDestin
)
dialogViewModel.showOkButton(
{
Log.w("[Main Activity] Remote provisioning URL set to [$remoteConfigUri], restarting Core now")
Log.w(
"[Main Activity] Remote provisioning URL set to [$remoteConfigUri], restarting Core now"
)
applyRemoteProvisioning(remoteConfigUri)
dialog.dismiss()
},

View file

@ -26,6 +26,7 @@ internal abstract class ChatScrollListener(private val mLayoutManager: LinearLay
RecyclerView.OnScrollListener() {
// The total number of items in the data set after the last load
private var previousTotalItemCount = 0
// True if we are still waiting for the last set of data to load.
private var loading = true

View file

@ -55,7 +55,10 @@ import org.linphone.utils.HeaderAdapter
class ChatMessagesListAdapter(
selectionVM: ListTopBarViewModel,
private val viewLifecycleOwner: LifecycleOwner
) : SelectionListAdapter<EventLogData, RecyclerView.ViewHolder>(selectionVM, ChatMessageDiffCallback()),
) : SelectionListAdapter<EventLogData, RecyclerView.ViewHolder>(
selectionVM,
ChatMessageDiffCallback()
),
HeaderAdapter {
companion object {
const val MAX_TIME_TO_GROUP_MESSAGES = 60 // 1 minute
@ -116,7 +119,9 @@ class ChatMessagesListAdapter(
override fun onWebUrlClicked(url: String) {
if (popup?.isShowing == true) {
Log.w("[Chat Message Data] Long press that displayed context menu detected, aborting click on URL [$url]")
Log.w(
"[Chat Message Data] Long press that displayed context menu detected, aborting click on URL [$url]"
)
return
}
urlClickEvent.value = Event(url)
@ -124,7 +129,9 @@ class ChatMessagesListAdapter(
override fun onSipAddressClicked(sipUri: String) {
if (popup?.isShowing == true) {
Log.w("[Chat Message Data] Long press that displayed context menu detected, aborting click on SIP URI [$sipUri]")
Log.w(
"[Chat Message Data] Long press that displayed context menu detected, aborting click on SIP URI [$sipUri]"
)
return
}
sipUriClickedEvent.value = Event(sipUri)
@ -155,7 +162,9 @@ class ChatMessagesListAdapter(
private fun createChatMessageViewHolder(parent: ViewGroup): ChatMessageViewHolder {
val binding: ChatMessageListCellBinding = DataBindingUtil.inflate(
LayoutInflater.from(parent.context),
R.layout.chat_message_list_cell, parent, false
R.layout.chat_message_list_cell,
parent,
false
)
return ChatMessageViewHolder(binding)
}
@ -163,7 +172,9 @@ class ChatMessagesListAdapter(
private fun createEventViewHolder(parent: ViewGroup): EventViewHolder {
val binding: ChatEventListCellBinding = DataBindingUtil.inflate(
LayoutInflater.from(parent.context),
R.layout.chat_event_list_cell, parent, false
R.layout.chat_event_list_cell,
parent,
false
)
return EventViewHolder(binding)
}
@ -199,9 +210,14 @@ class ChatMessagesListAdapter(
override fun getHeaderViewForPosition(context: Context, position: Int): View {
val binding: ChatUnreadMessagesListHeaderBinding = DataBindingUtil.inflate(
LayoutInflater.from(context),
R.layout.chat_unread_messages_list_header, null, false
R.layout.chat_unread_messages_list_header,
null,
false
)
binding.title = AppUtils.getStringWithPlural(
R.plurals.chat_room_unread_messages_event,
unreadMessagesCount
)
binding.title = AppUtils.getStringWithPlural(R.plurals.chat_room_unread_messages_event, unreadMessagesCount)
binding.executePendingBindings()
return binding.root
}
@ -281,7 +297,10 @@ class ChatMessagesListAdapter(
val previousItem = getItem(bindingAdapterPosition - 1)
if (previousItem.eventLog.type == EventLog.Type.ConferenceChatMessage) {
val previousMessage = previousItem.eventLog.chatMessage
if (previousMessage != null && previousMessage.fromAddress.weakEqual(chatMessage.fromAddress)) {
if (previousMessage != null && previousMessage.fromAddress.weakEqual(
chatMessage.fromAddress
)
) {
if (abs(chatMessage.time - previousMessage.time) < MAX_TIME_TO_GROUP_MESSAGES) {
hasPrevious = true
}
@ -293,7 +312,10 @@ class ChatMessagesListAdapter(
val nextItem = getItem(bindingAdapterPosition + 1)
if (nextItem.eventLog.type == EventLog.Type.ConferenceChatMessage) {
val nextMessage = nextItem.eventLog.chatMessage
if (nextMessage != null && nextMessage.fromAddress.weakEqual(chatMessage.fromAddress)) {
if (nextMessage != null && nextMessage.fromAddress.weakEqual(
chatMessage.fromAddress
)
) {
if (abs(nextMessage.time - chatMessage.time) < MAX_TIME_TO_GROUP_MESSAGES) {
hasNext = true
}
@ -308,12 +330,17 @@ class ChatMessagesListAdapter(
setContextMenuClickListener {
val popupView: ChatMessageLongPressMenuBindingImpl = DataBindingUtil.inflate(
LayoutInflater.from(root.context),
R.layout.chat_message_long_press_menu, null, false
R.layout.chat_message_long_press_menu,
null,
false
)
val itemSize = AppUtils.getDimension(R.dimen.chat_message_popup_item_height).toInt()
var totalSize = itemSize * 7
if (chatMessage.chatRoom.hasCapability(ChatRoomCapabilities.OneToOne.toInt())) {
if (chatMessage.chatRoom.hasCapability(
ChatRoomCapabilities.OneToOne.toInt()
)
) {
// No message id
popupView.imdnHidden = true
totalSize -= itemSize
@ -497,7 +524,9 @@ private class ChatMessageDiffCallback : DiffUtil.ItemCallback<EventLogData>() {
oldData.time.value == newData.time.value &&
oldData.isOutgoing == newData.isOutgoing
} else oldItem.notifyId == newItem.notifyId
} else {
oldItem.notifyId == newItem.notifyId
}
}
override fun areContentsTheSame(

View file

@ -48,7 +48,9 @@ class ChatRoomsListAdapter(
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val binding: ChatRoomListCellBinding = DataBindingUtil.inflate(
LayoutInflater.from(parent.context),
R.layout.chat_room_list_cell, parent, false
R.layout.chat_room_list_cell,
parent,
false
)
return ViewHolder(binding)
}
@ -77,7 +79,9 @@ class ChatRoomsListAdapter(
try {
notifyItemChanged(bindingAdapterPosition)
} catch (e: Exception) {
Log.e("[Chat Rooms Adapter] Can't notify item [$bindingAdapterPosition] has changed: $e")
Log.e(
"[Chat Rooms Adapter] Can't notify item [$bindingAdapterPosition] has changed: $e"
)
}
}
}

View file

@ -46,7 +46,9 @@ class GroupInfoParticipantsAdapter(
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val binding: ChatRoomGroupInfoParticipantCellBinding = DataBindingUtil.inflate(
LayoutInflater.from(parent.context),
R.layout.chat_room_group_info_participant_cell, parent, false
R.layout.chat_room_group_info_participant_cell,
parent,
false
)
return ViewHolder(binding)
}

View file

@ -41,7 +41,9 @@ class ImdnAdapter(
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val binding: ChatRoomImdnParticipantCellBinding = DataBindingUtil.inflate(
LayoutInflater.from(parent.context),
R.layout.chat_room_imdn_participant_cell, parent, false
R.layout.chat_room_imdn_participant_cell,
parent,
false
)
return ViewHolder(binding)
}
@ -70,14 +72,18 @@ class ImdnAdapter(
val previousPosition = position - 1
return if (previousPosition >= 0) {
getItem(previousPosition).imdnState.state != participantImdnState.imdnState.state
} else true
} else {
true
}
}
override fun getHeaderViewForPosition(context: Context, position: Int): View {
val participantImdnState = getItem(position).imdnState
val binding: ImdnListHeaderBinding = DataBindingUtil.inflate(
LayoutInflater.from(context),
R.layout.imdn_list_header, null, false
R.layout.imdn_list_header,
null,
false
)
when (participantImdnState.state) {
ChatMessage.State.Displayed -> {

View file

@ -48,7 +48,7 @@ import org.linphone.utils.TimestampUtils
class ChatMessageContentData(
private val chatMessage: ChatMessage,
private val contentIndex: Int,
private val contentIndex: Int
) {
var listener: OnContentClickedListener? = null
@ -172,7 +172,9 @@ class ChatMessageContentData(
fun download() {
if (chatMessage.isFileTransferInProgress) {
Log.w("[Content] Another FileTransfer content for this message is currently being downloaded, can't start another one for now")
Log.w(
"[Content] Another FileTransfer content for this message is currently being downloaded, can't start another one for now"
)
listener?.onError(R.string.chat_message_download_already_in_progress)
return
}
@ -191,7 +193,9 @@ class ChatMessageContentData(
return
}
} else {
Log.w("[Content] File path already set [$filePath] using it (auto download that failed probably)")
Log.w(
"[Content] File path already set [$filePath] using it (auto download that failed probably)"
)
}
if (!chatMessage.downloadContent(content)) {
@ -221,7 +225,9 @@ class ChatMessageContentData(
val content = getContent()
isFileEncrypted = content.isFileEncrypted
Log.i("[Content] Is ${if (content.isFile) "file" else "file transfer"} content encrypted ? $isFileEncrypted")
Log.i(
"[Content] Is ${if (content.isFile) "file" else "file transfer"} content encrypted ? $isFileEncrypted"
)
filePath.value = ""
fileName.value = if (content.name.isNullOrEmpty() && !content.filePath.isNullOrEmpty()) {
@ -232,7 +238,9 @@ class ChatMessageContentData(
// Display download size and underline text
val fileSize = AppUtils.bytesToDisplayableSize(content.fileSize.toLong())
val spannable = SpannableString("${AppUtils.getString(R.string.chat_message_download_file)} ($fileSize)")
val spannable = SpannableString(
"${AppUtils.getString(R.string.chat_message_download_file)} ($fileSize)"
)
spannable.setSpan(UnderlineSpan(), 0, spannable.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
downloadLabel.value = spannable
@ -247,7 +255,9 @@ class ChatMessageContentData(
if (content.isFile || (content.isFileTransfer && chatMessage.isOutgoing)) {
val path = if (isFileEncrypted) {
Log.i("[Content] [VFS] Content is encrypted, requesting plain file path for file [${content.filePath}]")
Log.i(
"[Content] [VFS] Content is encrypted, requesting plain file path for file [${content.filePath}]"
)
content.exportPlainFile()
} else {
content.filePath ?: ""
@ -277,13 +287,19 @@ class ChatMessageContentData(
isConferenceIcs -> "conference invitation"
else -> "unknown"
}
Log.i("[Content] Extension for file [$path] is [$extension], deduced type from MIME is [$type]")
Log.i(
"[Content] Extension for file [$path] is [$extension], deduced type from MIME is [$type]"
)
if (isVoiceRecord) {
val duration = content.fileDuration // duration is in ms
voiceRecordDuration.value = duration
formattedDuration.value = SimpleDateFormat("mm:ss", Locale.getDefault()).format(duration)
Log.i("[Content] Voice recording duration is ${voiceRecordDuration.value} ($duration)")
formattedDuration.value = SimpleDateFormat("mm:ss", Locale.getDefault()).format(
duration
)
Log.i(
"[Content] Voice recording duration is ${voiceRecordDuration.value} ($duration)"
)
} else if (isConferenceIcs) {
parseConferenceInvite(content)
}
@ -291,7 +307,9 @@ class ChatMessageContentData(
Log.i("[Content] Found content with icalendar file")
parseConferenceInvite(content)
} else {
Log.w("[Content] Found ${if (content.isFile) "file" else "file transfer"} content with empty path...")
Log.w(
"[Content] Found ${if (content.isFile) "file" else "file transfer"} content with empty path..."
)
isImage.value = false
isVideo.value = false
isAudio.value = false
@ -325,7 +343,9 @@ class ChatMessageContentData(
val conferenceUri = conferenceInfo?.uri?.asStringUriOnly()
if (conferenceInfo != null && conferenceUri != null) {
conferenceAddress.value = conferenceUri!!
Log.i("[Content] Created conference info from ICS with address ${conferenceAddress.value}")
Log.i(
"[Content] Created conference info from ICS with address ${conferenceAddress.value}"
)
conferenceSubject.value = conferenceInfo.subject
conferenceDescription.value = conferenceInfo.description
@ -355,7 +375,10 @@ class ChatMessageContentData(
}
}
if (!organizerFound) participantsCount += 1 // +1 for organizer
conferenceParticipantCount.value = String.format(AppUtils.getString(R.string.conference_invite_participants_count), participantsCount)
conferenceParticipantCount.value = String.format(
AppUtils.getString(R.string.conference_invite_participants_count),
participantsCount
)
} else if (conferenceInfo == null) {
if (content.filePath != null) {
try {
@ -367,7 +390,9 @@ class ChatMessageContentData(
textBuilder.append('\n')
}
br.close()
Log.e("[Content] Failed to create conference info from ICS file [${content.filePath}]: $textBuilder")
Log.e(
"[Content] Failed to create conference info from ICS file [${content.filePath}]: $textBuilder"
)
} catch (e: Exception) {
Log.e("[Content] Failed to read content of ICS file [${content.filePath}]: $e")
}
@ -375,7 +400,9 @@ class ChatMessageContentData(
Log.e("[Content] Failed to create conference info from ICS: ${content.utf8Text}")
}
} else if (conferenceInfo.uri == null) {
Log.e("[Content] Failed to find the conference URI in conference info [$conferenceInfo]")
Log.e(
"[Content] Failed to find the conference URI in conference info [$conferenceInfo]"
)
}
}
@ -398,7 +425,11 @@ class ChatMessageContentData(
}
if (AppUtils.isMediaVolumeLow(coreContext.context)) {
Toast.makeText(coreContext.context, R.string.chat_message_voice_recording_playback_low_volume, Toast.LENGTH_LONG).show()
Toast.makeText(
coreContext.context,
R.string.chat_message_voice_recording_playback_low_volume,
Toast.LENGTH_LONG
).show()
}
if (voiceRecordAudioFocusRequest == null) {
@ -440,7 +471,9 @@ class ChatMessageContentData(
private fun initVoiceRecordPlayer() {
Log.i("[Voice Recording] Creating player for voice record")
val playbackSoundCard = AudioRouteUtils.getAudioPlaybackDeviceIdForCallRecordingOrVoiceMessage()
Log.i("[Voice Recording] Using device $playbackSoundCard to make the voice message playback")
Log.i(
"[Voice Recording] Using device $playbackSoundCard to make the voice message playback"
)
val localPlayer = coreContext.core.createLocalPlayer(playbackSoundCard, null, null)
if (localPlayer != null) {
@ -454,8 +487,12 @@ class ChatMessageContentData(
val path = filePath.value
voiceRecordingPlayer.open(path.orEmpty())
voiceRecordDuration.value = voiceRecordingPlayer.duration
formattedDuration.value = SimpleDateFormat("mm:ss", Locale.getDefault()).format(voiceRecordingPlayer.duration) // is already in milliseconds
Log.i("[Voice Recording] Duration is ${voiceRecordDuration.value} (${voiceRecordingPlayer.duration})")
formattedDuration.value = SimpleDateFormat("mm:ss", Locale.getDefault()).format(
voiceRecordingPlayer.duration
) // is already in milliseconds
Log.i(
"[Voice Recording] Duration is ${voiceRecordDuration.value} (${voiceRecordingPlayer.duration})"
)
}
private fun stopVoiceRecording() {

View file

@ -89,7 +89,9 @@ class ChatMessageData(val chatMessage: ChatMessage) : GenericContactData(chatMes
if (chatMessage.isReply) {
val reply = chatMessage.replyMessage
if (reply != null) {
Log.i("[Chat Message Data] Message is a reply of message id [${chatMessage.replyMessageId}] sent by [${chatMessage.replyMessageSenderAddress?.asStringUriOnly()}]")
Log.i(
"[Chat Message Data] Message is a reply of message id [${chatMessage.replyMessageId}] sent by [${chatMessage.replyMessageSenderAddress?.asStringUriOnly()}]"
)
replyData.value = ChatMessageData(reply)
}
}
@ -194,7 +196,9 @@ class ChatMessageData(val chatMessage: ChatMessage) : GenericContactData(chatMes
val spannable = Spannable.Factory.getInstance().newSpannable(textContent)
text.value = PatternClickableSpan()
.add(
Pattern.compile("(?:<?sips?:)?[a-zA-Z0-9+_.\\-]+(?:@([a-zA-Z0-9+_.\\-;=~]+))+(>)?"),
Pattern.compile(
"(?:<?sips?:)?[a-zA-Z0-9+_.\\-]+(?:@([a-zA-Z0-9+_.\\-;=~]+))+(>)?"
),
object : PatternClickableSpan.SpannableClickedListener {
override fun onSpanClicked(text: String) {
Log.i("[Chat Message Data] Clicked on SIP URI: $text")
@ -222,7 +226,9 @@ class ChatMessageData(val chatMessage: ChatMessage) : GenericContactData(chatMes
).build(spannable)
isTextEmoji.value = AppUtils.isTextOnlyContainingEmoji(textContent)
} else {
Log.e("[Chat Message Data] Unexpected content with type: ${content.type}/${content.subtype}")
Log.e(
"[Chat Message Data] Unexpected content with type: ${content.type}/${content.subtype}"
)
}
}
@ -257,7 +263,12 @@ class ChatMessageData(val chatMessage: ChatMessage) : GenericContactData(chatMes
val days = seconds / 86400
return when {
days >= 1L -> AppUtils.getStringWithPlural(R.plurals.days, days.toInt())
else -> String.format("%02d:%02d:%02d", seconds / 3600, (seconds % 3600) / 60, (seconds % 60))
else -> String.format(
"%02d:%02d:%02d",
seconds / 3600,
(seconds % 3600) / 60,
(seconds % 60)
)
}
}
}

View file

@ -158,7 +158,9 @@ class ChatRoomData(val chatRoom: ChatRoom) : ContactDataInterface {
if (participants.isNotEmpty()) {
participants.first().address
} else {
Log.e("[Chat Room] ${chatRoom.peerAddress} doesn't have any participant (state ${chatRoom.state})!")
Log.e(
"[Chat Room] ${chatRoom.peerAddress} doesn't have any participant (state ${chatRoom.state})!"
)
null
}
}
@ -213,7 +215,9 @@ class ChatRoomData(val chatRoom: ChatRoom) : ContactDataInterface {
val sender: String =
coreContext.contactsManager.findContactByAddress(msg.fromAddress)?.name
?: LinphoneUtils.getDisplayName(msg.fromAddress)
builder.append(coreContext.context.getString(R.string.chat_room_last_message_sender_format, sender))
builder.append(
coreContext.context.getString(R.string.chat_room_last_message_sender_format, sender)
)
builder.append(" ")
}
@ -221,12 +225,22 @@ class ChatRoomData(val chatRoom: ChatRoom) : ContactDataInterface {
if (content.isIcalendar) {
val body = AppUtils.getString(R.string.conference_invitation)
builder.append(body)
builder.setSpan(StyleSpan(Typeface.ITALIC), builder.length - body.length, builder.length, 0)
builder.setSpan(
StyleSpan(Typeface.ITALIC),
builder.length - body.length,
builder.length,
0
)
break
} else if (content.isVoiceRecording) {
val body = AppUtils.getString(R.string.chat_message_voice_recording)
builder.append(body)
builder.setSpan(StyleSpan(Typeface.ITALIC), builder.length - body.length, builder.length, 0)
builder.setSpan(
StyleSpan(Typeface.ITALIC),
builder.length - body.length,
builder.length,
0
)
break
} else if (content.isFile || content.isFileTransfer) {
builder.append(content.name + " ")

View file

@ -27,7 +27,9 @@ import org.linphone.core.ChatRoomSecurityLevel
import org.linphone.core.Participant
import org.linphone.utils.LinphoneUtils
class DevicesListGroupData(private val participant: Participant) : GenericContactData(participant.address) {
class DevicesListGroupData(private val participant: Participant) : GenericContactData(
participant.address
) {
val securityLevelIcon: Int by lazy {
when (participant.securityLevel) {
ChatRoomSecurityLevel.Safe -> R.drawable.security_2_indicator

View file

@ -65,28 +65,60 @@ class EventData(private val eventLog: EventLog) : GenericContactData(
val context: Context = coreContext.context
text.value = when (eventLog.type) {
EventLog.Type.ConferenceCreated -> context.getString(R.string.chat_event_conference_created)
EventLog.Type.ConferenceTerminated -> context.getString(R.string.chat_event_conference_destroyed)
EventLog.Type.ConferenceParticipantAdded -> context.getString(R.string.chat_event_participant_added).format(getName())
EventLog.Type.ConferenceParticipantRemoved -> context.getString(R.string.chat_event_participant_removed).format(getName())
EventLog.Type.ConferenceSubjectChanged -> context.getString(R.string.chat_event_subject_changed).format(eventLog.subject)
EventLog.Type.ConferenceParticipantSetAdmin -> context.getString(R.string.chat_event_admin_set).format(getName())
EventLog.Type.ConferenceParticipantUnsetAdmin -> context.getString(R.string.chat_event_admin_unset).format(getName())
EventLog.Type.ConferenceParticipantDeviceAdded -> context.getString(R.string.chat_event_device_added).format(getName())
EventLog.Type.ConferenceParticipantDeviceRemoved -> context.getString(R.string.chat_event_device_removed).format(getName())
EventLog.Type.ConferenceCreated -> context.getString(
R.string.chat_event_conference_created
)
EventLog.Type.ConferenceTerminated -> context.getString(
R.string.chat_event_conference_destroyed
)
EventLog.Type.ConferenceParticipantAdded -> context.getString(
R.string.chat_event_participant_added
).format(getName())
EventLog.Type.ConferenceParticipantRemoved -> context.getString(
R.string.chat_event_participant_removed
).format(getName())
EventLog.Type.ConferenceSubjectChanged -> context.getString(
R.string.chat_event_subject_changed
).format(eventLog.subject)
EventLog.Type.ConferenceParticipantSetAdmin -> context.getString(
R.string.chat_event_admin_set
).format(getName())
EventLog.Type.ConferenceParticipantUnsetAdmin -> context.getString(
R.string.chat_event_admin_unset
).format(getName())
EventLog.Type.ConferenceParticipantDeviceAdded -> context.getString(
R.string.chat_event_device_added
).format(getName())
EventLog.Type.ConferenceParticipantDeviceRemoved -> context.getString(
R.string.chat_event_device_removed
).format(getName())
EventLog.Type.ConferenceSecurityEvent -> {
val name = getName()
when (eventLog.securityEventType) {
EventLog.SecurityEventType.EncryptionIdentityKeyChanged -> context.getString(R.string.chat_security_event_lime_identity_key_changed).format(name)
EventLog.SecurityEventType.ManInTheMiddleDetected -> context.getString(R.string.chat_security_event_man_in_the_middle_detected).format(name)
EventLog.SecurityEventType.SecurityLevelDowngraded -> context.getString(R.string.chat_security_event_security_level_downgraded).format(name)
EventLog.SecurityEventType.ParticipantMaxDeviceCountExceeded -> context.getString(R.string.chat_security_event_participant_max_count_exceeded).format(name)
EventLog.SecurityEventType.EncryptionIdentityKeyChanged -> context.getString(
R.string.chat_security_event_lime_identity_key_changed
).format(name)
EventLog.SecurityEventType.ManInTheMiddleDetected -> context.getString(
R.string.chat_security_event_man_in_the_middle_detected
).format(name)
EventLog.SecurityEventType.SecurityLevelDowngraded -> context.getString(
R.string.chat_security_event_security_level_downgraded
).format(name)
EventLog.SecurityEventType.ParticipantMaxDeviceCountExceeded -> context.getString(
R.string.chat_security_event_participant_max_count_exceeded
).format(name)
else -> "Unexpected security event for $name: ${eventLog.securityEventType}"
}
}
EventLog.Type.ConferenceEphemeralMessageDisabled -> context.getString(R.string.chat_event_ephemeral_disabled)
EventLog.Type.ConferenceEphemeralMessageEnabled -> context.getString(R.string.chat_event_ephemeral_enabled).format(formatEphemeralExpiration(context, eventLog.ephemeralMessageLifetime))
EventLog.Type.ConferenceEphemeralMessageLifetimeChanged -> context.getString(R.string.chat_event_ephemeral_lifetime_changed).format(formatEphemeralExpiration(context, eventLog.ephemeralMessageLifetime))
EventLog.Type.ConferenceEphemeralMessageDisabled -> context.getString(
R.string.chat_event_ephemeral_disabled
)
EventLog.Type.ConferenceEphemeralMessageEnabled -> context.getString(
R.string.chat_event_ephemeral_enabled
).format(formatEphemeralExpiration(context, eventLog.ephemeralMessageLifetime))
EventLog.Type.ConferenceEphemeralMessageLifetimeChanged -> context.getString(
R.string.chat_event_ephemeral_lifetime_changed
).format(formatEphemeralExpiration(context, eventLog.ephemeralMessageLifetime))
else -> "Unexpected event: ${eventLog.type}"
}
}

View file

@ -26,7 +26,9 @@ import org.linphone.contact.GenericContactData
import org.linphone.core.ChatRoomSecurityLevel
import org.linphone.utils.LinphoneUtils
class GroupInfoParticipantData(val participant: GroupChatRoomMember) : GenericContactData(participant.address) {
class GroupInfoParticipantData(val participant: GroupChatRoomMember) : GenericContactData(
participant.address
) {
val sipUri: String get() = LinphoneUtils.getDisplayableAddress(participant.address)
val isAdmin = MutableLiveData<Boolean>()

View file

@ -23,7 +23,9 @@ import org.linphone.contact.GenericContactData
import org.linphone.core.ParticipantImdnState
import org.linphone.utils.TimestampUtils
class ImdnParticipantData(val imdnState: ParticipantImdnState) : GenericContactData(imdnState.participant.address) {
class ImdnParticipantData(val imdnState: ParticipantImdnState) : GenericContactData(
imdnState.participant.address
) {
val sipUri: String = imdnState.participant.address.asStringUriOnly()
val time: String = TimestampUtils.toString(imdnState.stateChangeTime)

View file

@ -70,7 +70,9 @@ class ChatRoomCreationFragment : SecureFragment<ChatRoomCreationFragmentBinding>
binding.contactsList.layoutManager = layoutManager
// Divider between items
binding.contactsList.addItemDecoration(AppUtils.getDividerDecoration(requireContext(), layoutManager))
binding.contactsList.addItemDecoration(
AppUtils.getDividerDecoration(requireContext(), layoutManager)
)
binding.back.visibility = if (resources.getBoolean(R.bool.isTablet)) View.INVISIBLE else View.VISIBLE

View file

@ -85,7 +85,9 @@ class DetailChatRoomFragment : MasterFragment<ChatRoomDetailFragmentBinding, Cha
if (viewModel.isUserScrollingUp.value == false) {
scrollToFirstUnreadMessageOrBottom(false)
} else {
Log.d("[Chat Room] User has scrolled up manually in the messages history, don't scroll to the newly added message at the bottom & don't mark the chat room as read")
Log.d(
"[Chat Room] User has scrolled up manually in the messages history, don't scroll to the newly added message at the bottom & don't mark the chat room as read"
)
}
}
}
@ -106,7 +108,9 @@ class DetailChatRoomFragment : MasterFragment<ChatRoomDetailFragmentBinding, Cha
Log.i("[Chat Room] Messages have been displayed, scrolling to first unread")
val notAllMessagesDisplayed = scrollToFirstUnreadMessageOrBottom(false)
if (notAllMessagesDisplayed) {
Log.w("[Chat Room] More unread messages than the screen can display, do not mark chat room as read now, wait for user to scroll to bottom")
Log.w(
"[Chat Room] More unread messages than the screen can display, do not mark chat room as read now, wait for user to scroll to bottom"
)
} else {
// Consider user as scrolled to the end when marking chat room as read
viewModel.isUserScrollingUp.value = false
@ -138,10 +142,14 @@ class DetailChatRoomFragment : MasterFragment<ChatRoomDetailFragmentBinding, Cha
if (chatRoom != null) {
outState.putString("LocalSipUri", chatRoom.localAddress.asStringUriOnly())
outState.putString("RemoteSipUri", chatRoom.peerAddress.asStringUriOnly())
Log.i("[Chat Room] Saving current chat room local & remote addresses in save instance state")
Log.i(
"[Chat Room] Saving current chat room local & remote addresses in save instance state"
)
}
} else {
Log.w("[Chat Room] Can't save instance state, sharedViewModel hasn't been initialized yet")
Log.w(
"[Chat Room] Can't save instance state, sharedViewModel hasn't been initialized yet"
)
}
super.onSaveInstanceState(outState)
}
@ -155,8 +163,12 @@ class DetailChatRoomFragment : MasterFragment<ChatRoomDetailFragmentBinding, Cha
useMaterialSharedAxisXForwardAnimation = sharedViewModel.isSlidingPaneSlideable.value == false
val localSipUri = arguments?.getString("LocalSipUri") ?: savedInstanceState?.getString("LocalSipUri")
val remoteSipUri = arguments?.getString("RemoteSipUri") ?: savedInstanceState?.getString("RemoteSipUri")
val localSipUri = arguments?.getString("LocalSipUri") ?: savedInstanceState?.getString(
"LocalSipUri"
)
val remoteSipUri = arguments?.getString("RemoteSipUri") ?: savedInstanceState?.getString(
"RemoteSipUri"
)
val textToShare = arguments?.getString("TextToShare")
val filesToShare = arguments?.getStringArrayList("FilesToShare")
@ -166,12 +178,16 @@ class DetailChatRoomFragment : MasterFragment<ChatRoomDetailFragmentBinding, Cha
}
arguments?.clear()
if (localSipUri != null && remoteSipUri != null) {
Log.i("[Chat Room] Found local [$localSipUri] & remote [$remoteSipUri] addresses in arguments or saved instance state")
Log.i(
"[Chat Room] Found local [$localSipUri] & remote [$remoteSipUri] addresses in arguments or saved instance state"
)
val localAddress = Factory.instance().createAddress(localSipUri)
val remoteSipAddress = Factory.instance().createAddress(remoteSipUri)
sharedViewModel.selectedChatRoom.value = coreContext.core.searchChatRoom(
null, localAddress, remoteSipAddress,
null,
localAddress,
remoteSipAddress,
arrayOfNulls(
0
)
@ -192,7 +208,9 @@ class DetailChatRoomFragment : MasterFragment<ChatRoomDetailFragmentBinding, Cha
binding.root.addKeyboardInsetListener { keyboardVisible ->
if (keyboardVisible && chatSendingViewModel.isEmojiPickerOpen.value == true) {
Log.d("[Chat Room] Emoji picker is opened, closing it because keyboard is now visible")
Log.d(
"[Chat Room] Emoji picker is opened, closing it because keyboard is now visible"
)
chatSendingViewModel.isEmojiPickerOpen.value = false
}
}
@ -275,12 +293,18 @@ class DetailChatRoomFragment : MasterFragment<ChatRoomDetailFragmentBinding, Cha
}
}
}
RecyclerViewSwipeUtils(ItemTouchHelper.RIGHT or ItemTouchHelper.LEFT, swipeConfiguration, swipeListener)
RecyclerViewSwipeUtils(
ItemTouchHelper.RIGHT or ItemTouchHelper.LEFT,
swipeConfiguration,
swipeListener
)
.attachToRecyclerView(binding.chatMessagesList)
chatScrollListener = object : ChatScrollListener(layoutManager) {
override fun onLoadMore(totalItemsCount: Int) {
Log.i("[Chat Room] User has scrolled up far enough, load more items from history (currently there are $totalItemsCount messages displayed)")
Log.i(
"[Chat Room] User has scrolled up far enough, load more items from history (currently there are $totalItemsCount messages displayed)"
)
listViewModel.loadMoreData(totalItemsCount)
}
@ -295,7 +319,9 @@ class DetailChatRoomFragment : MasterFragment<ChatRoomDetailFragmentBinding, Cha
if (viewModel.unreadMessagesCount.value != 0 &&
coreContext.notificationsManager.currentlyDisplayedChatRoomAddress == peerAddress
) {
Log.i("[Chat Room] User has scrolled to the latest message, mark chat room as read")
Log.i(
"[Chat Room] User has scrolled to the latest message, mark chat room as read"
)
viewModel.chatRoom.markAsRead()
}
}
@ -447,7 +473,9 @@ class DetailChatRoomFragment : MasterFragment<ChatRoomDetailFragmentBinding, Cha
if (path.isNotEmpty() && !File(path).exists()) {
Log.e("[Chat Room] File not found: $path")
(requireActivity() as MainActivity).showSnackBar(R.string.chat_room_file_not_found)
(requireActivity() as MainActivity).showSnackBar(
R.string.chat_room_file_not_found
)
} else {
if (path.isEmpty()) {
val name = content.name
@ -456,14 +484,18 @@ class DetailChatRoomFragment : MasterFragment<ChatRoomDetailFragmentBinding, Cha
FileUtils.writeIntoFile(content.buffer, file)
path = file.absolutePath
content.filePath = path
Log.i("[Chat Room] Content file path was empty, created file from buffer at $path")
Log.i(
"[Chat Room] Content file path was empty, created file from buffer at $path"
)
} else if (content.isIcalendar) {
val name = "conference.ics"
val file = FileUtils.getFileStoragePath(name)
FileUtils.writeIntoFile(content.buffer, file)
path = file.absolutePath
content.filePath = path
Log.i("[Chat Room] Content file path was empty, created conference.ics from buffer at $path")
Log.i(
"[Chat Room] Content file path was empty, created conference.ics from buffer at $path"
)
}
}
@ -494,7 +526,9 @@ class DetailChatRoomFragment : MasterFragment<ChatRoomDetailFragmentBinding, Cha
)
else -> {
if (content.isFileEncrypted) {
Log.w("[Chat Room] File is encrypted and can't be opened in one of our viewers...")
Log.w(
"[Chat Room] File is encrypted and can't be opened in one of our viewers..."
)
showDialogForUserConsentBeforeExportingFileInThirdPartyApp(
content
)
@ -570,7 +604,9 @@ class DetailChatRoomFragment : MasterFragment<ChatRoomDetailFragmentBinding, Cha
val eventLog = events.find { eventLog ->
if (eventLog.eventLog.type == EventLog.Type.ConferenceChatMessage) {
(eventLog.data as ChatMessageData).chatMessage.messageId == chatMessage.messageId
} else false
} else {
false
}
}
index = events.indexOf(eventLog)
if (index == -1) {
@ -632,7 +668,7 @@ class DetailChatRoomFragment : MasterFragment<ChatRoomDetailFragmentBinding, Cha
alertDialog.setMessage(message)
alertDialog.setNeutralButton(R.string.chat_message_context_menu_copy_text) {
_, _ ->
_, _ ->
val clipboard: ClipboardManager =
coreContext.context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
val clip = ClipData.newPlainText("Chat room info", message)
@ -662,17 +698,25 @@ class DetailChatRoomFragment : MasterFragment<ChatRoomDetailFragmentBinding, Cha
if (corePreferences.holdToRecordVoiceMessage) {
when (event.action) {
MotionEvent.ACTION_DOWN -> {
Log.i("[Chat Room] Start recording voice message as long as recording button is held")
Log.i(
"[Chat Room] Start recording voice message as long as recording button is held"
)
chatSendingViewModel.startVoiceRecording()
}
MotionEvent.ACTION_UP -> {
val voiceRecordingDuration = chatSendingViewModel.voiceRecordingDuration.value ?: 0
if (voiceRecordingDuration < 1000) {
Log.w("[Chat Room] Voice recording button has been held for less than a second, considering miss click")
Log.w(
"[Chat Room] Voice recording button has been held for less than a second, considering miss click"
)
chatSendingViewModel.cancelVoiceRecording()
(activity as MainActivity).showSnackBar(R.string.chat_message_voice_recording_hold_to_record)
(activity as MainActivity).showSnackBar(
R.string.chat_message_voice_recording_hold_to_record
)
} else {
Log.i("[Chat Room] Voice recording button has been released, stop recording")
Log.i(
"[Chat Room] Voice recording button has been released, stop recording"
)
chatSendingViewModel.stopVoiceRecording()
}
view.performClick()
@ -712,7 +756,9 @@ class DetailChatRoomFragment : MasterFragment<ChatRoomDetailFragmentBinding, Cha
chatSendingViewModel.attachingFileInProgress.value = true
for (filePath in filesToShare) {
val path = FileUtils.copyToLocalStorage(filePath)
Log.i("[Chat Room] Found [$filePath] file to share, matching path is [$path]")
Log.i(
"[Chat Room] Found [$filePath] file to share, matching path is [$path]"
)
if (path != null) {
chatSendingViewModel.addAttachment(path)
}
@ -806,7 +852,9 @@ class DetailChatRoomFragment : MasterFragment<ChatRoomDetailFragmentBinding, Cha
.viewTreeObserver
.addOnGlobalLayoutListener(globalLayoutLayout)
} else {
Log.e("[Chat Room] Fragment resuming but viewModel lateinit property isn't initialized!")
Log.e(
"[Chat Room] Fragment resuming but viewModel lateinit property isn't initialized!"
)
}
}
@ -838,10 +886,10 @@ class DetailChatRoomFragment : MasterFragment<ChatRoomDetailFragmentBinding, Cha
withContext(Dispatchers.Main) {
chatSendingViewModel.attachingFileInProgress.value = true
for (
fileToUploadPath in FileUtils.getFilesPathFromPickerIntent(
data,
chatSendingViewModel.temporaryFileUploadPath
)
fileToUploadPath in FileUtils.getFilesPathFromPickerIntent(
data,
chatSendingViewModel.temporaryFileUploadPath
)
) {
Log.i("[Chat Room] Found [$fileToUploadPath] file from intent")
chatSendingViewModel.addAttachment(fileToUploadPath)
@ -867,9 +915,13 @@ class DetailChatRoomFragment : MasterFragment<ChatRoomDetailFragmentBinding, Cha
dialog.dismiss()
}
val okLabel = if (viewModel.oneParticipantOneDevice) getString(R.string.dialog_call) else getString(
R.string.dialog_ok
)
val okLabel = if (viewModel.oneParticipantOneDevice) {
getString(R.string.dialog_call)
} else {
getString(
R.string.dialog_ok
)
}
dialogViewModel.showOkButton(
{ doNotAskAgain ->
if (doNotAskAgain) corePreferences.limeSecurityPopupEnabled = false
@ -921,7 +973,9 @@ class DetailChatRoomFragment : MasterFragment<ChatRoomDetailFragmentBinding, Cha
}
private fun showForwardConfirmationDialog(chatMessage: ChatMessage) {
val viewModel = DialogViewModel(getString(R.string.chat_message_forward_confirmation_dialog))
val viewModel = DialogViewModel(
getString(R.string.chat_message_forward_confirmation_dialog)
)
viewModel.iconResource = R.drawable.forward_message_default
viewModel.showIcon = true
val dialog: Dialog = DialogUtils.getDialog(requireContext(), viewModel)
@ -946,7 +1000,9 @@ class DetailChatRoomFragment : MasterFragment<ChatRoomDetailFragmentBinding, Cha
private fun showPopupMenu(chatRoom: ChatRoom) {
val popupView: ChatRoomMenuBindingImpl = DataBindingUtil.inflate(
LayoutInflater.from(context),
R.layout.chat_room_menu, null, false
R.layout.chat_room_menu,
null,
false
)
val readOnly = chatRoom.isReadOnly
popupView.ephemeralEnabled = !readOnly
@ -1014,7 +1070,9 @@ class DetailChatRoomFragment : MasterFragment<ChatRoomDetailFragmentBinding, Cha
if (viewModel.ephemeralChatRoom) {
if (chatRoom.currentParams.ephemeralMode == ChatRoomEphemeralMode.AdminManaged) {
if (chatRoom.me?.isAdmin == false) {
Log.w("[Chat Room] Hiding ephemeral menu as mode is admin managed and we aren't admin")
Log.w(
"[Chat Room] Hiding ephemeral menu as mode is admin managed and we aren't admin"
)
popupView.ephemeralHidden = true
totalSize -= itemSize
}
@ -1117,7 +1175,9 @@ class DetailChatRoomFragment : MasterFragment<ChatRoomDetailFragmentBinding, Cha
R.string.chat_message_removal_info,
R.string.chat_message_abort_removal
) {
Log.i("[Chat Room] Canceled message/event deletion task: $task for message/event at position $position")
Log.i(
"[Chat Room] Canceled message/event deletion task: $task for message/event at position $position"
)
adapter.notifyItemRangeChanged(position, adapter.itemCount - position)
task.cancel()
}
@ -1136,7 +1196,9 @@ class DetailChatRoomFragment : MasterFragment<ChatRoomDetailFragmentBinding, Cha
adapter.itemCount - 1
}
Log.i("[Chat Room] Scrolling to position $indexToScrollTo, first unread message is at $firstUnreadMessagePosition")
Log.i(
"[Chat Room] Scrolling to position $indexToScrollTo, first unread message is at $firstUnreadMessagePosition"
)
scrollTo(indexToScrollTo, smooth)
if (firstUnreadMessagePosition == 0) {
@ -1219,7 +1281,9 @@ class DetailChatRoomFragment : MasterFragment<ChatRoomDetailFragmentBinding, Cha
{
dialog.dismiss()
lifecycleScope.launch {
Log.i("[Chat Room] [VFS] Content is encrypted, requesting plain file path for file [${content.filePath}]")
Log.i(
"[Chat Room] [VFS] Content is encrypted, requesting plain file path for file [${content.filePath}]"
)
val plainFilePath = content.exportPlainFile()
if (!FileUtils.openFileInThirdPartyApp(requireActivity(), plainFilePath)) {
showDialogToSuggestOpeningFileAsText()
@ -1245,7 +1309,10 @@ class DetailChatRoomFragment : MasterFragment<ChatRoomDetailFragmentBinding, Cha
}
private fun showGroupCallDialog() {
val dialogViewModel = DialogViewModel(getString(R.string.conference_start_group_call_dialog_message), getString(R.string.conference_start_group_call_dialog_title))
val dialogViewModel = DialogViewModel(
getString(R.string.conference_start_group_call_dialog_message),
getString(R.string.conference_start_group_call_dialog_title)
)
val dialog: Dialog = DialogUtils.getDialog(requireContext(), dialogViewModel)
dialogViewModel.iconResource = R.drawable.icon_video_conf_incoming

View file

@ -76,7 +76,9 @@ class GroupInfoFragment : SecureFragment<ChatRoomGroupInfoFragmentBinding>() {
binding.participants.layoutManager = layoutManager
// Divider between items
binding.participants.addItemDecoration(AppUtils.getDividerDecoration(requireContext(), layoutManager))
binding.participants.addItemDecoration(
AppUtils.getDividerDecoration(requireContext(), layoutManager)
)
viewModel.participants.observe(
viewLifecycleOwner
@ -148,7 +150,9 @@ class GroupInfoFragment : SecureFragment<ChatRoomGroupInfoFragmentBinding>() {
}
binding.setLeaveClickListener {
val dialogViewModel = DialogViewModel(getString(R.string.chat_room_group_info_leave_dialog_message))
val dialogViewModel = DialogViewModel(
getString(R.string.chat_room_group_info_leave_dialog_message)
)
val dialog: Dialog = DialogUtils.getDialog(requireContext(), dialogViewModel)
dialogViewModel.showDeleteButton(
@ -190,7 +194,11 @@ class GroupInfoFragment : SecureFragment<ChatRoomGroupInfoFragmentBinding>() {
} else {
list.add(
GroupInfoParticipantData(
GroupChatRoomMember(address, false, hasLimeX3DHCapability = viewModel.isEncrypted.value == true)
GroupChatRoomMember(
address,
false,
hasLimeX3DHCapability = viewModel.isEncrypted.value == true
)
)
)
}

View file

@ -84,7 +84,9 @@ class ImdnFragment : SecureFragment<ChatRoomImdnFragmentBinding>() {
binding.participantsList.layoutManager = layoutManager
// Divider between items
binding.participantsList.addItemDecoration(AppUtils.getDividerDecoration(requireContext(), layoutManager))
binding.participantsList.addItemDecoration(
AppUtils.getDividerDecoration(requireContext(), layoutManager)
)
// Displays state header
val headerItemDecoration = RecyclerViewHeaderDecoration(requireContext(), adapter)

View file

@ -133,7 +133,9 @@ class MasterChatRoomsFragment : MasterFragment<ChatRoomMasterFragmentBinding, Ch
val navHostFragment =
childFragmentManager.findFragmentById(R.id.chat_nav_container) as NavHostFragment
if (navHostFragment.navController.currentDestination?.id == R.id.emptyChatFragment) {
Log.i("[Chat] Foldable device has been folded, closing side pane with empty fragment")
Log.i(
"[Chat] Foldable device has been folded, closing side pane with empty fragment"
)
binding.slidingPane.closePane()
}
}
@ -212,7 +214,9 @@ class MasterChatRoomsFragment : MasterFragment<ChatRoomMasterFragmentBinding, Ch
if (!binding.slidingPane.isSlideable &&
deletedChatRoom == sharedViewModel.selectedChatRoom.value
) {
Log.i("[Chat] Currently displayed chat room has been deleted, removing detail fragment")
Log.i(
"[Chat] Currently displayed chat room has been deleted, removing detail fragment"
)
clearDisplayedChatRoom()
}
dialog.dismiss()
@ -224,11 +228,17 @@ class MasterChatRoomsFragment : MasterFragment<ChatRoomMasterFragmentBinding, Ch
}
}
}
RecyclerViewSwipeUtils(ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT, swipeConfiguration, swipeListener)
RecyclerViewSwipeUtils(
ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT,
swipeConfiguration,
swipeListener
)
.attachToRecyclerView(binding.chatList)
// Divider between items
binding.chatList.addItemDecoration(AppUtils.getDividerDecoration(requireContext(), layoutManager))
binding.chatList.addItemDecoration(
AppUtils.getDividerDecoration(requireContext(), layoutManager)
)
listViewModel.chatRooms.observe(
viewLifecycleOwner
@ -256,7 +266,9 @@ class MasterChatRoomsFragment : MasterFragment<ChatRoomMasterFragmentBinding, Ch
if (!binding.slidingPane.isOpen) {
Log.w("[Chat] Chat room is displayed but sliding pane is closed...")
if (!binding.slidingPane.openPane()) {
Log.e("[Chat] Tried to open pane to workaround already displayed chat room issue, failed!")
Log.e(
"[Chat] Tried to open pane to workaround already displayed chat room issue, failed!"
)
}
} else {
Log.w("[Chat] This chat room is already displayed!")
@ -314,11 +326,18 @@ class MasterChatRoomsFragment : MasterFragment<ChatRoomMasterFragmentBinding, Ch
val localSipUri = arguments?.getString("LocalSipUri")
val remoteSipUri = arguments?.getString("RemoteSipUri")
if (localSipUri != null && remoteSipUri != null) {
Log.i("[Chat] Found local [$localSipUri] & remote [$remoteSipUri] addresses in arguments")
Log.i(
"[Chat] Found local [$localSipUri] & remote [$remoteSipUri] addresses in arguments"
)
arguments?.clear()
val localAddress = Factory.instance().createAddress(localSipUri)
val remoteSipAddress = Factory.instance().createAddress(remoteSipUri)
val chatRoom = coreContext.core.searchChatRoom(null, localAddress, remoteSipAddress, arrayOfNulls(0))
val chatRoom = coreContext.core.searchChatRoom(
null,
localAddress,
remoteSipAddress,
arrayOfNulls(0)
)
if (chatRoom != null) {
Log.i("[Chat] Found matching chat room $chatRoom")
adapter.selectedChatRoomEvent.value = Event(chatRoom)

View file

@ -209,7 +209,9 @@ class ChatMessageSendingViewModel(private val chatRoom: ChatRoom) : ViewModel()
attachments.value = list
val pathToDelete = attachment.path
Log.i("[Chat Message Sending] Attachment is being removed, delete local copy [$pathToDelete]")
Log.i(
"[Chat Message Sending] Attachment is being removed, delete local copy [$pathToDelete]"
)
FileUtils.deleteFile(pathToDelete)
sendMessageEnabled.value = textToSend.value.orEmpty().trim().isNotEmpty() || list.isNotEmpty() || isPendingVoiceRecord.value == true
@ -227,10 +229,11 @@ class ChatMessageSendingViewModel(private val chatRoom: ChatRoom) : ViewModel()
private fun createChatMessage(): ChatMessage {
val pendingMessageToReplyTo = pendingChatMessageToReplyTo.value
return if (isPendingAnswer.value == true && pendingMessageToReplyTo != null)
return if (isPendingAnswer.value == true && pendingMessageToReplyTo != null) {
chatRoom.createReplyMessage(pendingMessageToReplyTo.chatMessage)
else
} else {
chatRoom.createEmptyMessage()
}
}
fun sendMessage() {
@ -249,7 +252,9 @@ class ChatMessageSendingViewModel(private val chatRoom: ChatRoom) : ViewModel()
if (isPendingVoiceRecord.value == true && recorder.file != null) {
val content = recorder.createContent()
if (content != null) {
Log.i("[Chat Message Sending] Voice recording content created, file name is ${content.name} and duration is ${content.fileDuration}")
Log.i(
"[Chat Message Sending] Voice recording content created, file name is ${content.name} and duration is ${content.fileDuration}"
)
message.addContent(content)
voiceRecord = true
} else {
@ -373,7 +378,9 @@ class ChatMessageSendingViewModel(private val chatRoom: ChatRoom) : ViewModel()
}
val tempFileName = "voice-recording-${System.currentTimeMillis()}.$extension"
val file = FileUtils.getFileStoragePath(tempFileName)
Log.w("[Chat Message Sending] Recorder is closed, starting recording in ${file.absoluteFile}")
Log.w(
"[Chat Message Sending] Recorder is closed, starting recording in ${file.absoluteFile}"
)
recorder.open(file.absolutePath)
recorder.start()
}
@ -393,10 +400,14 @@ class ChatMessageSendingViewModel(private val chatRoom: ChatRoom) : ViewModel()
withContext(Dispatchers.Main) {
val duration = recorder.duration
voiceRecordingDuration.value = recorder.duration % voiceRecordingProgressBarMax
formattedDuration.value = SimpleDateFormat("mm:ss", Locale.getDefault()).format(duration) // duration is in ms
formattedDuration.value = SimpleDateFormat("mm:ss", Locale.getDefault()).format(
duration
) // duration is in ms
if (duration >= maxVoiceRecordDuration) {
Log.w("[Chat Message Sending] Max duration for voice recording exceeded (${maxVoiceRecordDuration}ms), stopping.")
Log.w(
"[Chat Message Sending] Max duration for voice recording exceeded (${maxVoiceRecordDuration}ms), stopping."
)
stopVoiceRecording()
}
}
@ -463,7 +474,11 @@ class ChatMessageSendingViewModel(private val chatRoom: ChatRoom) : ViewModel()
}
if (AppUtils.isMediaVolumeLow(coreContext.context)) {
Toast.makeText(coreContext.context, R.string.chat_message_voice_recording_playback_low_volume, Toast.LENGTH_LONG).show()
Toast.makeText(
coreContext.context,
R.string.chat_message_voice_recording_playback_low_volume,
Toast.LENGTH_LONG
).show()
}
if (voiceRecordAudioFocusRequest == null) {
@ -508,7 +523,9 @@ class ChatMessageSendingViewModel(private val chatRoom: ChatRoom) : ViewModel()
val recordingAudioDevice = AudioRouteUtils.getAudioRecordingDeviceForVoiceMessage()
recorderParams.audioDevice = recordingAudioDevice
Log.i("[Chat Message Sending] Using device ${recorderParams.audioDevice?.id} to make the voice message recording")
Log.i(
"[Chat Message Sending] Using device ${recorderParams.audioDevice?.id} to make the voice message recording"
)
recorder = coreContext.core.createRecorder(recorderParams)
}
@ -517,7 +534,9 @@ class ChatMessageSendingViewModel(private val chatRoom: ChatRoom) : ViewModel()
Log.i("[Chat Message Sending] Creating player for voice record")
val playbackSoundCard = AudioRouteUtils.getAudioPlaybackDeviceIdForCallRecordingOrVoiceMessage()
Log.i("[Chat Message Sending] Using device $playbackSoundCard to make the voice message playback")
Log.i(
"[Chat Message Sending] Using device $playbackSoundCard to make the voice message playback"
)
val localPlayer = coreContext.core.createLocalPlayer(playbackSoundCard, null, null)
if (localPlayer != null) {
@ -559,6 +578,10 @@ class ChatMessageSendingViewModel(private val chatRoom: ChatRoom) : ViewModel()
}
private fun updateChatRoomReadOnlyState() {
isReadOnly.value = chatRoom.isReadOnly || (chatRoom.hasCapability(ChatRoomCapabilities.Conference.toInt()) && chatRoom.participants.isEmpty())
isReadOnly.value = chatRoom.isReadOnly || (
chatRoom.hasCapability(
ChatRoomCapabilities.Conference.toInt()
) && chatRoom.participants.isEmpty()
)
}
}

View file

@ -107,7 +107,9 @@ class ChatMessagesListViewModel(private val chatRoom: ChatRoom) : ViewModel() {
}
override fun onEphemeralMessageDeleted(chatRoom: ChatRoom, eventLog: EventLog) {
Log.i("[Chat Messages] An ephemeral chat message has expired, removing it from event list")
Log.i(
"[Chat Messages] An ephemeral chat message has expired, removing it from event list"
)
deleteEvent(eventLog)
}
@ -163,7 +165,10 @@ class ChatMessagesListViewModel(private val chatRoom: ChatRoom) : ViewModel() {
upperBound = maxSize
}
val history: Array<EventLog> = chatRoom.getHistoryRangeEvents(totalItemsCount, upperBound)
val history: Array<EventLog> = chatRoom.getHistoryRangeEvents(
totalItemsCount,
upperBound
)
val list = arrayListOf<EventLogData>()
for (eventLog in history) {
list.add(EventLogData(eventLog))
@ -187,7 +192,9 @@ class ChatMessagesListViewModel(private val chatRoom: ChatRoom) : ViewModel() {
val list = arrayListOf<EventLogData>()
val unreadCount = chatRoom.unreadMessagesCount
var loadCount = max(MESSAGES_PER_PAGE, unreadCount)
Log.i("[Chat Messages] $unreadCount unread messages in this chat room, loading $loadCount from history")
Log.i(
"[Chat Messages] $unreadCount unread messages in this chat room, loading $loadCount from history"
)
val history = chatRoom.getHistoryEvents(loadCount)
var messageCount = 0
@ -200,8 +207,13 @@ class ChatMessagesListViewModel(private val chatRoom: ChatRoom) : ViewModel() {
// Load enough events to have at least all unread messages
while (unreadCount > 0 && messageCount < unreadCount) {
Log.w("[Chat Messages] There is only $messageCount messages in the last $loadCount events, loading $MESSAGES_PER_PAGE more")
val moreHistory = chatRoom.getHistoryRangeEvents(loadCount, loadCount + MESSAGES_PER_PAGE)
Log.w(
"[Chat Messages] There is only $messageCount messages in the last $loadCount events, loading $MESSAGES_PER_PAGE more"
)
val moreHistory = chatRoom.getHistoryRangeEvents(
loadCount,
loadCount + MESSAGES_PER_PAGE
)
loadCount += MESSAGES_PER_PAGE
for (eventLog in moreHistory) {
list.add(EventLogData(eventLog))
@ -235,14 +247,18 @@ class ChatMessagesListViewModel(private val chatRoom: ChatRoom) : ViewModel() {
data.eventLog.type == EventLog.Type.ConferenceChatMessage && data.eventLog.chatMessage?.messageId == chatMessage.messageId
}
if (existingEvent != null) {
Log.w("[Chat Messages] Found already present chat message, don't add it it's probably the result of an auto download or an aggregated message received before but notified after the conversation was displayed")
Log.w(
"[Chat Messages] Found already present chat message, don't add it it's probably the result of an auto download or an aggregated message received before but notified after the conversation was displayed"
)
return
}
if (!PermissionHelper.get().hasWriteExternalStoragePermission()) {
for (content in chatMessage.contents) {
if (content.isFileTransfer) {
Log.i("[Chat Messages] Android < 10 detected and WRITE_EXTERNAL_STORAGE permission isn't granted yet")
Log.i(
"[Chat Messages] Android < 10 detected and WRITE_EXTERNAL_STORAGE permission isn't granted yet"
)
requestWriteExternalStoragePermissionEvent.value = Event(true)
}
}

View file

@ -68,7 +68,9 @@ class ChatRoomCreationViewModel : ContactsSelectionViewModel() {
fun updateEncryption(encrypted: Boolean) {
if (!encrypted && secureChatMandatory) {
Log.w("[Chat Room Creation] Something tries to force plain text chat room even if secureChatMandatory is enabled!")
Log.w(
"[Chat Room Creation] Something tries to force plain text chat room even if secureChatMandatory is enabled!"
)
return
}
isEncrypted.value = encrypted
@ -79,7 +81,10 @@ class ChatRoomCreationViewModel : ContactsSelectionViewModel() {
val defaultAccount = coreContext.core.defaultAccount
var room: ChatRoom?
val address = searchResult.address ?: coreContext.core.interpretUrl(searchResult.phoneNumber ?: "", LinphoneUtils.applyInternationalPrefix())
val address = searchResult.address ?: coreContext.core.interpretUrl(
searchResult.phoneNumber ?: "",
LinphoneUtils.applyInternationalPrefix()
)
if (address == null) {
Log.e("[Chat Room Creation] Can't get a valid address from search result $searchResult")
onMessageToNotifyEvent.value = Event(R.string.chat_room_creation_failed_snack)
@ -94,12 +99,15 @@ class ChatRoomCreationViewModel : ContactsSelectionViewModel() {
if (encrypted) {
params.isEncryptionEnabled = true
params.backend = ChatRoomBackend.FlexisipChat
params.ephemeralMode = if (corePreferences.useEphemeralPerDeviceMode)
params.ephemeralMode = if (corePreferences.useEphemeralPerDeviceMode) {
ChatRoomEphemeralMode.DeviceManaged
else
} else {
ChatRoomEphemeralMode.AdminManaged
}
params.ephemeralLifetime = 0 // Make sure ephemeral is disabled by default
Log.i("[Chat Room Creation] Ephemeral mode is ${params.ephemeralMode}, lifetime is ${params.ephemeralLifetime}")
Log.i(
"[Chat Room Creation] Ephemeral mode is ${params.ephemeralMode}, lifetime is ${params.ephemeralLifetime}"
)
params.subject = AppUtils.getString(R.string.chat_room_dummy_subject)
}
@ -108,7 +116,9 @@ class ChatRoomCreationViewModel : ContactsSelectionViewModel() {
room = coreContext.core.searchChatRoom(params, localAddress, null, participants)
if (room == null) {
Log.w("[Chat Room Creation] Couldn't find existing 1-1 chat room with remote ${address.asStringUriOnly()}, encryption=$encrypted and local identity ${localAddress?.asStringUriOnly()}")
Log.w(
"[Chat Room Creation] Couldn't find existing 1-1 chat room with remote ${address.asStringUriOnly()}, encryption=$encrypted and local identity ${localAddress?.asStringUriOnly()}"
)
room = coreContext.core.createChatRoom(params, localAddress, participants)
if (room != null) {
@ -119,7 +129,9 @@ class ChatRoomCreationViewModel : ContactsSelectionViewModel() {
chatRoomCreatedEvent.value = Event(room)
waitForChatRoomCreation.value = false
} else {
Log.i("[Chat Room Creation] Chat room creation is pending [$state], waiting for Created state")
Log.i(
"[Chat Room Creation] Chat room creation is pending [$state], waiting for Created state"
)
room.addListener(listener)
}
} else {
@ -127,11 +139,15 @@ class ChatRoomCreationViewModel : ContactsSelectionViewModel() {
waitForChatRoomCreation.value = false
}
} else {
Log.e("[Chat Room Creation] Couldn't create chat room with remote ${address.asStringUriOnly()} and local identity ${localAddress?.asStringUriOnly()}")
Log.e(
"[Chat Room Creation] Couldn't create chat room with remote ${address.asStringUriOnly()} and local identity ${localAddress?.asStringUriOnly()}"
)
waitForChatRoomCreation.value = false
}
} else {
Log.i("[Chat Room Creation] Found existing 1-1 chat room with remote ${address.asStringUriOnly()}, encryption=$encrypted and local identity ${localAddress?.asStringUriOnly()}")
Log.i(
"[Chat Room Creation] Found existing 1-1 chat room with remote ${address.asStringUriOnly()}, encryption=$encrypted and local identity ${localAddress?.asStringUriOnly()}"
)
chatRoomCreatedEvent.value = Event(room)
waitForChatRoomCreation.value = false
}

View file

@ -111,7 +111,10 @@ class ChatRoomViewModel(val chatRoom: ChatRoom) : ViewModel(), ContactDataInterf
private var addressToCall: Address? = null
private val bounceAnimator: ValueAnimator by lazy {
ValueAnimator.ofFloat(AppUtils.getDimension(R.dimen.tabs_fragment_unread_count_bounce_offset), 0f).apply {
ValueAnimator.ofFloat(
AppUtils.getDimension(R.dimen.tabs_fragment_unread_count_bounce_offset),
0f
).apply {
addUpdateListener {
val value = it.animatedValue as Float
chatUnreadCountTranslateY.value = value
@ -265,11 +268,11 @@ class ChatRoomViewModel(val chatRoom: ChatRoom) : ViewModel(), ContactDataInterf
val localAddress = chatRoom.localAddress.clone()
localAddress.clean() // Remove GRUU
val addresses = Array(chatRoom.participants.size) {
index ->
index ->
chatRoom.participants[index].address
}
val localAccount = coreContext.core.accountList.find {
account ->
account ->
account.params.identityAddress?.weakEqual(localAddress) ?: false
}
@ -298,7 +301,9 @@ class ChatRoomViewModel(val chatRoom: ChatRoom) : ViewModel(), ContactDataInterf
if (chatRoom.participants.isNotEmpty()) {
chatRoom.participants[0].address
} else {
Log.e("[Chat Room] ${chatRoom.peerAddress} doesn't have any participant (state ${chatRoom.state})!")
Log.e(
"[Chat Room] ${chatRoom.peerAddress} doesn't have any participant (state ${chatRoom.state})!"
)
null
}
}
@ -338,11 +343,18 @@ class ChatRoomViewModel(val chatRoom: ChatRoom) : ViewModel(), ContactDataInterf
}
TimestampUtils.isYesterday(timestamp) -> {
val time = TimestampUtils.timeToString(timestamp, timestampInSecs = true)
val text = AppUtils.getString(R.string.chat_room_presence_last_seen_online_yesterday)
val text = AppUtils.getString(
R.string.chat_room_presence_last_seen_online_yesterday
)
"$text $time"
}
else -> {
val date = TimestampUtils.toString(timestamp, onlyDate = true, shortDate = false, hideYear = true)
val date = TimestampUtils.toString(
timestamp,
onlyDate = true,
shortDate = false,
hideYear = true
)
val text = AppUtils.getString(R.string.chat_room_presence_last_seen_online)
"$text $date"
}
@ -390,7 +402,11 @@ class ChatRoomViewModel(val chatRoom: ChatRoom) : ViewModel(), ContactDataInterf
composing += if (composing.isNotEmpty()) ", " else ""
composing += contact?.name ?: LinphoneUtils.getDisplayName(address)
}
composingList.value = AppUtils.getStringWithPlural(R.plurals.chat_room_remote_composing, chatRoom.composingAddresses.size, composing)
composingList.value = AppUtils.getStringWithPlural(
R.plurals.chat_room_remote_composing,
chatRoom.composingAddresses.size,
composing
)
}
private fun updateParticipants() {
@ -400,10 +416,11 @@ class ChatRoomViewModel(val chatRoom: ChatRoom) : ViewModel(), ContactDataInterf
chatRoom.me?.devices?.size == 1 &&
participants.firstOrNull()?.devices?.size == 1
addressToCall = if (basicChatRoom)
addressToCall = if (basicChatRoom) {
chatRoom.peerAddress
else
} else {
participants.firstOrNull()?.address
}
onlyParticipantOnlyDeviceAddress = participants.firstOrNull()?.devices?.firstOrNull()?.address
}
@ -411,7 +428,8 @@ class ChatRoomViewModel(val chatRoom: ChatRoom) : ViewModel(), ContactDataInterf
private fun updateUnreadMessageCount() {
val count = chatRoom.unreadMessagesCount
unreadMessagesCount.value = count
if (count > 0 && corePreferences.enableAnimations) bounceAnimator.start()
else if (count == 0 && bounceAnimator.isStarted) bounceAnimator.end()
if (count > 0 && corePreferences.enableAnimations) {
bounceAnimator.start()
} else if (count == 0 && bounceAnimator.isStarted) bounceAnimator.end()
}
}

View file

@ -48,7 +48,9 @@ class ChatRoomsListViewModel : MessageNotifierViewModel() {
private val chatRoomListener = object : ChatRoomListenerStub() {
override fun onStateChanged(chatRoom: ChatRoom, newState: ChatRoom.State) {
if (newState == ChatRoom.State.Deleted) {
Log.i("[Chat Rooms] Chat room [${LinphoneUtils.getChatRoomId(chatRoom)}] is in Deleted state, removing it from list")
Log.i(
"[Chat Rooms] Chat room [${LinphoneUtils.getChatRoomId(chatRoom)}] is in Deleted state, removing it from list"
)
val list = arrayListOf<ChatRoomData>()
val id = LinphoneUtils.getChatRoomId(chatRoom)
for (data in chatRooms.value.orEmpty()) {
@ -64,14 +66,18 @@ class ChatRoomsListViewModel : MessageNotifierViewModel() {
private val listener: CoreListenerStub = object : CoreListenerStub() {
override fun onChatRoomStateChanged(core: Core, chatRoom: ChatRoom, state: ChatRoom.State) {
if (state == ChatRoom.State.Created) {
Log.i("[Chat Rooms] Chat room [${LinphoneUtils.getChatRoomId(chatRoom)}] is in Created state, adding it to list")
Log.i(
"[Chat Rooms] Chat room [${LinphoneUtils.getChatRoomId(chatRoom)}] is in Created state, adding it to list"
)
val data = ChatRoomData(chatRoom)
val list = arrayListOf<ChatRoomData>()
list.add(data)
list.addAll(chatRooms.value.orEmpty())
chatRooms.value = list
} else if (state == ChatRoom.State.TerminationFailed) {
Log.e("[Chat Rooms] Group chat room removal for address ${chatRoom.peerAddress.asStringUriOnly()} has failed !")
Log.e(
"[Chat Rooms] Group chat room removal for address ${chatRoom.peerAddress.asStringUriOnly()} has failed !"
)
onMessageToNotifyEvent.value = Event(R.string.chat_room_removal_failed_snack)
}
}
@ -155,8 +161,11 @@ class ChatRoomsListViewModel : MessageNotifierViewModel() {
fun notifyChatRoomUpdate(chatRoom: ChatRoom) {
val index = findChatRoomIndex(chatRoom)
if (index == -1) updateChatRooms()
else chatRoomIndexUpdatedEvent.value = Event(index)
if (index == -1) {
updateChatRooms()
} else {
chatRoomIndexUpdatedEvent.value = Event(index)
}
}
private fun reorderChatRooms() {

View file

@ -50,7 +50,9 @@ class EphemeralViewModel(private val chatRoom: ChatRoom) : ViewModel() {
}
init {
Log.i("[Ephemeral Messages] Current lifetime is ${chatRoom.ephemeralLifetime}, ephemeral enabled? ${chatRoom.isEphemeralEnabled}")
Log.i(
"[Ephemeral Messages] Current lifetime is ${chatRoom.ephemeralLifetime}, ephemeral enabled? ${chatRoom.isEphemeralEnabled}"
)
currentSelectedDuration = if (chatRoom.isEphemeralEnabled) chatRoom.ephemeralLifetime else 0
computeEphemeralDurationValues()
}
@ -59,10 +61,14 @@ class EphemeralViewModel(private val chatRoom: ChatRoom) : ViewModel() {
Log.i("[Ephemeral Messages] Selected value is $currentSelectedDuration")
if (currentSelectedDuration > 0) {
if (chatRoom.ephemeralLifetime != currentSelectedDuration) {
Log.i("[Ephemeral Messages] Setting new lifetime for ephemeral messages to $currentSelectedDuration")
Log.i(
"[Ephemeral Messages] Setting new lifetime for ephemeral messages to $currentSelectedDuration"
)
chatRoom.ephemeralLifetime = currentSelectedDuration
} else {
Log.i("[Ephemeral Messages] Configured lifetime for ephemeral messages was already $currentSelectedDuration")
Log.i(
"[Ephemeral Messages] Configured lifetime for ephemeral messages was already $currentSelectedDuration"
)
}
if (!chatRoom.isEphemeralEnabled) {
@ -77,12 +83,54 @@ class EphemeralViewModel(private val chatRoom: ChatRoom) : ViewModel() {
private fun computeEphemeralDurationValues() {
val list = arrayListOf<EphemeralDurationData>()
list.add(EphemeralDurationData(R.string.chat_room_ephemeral_message_disabled, currentSelectedDuration, 0, listener))
list.add(EphemeralDurationData(R.string.chat_room_ephemeral_message_one_minute, currentSelectedDuration, 60, listener))
list.add(EphemeralDurationData(R.string.chat_room_ephemeral_message_one_hour, currentSelectedDuration, 3600, listener))
list.add(EphemeralDurationData(R.string.chat_room_ephemeral_message_one_day, currentSelectedDuration, 86400, listener))
list.add(EphemeralDurationData(R.string.chat_room_ephemeral_message_three_days, currentSelectedDuration, 259200, listener))
list.add(EphemeralDurationData(R.string.chat_room_ephemeral_message_one_week, currentSelectedDuration, 604800, listener))
list.add(
EphemeralDurationData(
R.string.chat_room_ephemeral_message_disabled,
currentSelectedDuration,
0,
listener
)
)
list.add(
EphemeralDurationData(
R.string.chat_room_ephemeral_message_one_minute,
currentSelectedDuration,
60,
listener
)
)
list.add(
EphemeralDurationData(
R.string.chat_room_ephemeral_message_one_hour,
currentSelectedDuration,
3600,
listener
)
)
list.add(
EphemeralDurationData(
R.string.chat_room_ephemeral_message_one_day,
currentSelectedDuration,
86400,
listener
)
)
list.add(
EphemeralDurationData(
R.string.chat_room_ephemeral_message_three_days,
currentSelectedDuration,
259200,
listener
)
)
list.add(
EphemeralDurationData(
R.string.chat_room_ephemeral_message_one_week,
currentSelectedDuration,
604800,
listener
)
)
durationsList.value = list
}
}

View file

@ -99,7 +99,9 @@ class GroupInfoViewModel(val chatRoom: ChatRoom?) : MessageNotifierViewModel() {
subject.value = chatRoom?.subject
isMeAdmin.value = chatRoom == null || (chatRoom.me?.isAdmin == true && !chatRoom.isReadOnly)
canLeaveGroup.value = chatRoom != null && !chatRoom.isReadOnly
isEncrypted.value = corePreferences.forceEndToEndEncryptedChat || chatRoom?.hasCapability(ChatRoomCapabilities.Encrypted.toInt()) == true
isEncrypted.value = corePreferences.forceEndToEndEncryptedChat || chatRoom?.hasCapability(
ChatRoomCapabilities.Encrypted.toInt()
) == true
if (chatRoom != null) updateParticipants()
@ -120,13 +122,16 @@ class GroupInfoViewModel(val chatRoom: ChatRoom?) : MessageNotifierViewModel() {
params.isEncryptionEnabled = corePreferences.forceEndToEndEncryptedChat || isEncrypted.value == true
params.isGroupEnabled = true
if (params.isEncryptionEnabled) {
params.ephemeralMode = if (corePreferences.useEphemeralPerDeviceMode)
params.ephemeralMode = if (corePreferences.useEphemeralPerDeviceMode) {
ChatRoomEphemeralMode.DeviceManaged
else
} else {
ChatRoomEphemeralMode.AdminManaged
}
}
params.ephemeralLifetime = 0 // Make sure ephemeral is disabled by default
Log.i("[Chat Room Group Info] Ephemeral mode is ${params.ephemeralMode}, lifetime is ${params.ephemeralLifetime}")
Log.i(
"[Chat Room Group Info] Ephemeral mode is ${params.ephemeralMode}, lifetime is ${params.ephemeralLifetime}"
)
params.subject = subject.value
val addresses = arrayOfNulls<Address>(participants.value.orEmpty().size)
@ -137,7 +142,11 @@ class GroupInfoViewModel(val chatRoom: ChatRoom?) : MessageNotifierViewModel() {
index += 1
}
val chatRoom: ChatRoom? = coreContext.core.createChatRoom(params, coreContext.core.defaultAccount?.params?.identityAddress, addresses)
val chatRoom: ChatRoom? = coreContext.core.createChatRoom(
params,
coreContext.core.defaultAccount?.params?.identityAddress,
addresses
)
chatRoom?.addListener(listener)
if (chatRoom == null) {
Log.e("[Chat Room Group Info] Couldn't create chat room!")
@ -162,7 +171,9 @@ class GroupInfoViewModel(val chatRoom: ChatRoom?) : MessageNotifierViewModel() {
participant.address.weakEqual(member.participant.address)
}
if (member == null) {
Log.w("[Chat Room Group Info] Participant ${participant.address.asStringUriOnly()} will be removed from group")
Log.w(
"[Chat Room Group Info] Participant ${participant.address.asStringUriOnly()} will be removed from group"
)
participantsToRemove.add(participant)
}
}
@ -180,12 +191,19 @@ class GroupInfoViewModel(val chatRoom: ChatRoom?) : MessageNotifierViewModel() {
// Participant found, check if admin status needs to be updated
if (member.participant.isAdmin != participant.isAdmin) {
if (chatRoom.me?.isAdmin == true) {
Log.i("[Chat Room Group Info] Participant ${member.sipUri} will be admin? ${member.isAdmin}")
chatRoom.setParticipantAdminStatus(participant, member.participant.isAdmin)
Log.i(
"[Chat Room Group Info] Participant ${member.sipUri} will be admin? ${member.isAdmin}"
)
chatRoom.setParticipantAdminStatus(
participant,
member.participant.isAdmin
)
}
}
} else {
Log.i("[Chat Room Group Info] Participant ${member.sipUri} will be added to group")
Log.i(
"[Chat Room Group Info] Participant ${member.sipUri} will be added to group"
)
participantsToAdd.add(member.participant.address)
}
}
@ -223,7 +241,12 @@ class GroupInfoViewModel(val chatRoom: ChatRoom?) : MessageNotifierViewModel() {
for (participant in chatRoom.participants) {
list.add(
GroupInfoParticipantData(
GroupChatRoomMember(participant.address, participant.isAdmin, participant.securityLevel, canBeSetAdmin = true)
GroupChatRoomMember(
participant.address,
participant.isAdmin,
participant.securityLevel,
canBeSetAdmin = true
)
)
)
}

View file

@ -68,7 +68,9 @@ class ImdnViewModel(private val chatMessage: ChatMessage) : ViewModel() {
for (participant in chatMessage.getParticipantsByImdnState(ChatMessage.State.Displayed)) {
list.add(ImdnParticipantData(participant))
}
for (participant in chatMessage.getParticipantsByImdnState(ChatMessage.State.DeliveredToUser)) {
for (participant in chatMessage.getParticipantsByImdnState(
ChatMessage.State.DeliveredToUser
)) {
list.add(ImdnParticipantData(participant))
}
for (participant in chatMessage.getParticipantsByImdnState(ChatMessage.State.Delivered)) {

View file

@ -62,7 +62,8 @@ class RichEditText : AppCompatEditText {
private fun initReceiveContentListener() {
ViewCompat.setOnReceiveContentListener(
this, RichContentReceiver.MIME_TYPES,
this,
RichContentReceiver.MIME_TYPES,
RichContentReceiver { uri ->
Log.i("[Rich Edit Text] Received URI: $uri")
val activity = context as Activity

View file

@ -41,7 +41,10 @@ import org.linphone.utils.TimestampUtils
class ScheduledConferencesAdapter(
selectionVM: ListTopBarViewModel,
private val viewLifecycleOwner: LifecycleOwner
) : SelectionListAdapter<ScheduledConferenceData, RecyclerView.ViewHolder>(selectionVM, ConferenceInfoDiffCallback()),
) : SelectionListAdapter<ScheduledConferenceData, RecyclerView.ViewHolder>(
selectionVM,
ConferenceInfoDiffCallback()
),
HeaderAdapter {
val copyAddressToClipboardEvent: MutableLiveData<Event<String>> by lazy {
MutableLiveData<Event<String>>()
@ -62,7 +65,9 @@ class ScheduledConferencesAdapter(
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ScheduledConferencesAdapter.ViewHolder {
val binding: ConferenceScheduleCellBinding = DataBindingUtil.inflate(
LayoutInflater.from(parent.context),
R.layout.conference_schedule_cell, parent, false
R.layout.conference_schedule_cell,
parent,
false
)
return ViewHolder(binding)
}
@ -77,15 +82,22 @@ class ScheduledConferencesAdapter(
val previousPosition = position - 1
return if (previousPosition >= 0) {
val previousItem = getItem(previousPosition)
!TimestampUtils.isSameDay(previousItem.conferenceInfo.dateTime, conferenceInfo.conferenceInfo.dateTime)
} else true
!TimestampUtils.isSameDay(
previousItem.conferenceInfo.dateTime,
conferenceInfo.conferenceInfo.dateTime
)
} else {
true
}
}
override fun getHeaderViewForPosition(context: Context, position: Int): View {
val data = getItem(position)
val binding: ConferenceScheduleListHeaderBinding = DataBindingUtil.inflate(
LayoutInflater.from(context),
R.layout.conference_schedule_list_header, null, false
R.layout.conference_schedule_list_header,
null,
false
)
binding.title = formatDate(context, data.conferenceInfo.dateTime)
binding.executePendingBindings()
@ -143,7 +155,9 @@ class ScheduledConferencesAdapter(
setJoinConferenceClickListener {
val address = conferenceData.conferenceInfo.uri
if (address != null) {
joinConferenceEvent.value = Event(Pair(address.asStringUriOnly(), conferenceData.conferenceInfo.subject))
joinConferenceEvent.value = Event(
Pair(address.asStringUriOnly(), conferenceData.conferenceInfo.subject)
)
}
}

View file

@ -54,7 +54,12 @@ class ScheduledConferenceData(val conferenceInfo: ConferenceInfo, private val is
description.value = conferenceInfo.description
time.value = TimestampUtils.timeToString(conferenceInfo.dateTime)
date.value = TimestampUtils.toString(conferenceInfo.dateTime, onlyDate = true, shortDate = false, hideYear = false)
date.value = TimestampUtils.toString(
conferenceInfo.dateTime,
onlyDate = true,
shortDate = false,
hideYear = false
)
isConferenceCancelled.value = conferenceInfo.state == State.Cancelled
val minutes = conferenceInfo.duration
@ -72,13 +77,16 @@ class ScheduledConferenceData(val conferenceInfo: ConferenceInfo, private val is
canEdit.value = localAccount != null
val contact = coreContext.contactsManager.findContactByAddress(organizerAddress)
organizer.value = if (contact != null)
organizer.value = if (contact != null) {
contact.name
else
} else {
LinphoneUtils.getDisplayName(conferenceInfo.organizer)
}
} else {
canEdit.value = false
Log.e("[Scheduled Conference] No organizer SIP URI found for: ${conferenceInfo.uri?.asStringUriOnly()}")
Log.e(
"[Scheduled Conference] No organizer SIP URI found for: ${conferenceInfo.uri?.asStringUriOnly()}"
)
}
computeBackgroundResId()
@ -88,7 +96,9 @@ class ScheduledConferenceData(val conferenceInfo: ConferenceInfo, private val is
fun destroy() {}
fun delete() {
Log.w("[Scheduled Conference] Deleting conference info with URI: ${conferenceInfo.uri?.asStringUriOnly()}")
Log.w(
"[Scheduled Conference] Deleting conference info with URI: ${conferenceInfo.uri?.asStringUriOnly()}"
)
coreContext.core.deleteConferenceInformation(conferenceInfo)
}
@ -134,7 +144,13 @@ class ScheduledConferenceData(val conferenceInfo: ConferenceInfo, private val is
for (participant in conferenceInfo.participants) {
val contact = coreContext.contactsManager.findContactByAddress(participant)
val name = if (contact != null) contact.name else LinphoneUtils.getDisplayName(participant)
val name = if (contact != null) {
contact.name
} else {
LinphoneUtils.getDisplayName(
participant
)
}
val address = participant.asStringUriOnly()
participantsListShort += "$name, "
participantsListExpanded += "$name ($address)\n"

View file

@ -38,7 +38,9 @@ import org.linphone.core.tools.Log
import org.linphone.databinding.ConferenceSchedulingFragmentBinding
class ConferenceSchedulingFragment : GenericFragment<ConferenceSchedulingFragmentBinding>() {
private val viewModel: ConferenceSchedulingViewModel by navGraphViewModels(R.id.conference_scheduling_nav_graph)
private val viewModel: ConferenceSchedulingViewModel by navGraphViewModels(
R.id.conference_scheduling_nav_graph
)
override fun getLayoutId(): Int = R.layout.conference_scheduling_fragment
@ -53,7 +55,9 @@ class ConferenceSchedulingFragment : GenericFragment<ConferenceSchedulingFragmen
viewLifecycleOwner
) {
it.consume { participants ->
Log.i("[Conference Scheduling] Found participants (${participants.size}) to pre-populate for meeting schedule")
Log.i(
"[Conference Scheduling] Found participants (${participants.size}) to pre-populate for meeting schedule"
)
viewModel.prePopulateParticipantsList(participants, true)
}
}
@ -64,12 +68,18 @@ class ConferenceSchedulingFragment : GenericFragment<ConferenceSchedulingFragmen
it.consume { address ->
val conferenceAddress = Factory.instance().createAddress(address)
if (conferenceAddress != null) {
Log.i("[Conference Scheduling] Trying to edit conference info using address: $address")
val conferenceInfo = coreContext.core.findConferenceInformationFromUri(conferenceAddress)
Log.i(
"[Conference Scheduling] Trying to edit conference info using address: $address"
)
val conferenceInfo = coreContext.core.findConferenceInformationFromUri(
conferenceAddress
)
if (conferenceInfo != null) {
viewModel.populateFromConferenceInfo(conferenceInfo)
} else {
Log.e("[Conference Scheduling] Failed to find ConferenceInfo matching address: $address")
Log.e(
"[Conference Scheduling] Failed to find ConferenceInfo matching address: $address"
)
}
} else {
Log.e("[Conference Scheduling] Failed to parse conference address: $address")

View file

@ -37,7 +37,9 @@ import org.linphone.utils.AppUtils
import org.linphone.utils.PermissionHelper
class ConferenceSchedulingParticipantsListFragment : GenericFragment<ConferenceSchedulingParticipantsListFragmentBinding>() {
private val viewModel: ConferenceSchedulingViewModel by navGraphViewModels(R.id.conference_scheduling_nav_graph)
private val viewModel: ConferenceSchedulingViewModel by navGraphViewModels(
R.id.conference_scheduling_nav_graph
)
private lateinit var adapter: ContactsSelectionAdapter
override fun getLayoutId(): Int = R.layout.conference_scheduling_participants_list_fragment
@ -57,7 +59,9 @@ class ConferenceSchedulingParticipantsListFragment : GenericFragment<ConferenceS
binding.contactsList.layoutManager = layoutManager
// Divider between items
binding.contactsList.addItemDecoration(AppUtils.getDividerDecoration(requireContext(), layoutManager))
binding.contactsList.addItemDecoration(
AppUtils.getDividerDecoration(requireContext(), layoutManager)
)
binding.setNextClickListener {
navigateToSummary()

View file

@ -31,7 +31,9 @@ import org.linphone.activities.navigateToScheduledConferences
import org.linphone.databinding.ConferenceSchedulingSummaryFragmentBinding
class ConferenceSchedulingSummaryFragment : GenericFragment<ConferenceSchedulingSummaryFragmentBinding>() {
private val viewModel: ConferenceSchedulingViewModel by navGraphViewModels(R.id.conference_scheduling_nav_graph)
private val viewModel: ConferenceSchedulingViewModel by navGraphViewModels(
R.id.conference_scheduling_nav_graph
)
override fun getLayoutId(): Int = R.layout.conference_scheduling_summary_fragment
@ -47,7 +49,9 @@ class ConferenceSchedulingSummaryFragment : GenericFragment<ConferenceScheduling
) {
it.consume {
if (viewModel.scheduleForLater.value == true) {
(requireActivity() as MainActivity).showSnackBar(R.string.conference_schedule_info_created)
(requireActivity() as MainActivity).showSnackBar(
R.string.conference_schedule_info_created
)
navigateToScheduledConferences()
} else {
navigateToDialer()

View file

@ -63,10 +63,14 @@ class ConferenceWaitingRoomFragment : GenericFragment<ConferenceWaitingRoomFragm
call.remoteAddress.asStringUriOnly() == conferenceUri
}
if (callToCancel != null) {
Log.i("[Conference Waiting Room] Call to conference server with URI [$conferenceUri] was started, terminate it")
Log.i(
"[Conference Waiting Room] Call to conference server with URI [$conferenceUri] was started, terminate it"
)
callToCancel.terminate()
} else {
Log.w("[Conference Waiting Room] Call to conference server with URI [$conferenceUri] wasn't found!")
Log.w(
"[Conference Waiting Room] Call to conference server with URI [$conferenceUri] wasn't found!"
)
}
}
goBack()
@ -81,13 +85,19 @@ class ConferenceWaitingRoomFragment : GenericFragment<ConferenceWaitingRoomFragm
if (conferenceUri != null) {
val conferenceAddress = coreContext.core.interpretUrl(conferenceUri, false)
if (conferenceAddress != null) {
Log.i("[Conference Waiting Room] Calling conference SIP URI: ${conferenceAddress.asStringUriOnly()}")
Log.i(
"[Conference Waiting Room] Calling conference SIP URI: ${conferenceAddress.asStringUriOnly()}"
)
coreContext.startCall(conferenceAddress, callParams)
} else {
Log.e("[Conference Waiting Room] Failed to parse conference SIP URI: $conferenceUri")
Log.e(
"[Conference Waiting Room] Failed to parse conference SIP URI: $conferenceUri"
)
}
} else {
Log.e("[Conference Waiting Room] Failed to find conference SIP URI in arguments")
Log.e(
"[Conference Waiting Room] Failed to find conference SIP URI in arguments"
)
}
}
}
@ -191,7 +201,9 @@ class ConferenceWaitingRoomFragment : GenericFragment<ConferenceWaitingRoomFragm
viewModel.enableVideo()
}
Compatibility.BLUETOOTH_CONNECT -> if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
Log.i("[Conference Waiting Room] BLUETOOTH_CONNECT permission has been granted")
Log.i(
"[Conference Waiting Room] BLUETOOTH_CONNECT permission has been granted"
)
}
}
}

View file

@ -82,7 +82,9 @@ class ScheduledConferencesFragment : MasterFragment<ConferencesScheduledFragment
override fun onRightToLeftSwipe(viewHolder: RecyclerView.ViewHolder) {
val index = viewHolder.bindingAdapterPosition
if (index < 0 || index >= adapter.currentList.size) {
Log.e("[Scheduled Conferences] Index is out of bound, can't delete conference info")
Log.e(
"[Scheduled Conferences] Index is out of bound, can't delete conference info"
)
} else {
val deletedConfInfo = adapter.currentList[index]
showConfInfoDeleteConfirmationDialog(deletedConfInfo, index)
@ -111,7 +113,9 @@ class ScheduledConferencesFragment : MasterFragment<ConferencesScheduledFragment
val clip = ClipData.newPlainText("Conference address", address)
clipboard.setPrimaryClip(clip)
(activity as MainActivity).showSnackBar(R.string.conference_schedule_address_copied_to_clipboard)
(activity as MainActivity).showSnackBar(
R.string.conference_schedule_address_copied_to_clipboard
)
}
}

View file

@ -82,7 +82,9 @@ class ConferenceSchedulingViewModel : ContactsSelectionViewModel() {
Log.i("[Conference Creation] Conference scheduler state is $state")
if (state == ConferenceScheduler.State.Ready) {
val conferenceAddress = conferenceScheduler.info?.uri
Log.i("[Conference Creation] Conference info created, address will be ${conferenceAddress?.asStringUriOnly()}")
Log.i(
"[Conference Creation] Conference info created, address will be ${conferenceAddress?.asStringUriOnly()}"
)
conferenceAddress ?: return
address.value = conferenceAddress!!
@ -110,11 +112,17 @@ class ConferenceSchedulingViewModel : ContactsSelectionViewModel() {
if (failedInvitations?.isNotEmpty() == true) {
for (address in failedInvitations) {
Log.e("[Conference Creation] Conference information wasn't sent to participant ${address.asStringUriOnly()}")
Log.e(
"[Conference Creation] Conference information wasn't sent to participant ${address.asStringUriOnly()}"
)
}
onMessageToNotifyEvent.value = Event(R.string.conference_schedule_info_not_sent_to_participant)
onMessageToNotifyEvent.value = Event(
R.string.conference_schedule_info_not_sent_to_participant
)
} else {
Log.i("[Conference Creation] Conference information successfully sent to all participants")
Log.i(
"[Conference Creation] Conference information successfully sent to all participants"
)
}
val conferenceAddress = conferenceScheduler.info?.uri
@ -313,7 +321,9 @@ class ConferenceSchedulingViewModel : ContactsSelectionViewModel() {
}
private fun getConferenceStartTimestamp(): Long {
val calendar = Calendar.getInstance(TimeZone.getTimeZone(timeZone.value?.id ?: TimeZone.getDefault().id))
val calendar = Calendar.getInstance(
TimeZone.getTimeZone(timeZone.value?.id ?: TimeZone.getDefault().id)
)
calendar.timeInMillis = dateTimestamp
calendar.set(Calendar.HOUR_OF_DAY, hour)
calendar.set(Calendar.MINUTE, minutes)

View file

@ -125,7 +125,9 @@ class ConferenceWaitingRoomViewModel : MessageNotifierViewModel() {
leaveWaitingRoomEvent.value = Event(true)
}
Call.State.Error -> {
Log.w("[Conference Waiting Room] Call has failed, leaving waiting room fragment")
Log.w(
"[Conference Waiting Room] Call has failed, leaving waiting room fragment"
)
leaveWaitingRoomEvent.value = Event(true)
}
else -> {}
@ -138,7 +140,9 @@ class ConferenceWaitingRoomViewModel : MessageNotifierViewModel() {
state: Conference.State?
) {
if (state == Conference.State.Created) {
Log.i("[Conference Waiting Room] Conference has been created, leaving waiting room fragment")
Log.i(
"[Conference Waiting Room] Conference has been created, leaving waiting room fragment"
)
leaveWaitingRoomEvent.value = Event(true)
}
}
@ -156,8 +160,12 @@ class ConferenceWaitingRoomViewModel : MessageNotifierViewModel() {
val core = coreContext.core
core.addListener(listener)
audioRoutesMenuTranslateY.value = AppUtils.getDimension(R.dimen.voip_audio_routes_menu_translate_y)
conferenceLayoutMenuTranslateY.value = AppUtils.getDimension(R.dimen.voip_audio_routes_menu_translate_y)
audioRoutesMenuTranslateY.value = AppUtils.getDimension(
R.dimen.voip_audio_routes_menu_translate_y
)
conferenceLayoutMenuTranslateY.value = AppUtils.getDimension(
R.dimen.voip_audio_routes_menu_translate_y
)
val reachable = core.isNetworkReachable
networkReachable.value = reachable
@ -166,7 +174,9 @@ class ConferenceWaitingRoomViewModel : MessageNotifierViewModel() {
}
callParams.isMicEnabled = PermissionHelper.get().hasRecordAudioPermission() && coreContext.core.isMicEnabled
Log.i("[Conference Waiting Room] Microphone will be ${if (callParams.isMicEnabled) "enabled" else "muted"}")
Log.i(
"[Conference Waiting Room] Microphone will be ${if (callParams.isMicEnabled) "enabled" else "muted"}"
)
updateMicState()
callParams.isVideoEnabled = isVideoAvailableInCore()
@ -175,7 +185,9 @@ class ConferenceWaitingRoomViewModel : MessageNotifierViewModel() {
isLowBandwidth.value = false
if (LinphoneUtils.checkIfNetworkHasLowBandwidth(coreContext.context)) {
Log.w("[Conference Waiting Room] Enabling low bandwidth mode, forcing audio only layout!")
Log.w(
"[Conference Waiting Room] Enabling low bandwidth mode, forcing audio only layout!"
)
callParams.isLowBandwidthEnabled = true
callParams.isVideoEnabled = false
callParams.videoDirection = MediaDirection.Inactive
@ -224,7 +236,9 @@ class ConferenceWaitingRoomViewModel : MessageNotifierViewModel() {
}
callParams.isMicEnabled = !callParams.isMicEnabled
Log.i("[Conference Waiting Room] Microphone will be ${if (callParams.isMicEnabled) "enabled" else "muted"}")
Log.i(
"[Conference Waiting Room] Microphone will be ${if (callParams.isMicEnabled) "enabled" else "muted"}"
)
updateMicState()
}
@ -254,10 +268,14 @@ class ConferenceWaitingRoomViewModel : MessageNotifierViewModel() {
fun setBluetoothAudioRoute() {
Log.i("[Conference Waiting Room] Set default output audio device to Bluetooth")
callParams.outputAudioDevice = coreContext.core.audioDevices.find {
it.type == AudioDevice.Type.Bluetooth && it.hasCapability(AudioDevice.Capabilities.CapabilityPlay)
it.type == AudioDevice.Type.Bluetooth && it.hasCapability(
AudioDevice.Capabilities.CapabilityPlay
)
}
callParams.inputAudioDevice = coreContext.core.audioDevices.find {
it.type == AudioDevice.Type.Bluetooth && it.hasCapability(AudioDevice.Capabilities.CapabilityRecord)
it.type == AudioDevice.Type.Bluetooth && it.hasCapability(
AudioDevice.Capabilities.CapabilityRecord
)
}
updateAudioRouteState()
@ -270,10 +288,14 @@ class ConferenceWaitingRoomViewModel : MessageNotifierViewModel() {
fun setSpeakerAudioRoute() {
Log.i("[Conference Waiting Room] Set default output audio device to Speaker")
callParams.outputAudioDevice = coreContext.core.audioDevices.find {
it.type == AudioDevice.Type.Speaker && it.hasCapability(AudioDevice.Capabilities.CapabilityPlay)
it.type == AudioDevice.Type.Speaker && it.hasCapability(
AudioDevice.Capabilities.CapabilityPlay
)
}
callParams.inputAudioDevice = coreContext.core.audioDevices.find {
it.type == AudioDevice.Type.Microphone && it.hasCapability(AudioDevice.Capabilities.CapabilityRecord)
it.type == AudioDevice.Type.Microphone && it.hasCapability(
AudioDevice.Capabilities.CapabilityRecord
)
}
updateAudioRouteState()
@ -286,10 +308,14 @@ class ConferenceWaitingRoomViewModel : MessageNotifierViewModel() {
fun setEarpieceAudioRoute() {
Log.i("[Conference Waiting Room] Set default output audio device to Earpiece")
callParams.outputAudioDevice = coreContext.core.audioDevices.find {
it.type == AudioDevice.Type.Earpiece && it.hasCapability(AudioDevice.Capabilities.CapabilityPlay)
it.type == AudioDevice.Type.Earpiece && it.hasCapability(
AudioDevice.Capabilities.CapabilityPlay
)
}
callParams.inputAudioDevice = coreContext.core.audioDevices.find {
it.type == AudioDevice.Type.Microphone && it.hasCapability(AudioDevice.Capabilities.CapabilityRecord)
it.type == AudioDevice.Type.Microphone && it.hasCapability(
AudioDevice.Capabilities.CapabilityRecord
)
}
updateAudioRouteState()
@ -376,7 +402,9 @@ class ConferenceWaitingRoomViewModel : MessageNotifierViewModel() {
if (!bluetoothDeviceAvailable) {
audioRoutesSelected.value = false
Log.w("[Conference Waiting Room] Bluetooth device no longer available, switching back to default microphone & earpiece/speaker")
Log.w(
"[Conference Waiting Room] Bluetooth device no longer available, switching back to default microphone & earpiece/speaker"
)
if (isBluetoothHeadsetSelected.value == true) {
for (audioDevice in coreContext.core.audioDevices) {
if (isVideoEnabled.value == true) {
@ -418,7 +446,9 @@ class ConferenceWaitingRoomViewModel : MessageNotifierViewModel() {
private fun updateVideoState() {
isVideoAvailable.value = callParams.isVideoEnabled
isVideoEnabled.value = callParams.isVideoEnabled && callParams.videoDirection == MediaDirection.SendRecv
Log.i("[Conference Waiting Room] Video will be ${if (callParams.isVideoEnabled) "enabled" else "disabled"} with direction ${callParams.videoDirection}")
Log.i(
"[Conference Waiting Room] Video will be ${if (callParams.isVideoEnabled) "enabled" else "disabled"} with direction ${callParams.videoDirection}"
)
isSwitchCameraAvailable.value = callParams.isVideoEnabled && coreContext.showSwitchCameraButton()
coreContext.core.isVideoPreviewEnabled = isVideoEnabled.value == true

View file

@ -52,7 +52,9 @@ class ScheduledConferencesViewModel : ViewModel() {
) {
Log.i("[Scheduled Conferences] Conference scheduler state is $state")
if (state == ConferenceScheduler.State.Ready) {
Log.i("[Scheduled Conferences] Conference ${conferenceScheduler.info?.subject} cancelled")
Log.i(
"[Scheduled Conferences] Conference ${conferenceScheduler.info?.subject} cancelled"
)
val chatRoomParams = LinphoneUtils.getConferenceInvitationsChatRoomParams()
conferenceScheduler.sendInvitations(chatRoomParams) // Send cancel ICS
}
@ -64,10 +66,14 @@ class ScheduledConferencesViewModel : ViewModel() {
) {
if (failedInvitations?.isNotEmpty() == true) {
for (address in failedInvitations) {
Log.e("[Scheduled Conferences] Conference cancelled ICS wasn't sent to participant ${address.asStringUriOnly()}")
Log.e(
"[Scheduled Conferences] Conference cancelled ICS wasn't sent to participant ${address.asStringUriOnly()}"
)
}
} else {
Log.i("[Scheduled Conferences] Conference cancelled ICS successfully sent to all participants")
Log.i(
"[Scheduled Conferences] Conference cancelled ICS successfully sent to all participants"
)
}
}
}
@ -139,7 +145,9 @@ class ScheduledConferencesViewModel : ViewModel() {
}
} else {
val oneHourAgo = now - 7200 // Show all conferences from 2 hours ago and forward
for (conferenceInfo in coreContext.core.getConferenceInformationListAfterTime(oneHourAgo)) {
for (conferenceInfo in coreContext.core.getConferenceInformationListAfterTime(
oneHourAgo
)) {
if (conferenceInfo.duration == 0) continue // This isn't a scheduled conference, don't display it
val data = ScheduledConferenceData(conferenceInfo, false)
conferencesList.add(data)

View file

@ -42,7 +42,11 @@ import org.linphone.utils.HeaderAdapter
class ContactsListAdapter(
selectionVM: ListTopBarViewModel,
private val viewLifecycleOwner: LifecycleOwner
) : SelectionListAdapter<ContactViewModel, RecyclerView.ViewHolder>(selectionVM, ContactDiffCallback()), HeaderAdapter {
) : SelectionListAdapter<ContactViewModel, RecyclerView.ViewHolder>(
selectionVM,
ContactDiffCallback()
),
HeaderAdapter {
val selectedContactEvent: MutableLiveData<Event<Friend>> by lazy {
MutableLiveData<Event<Friend>>()
}
@ -50,7 +54,9 @@ class ContactsListAdapter(
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val binding: ContactListCellBinding = DataBindingUtil.inflate(
LayoutInflater.from(parent.context),
R.layout.contact_list_cell, parent, false
R.layout.contact_list_cell,
parent,
false
)
return ViewHolder(binding)
}
@ -108,7 +114,9 @@ class ContactsListAdapter(
return if (previousPosition >= 0) {
val previousItemFirstLetter = getItem(previousPosition).fullName.firstOrNull().toString()
!firstLetter.equals(previousItemFirstLetter, ignoreCase = true)
} else true
} else {
true
}
}
override fun getHeaderViewForPosition(context: Context, position: Int): View {
@ -116,7 +124,9 @@ class ContactsListAdapter(
val firstLetter = AppUtils.getInitials(contact.fullName, 1)
val binding: GenericListHeaderBinding = DataBindingUtil.inflate(
LayoutInflater.from(context),
R.layout.generic_list_header, null, false
R.layout.generic_list_header,
null,
false
)
binding.title = firstLetter
binding.executePendingBindings()

View file

@ -19,7 +19,11 @@ class SyncAccountAdapter : BaseAdapter() {
}
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val view: View = convertView ?: LayoutInflater.from(parent.context).inflate(R.layout.contact_sync_account_picker_cell, parent, false)
val view: View = convertView ?: LayoutInflater.from(parent.context).inflate(
R.layout.contact_sync_account_picker_cell,
parent,
false
)
val account = getItem(position)
val icon = view.findViewById<ImageView>(R.id.account_icon)

View file

@ -88,7 +88,9 @@ class ContactEditorData(val friend: Friend?) : ContactDataInterface {
Log.w("[Contact Editor] vCard first & last name not filled-in yet, doing it now")
fetchFirstAndLastNames(refKey)
} else {
Log.e("[Contact Editor] vCard first & last name not available as contact doesn't have a native ID")
Log.e(
"[Contact Editor] vCard first & last name not available as contact doesn't have a native ID"
)
}
} else {
firstName.value = vCard?.givenName
@ -246,10 +248,16 @@ class ContactEditorData(val friend: Friend?) : ContactDataInterface {
while (cursor != null && cursor.moveToNext()) {
val linphoneMime = AppUtils.getString(R.string.linphone_address_mime_type)
val mime: String? =
cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.Data.MIMETYPE))
cursor.getString(
cursor.getColumnIndexOrThrow(ContactsContract.Data.MIMETYPE)
)
if (mime == ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE) {
val data1: String? =
cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.NUMBER))
cursor.getString(
cursor.getColumnIndexOrThrow(
ContactsContract.CommonDataKinds.Phone.NUMBER
)
)
if (data1 != null) {
phoneNumbers.add(NumberOrAddressEditorData(data1, false))
}
@ -258,7 +266,11 @@ class ContactEditorData(val friend: Friend?) : ContactDataInterface {
mime == linphoneMime
) {
val data1: String? =
cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS))
cursor.getString(
cursor.getColumnIndexOrThrow(
ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS
)
)
if (data1 != null) {
sipAddresses.add(NumberOrAddressEditorData(data1, true))
}
@ -274,7 +286,9 @@ class ContactEditorData(val friend: Friend?) : ContactDataInterface {
}
if (!fetched) {
Log.w("[Contact Editor] Fall-backing to friend info (might be inaccurate and thus edition/removal might fail)")
Log.w(
"[Contact Editor] Fall-backing to friend info (might be inaccurate and thus edition/removal might fail)"
)
for (number in friend?.phoneNumbers.orEmpty()) {
phoneNumbers.add(NumberOrAddressEditorData(number, false))
}
@ -310,17 +324,27 @@ class ContactEditorData(val friend: Friend?) : ContactDataInterface {
)
while (cursor != null && cursor.moveToNext()) {
val mime: String? = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.Data.MIMETYPE))
val mime: String? = cursor.getString(
cursor.getColumnIndexOrThrow(ContactsContract.Data.MIMETYPE)
)
if (mime == ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE) {
val givenName: String? =
cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME))
cursor.getString(
cursor.getColumnIndexOrThrow(
ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME
)
)
if (!givenName.isNullOrEmpty()) {
friend?.vcard?.givenName = givenName
firstName.value = givenName!!
}
val familyName: String? =
cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME))
cursor.getString(
cursor.getColumnIndexOrThrow(
ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME
)
)
if (!familyName.isNullOrEmpty()) {
friend?.vcard?.familyName = familyName
lastName.value = familyName!!

View file

@ -116,7 +116,9 @@ class ContactEditorFragment : GenericFragment<ContactEditorFragmentBinding>(), S
Log.i("[Contact Editor] WRITE_CONTACTS permission granted")
} else {
Log.w("[Contact Editor] WRITE_CONTACTS permission denied")
(activity as MainActivity).showSnackBar(R.string.contact_editor_write_permission_denied)
(activity as MainActivity).showSnackBar(
R.string.contact_editor_write_permission_denied
)
goBack()
}
}
@ -126,7 +128,10 @@ class ContactEditorFragment : GenericFragment<ContactEditorFragmentBinding>(), S
override fun onActivityResult(requestCode: Int, resultCode: Int, intent: Intent?) {
if (resultCode == Activity.RESULT_OK) {
lifecycleScope.launch {
val contactImageFilePath = FileUtils.getFilePathFromPickerIntent(intent, temporaryPicturePath)
val contactImageFilePath = FileUtils.getFilePathFromPickerIntent(
intent,
temporaryPicturePath
)
if (contactImageFilePath != null) {
data.setPictureFromPath(contactImageFilePath)
}
@ -171,7 +176,10 @@ class ContactEditorFragment : GenericFragment<ContactEditorFragmentBinding>(), S
val chooserIntent =
Intent.createChooser(galleryIntent, getString(R.string.chat_message_pick_file_dialog))
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, cameraIntents.toArray(arrayOf<Parcelable>()))
chooserIntent.putExtra(
Intent.EXTRA_INITIAL_INTENTS,
cameraIntents.toArray(arrayOf<Parcelable>())
)
startActivityForResult(chooserIntent, 0)
}

View file

@ -88,7 +88,9 @@ class DetailContactFragment : GenericFragment<ContactDetailFragmentBinding>() {
) {
it.consume { address ->
if (coreContext.core.callsNb > 0) {
Log.i("[Contact] Starting dialer with pre-filled URI ${address.asStringUriOnly()}, is transfer? ${sharedViewModel.pendingCallTransfer}")
Log.i(
"[Contact] Starting dialer with pre-filled URI ${address.asStringUriOnly()}, is transfer? ${sharedViewModel.pendingCallTransfer}"
)
sharedViewModel.updateContactsAnimationsBasedOnDestination.value =
Event(R.id.dialerFragment)
sharedViewModel.updateDialerAnimationsBasedOnDestination.value =
@ -186,7 +188,9 @@ class DetailContactFragment : GenericFragment<ContactDetailFragmentBinding>() {
val smsIntent = Intent(Intent.ACTION_SENDTO)
smsIntent.putExtra("address", number)
smsIntent.data = Uri.parse("smsto:$number")
val text = getString(R.string.contact_send_sms_invite_text).format(getString(R.string.contact_send_sms_invite_download_link))
val text = getString(R.string.contact_send_sms_invite_text).format(
getString(R.string.contact_send_sms_invite_download_link)
)
smsIntent.putExtra("sms_body", text)
startActivity(smsIntent)
}

View file

@ -115,7 +115,9 @@ class MasterContactsFragment : MasterFragment<ContactMasterFragmentBinding, Cont
val navHostFragment =
childFragmentManager.findFragmentById(R.id.contacts_nav_container) as NavHostFragment
if (navHostFragment.navController.currentDestination?.id == R.id.emptyContactFragment) {
Log.i("[Contacts] Foldable device has been folded, closing side pane with empty fragment")
Log.i(
"[Contacts] Foldable device has been folded, closing side pane with empty fragment"
)
binding.slidingPane.closePane()
}
}
@ -184,7 +186,9 @@ class MasterContactsFragment : MasterFragment<ContactMasterFragmentBinding, Cont
if (!binding.slidingPane.isSlideable &&
deletedContact == sharedViewModel.selectedContact.value
) {
Log.i("[Contacts] Currently displayed contact has been deleted, removing detail fragment")
Log.i(
"[Contacts] Currently displayed contact has been deleted, removing detail fragment"
)
clearDisplayedContact()
}
}
@ -204,7 +208,9 @@ class MasterContactsFragment : MasterFragment<ContactMasterFragmentBinding, Cont
}
// Divider between items
binding.contactsList.addItemDecoration(AppUtils.getDividerDecoration(requireContext(), layoutManager))
binding.contactsList.addItemDecoration(
AppUtils.getDividerDecoration(requireContext(), layoutManager)
)
// Displays the first letter header
val headerItemDecoration = RecyclerViewHeaderDecoration(requireContext(), adapter)
@ -255,7 +261,9 @@ class MasterContactsFragment : MasterFragment<ContactMasterFragmentBinding, Cont
viewLifecycleOwner
) {
it.consume {
(requireActivity() as SnackBarActivity).showSnackBar(R.string.contacts_ldap_query_more_results_available)
(requireActivity() as SnackBarActivity).showSnackBar(
R.string.contacts_ldap_query_more_results_available
)
}
}
@ -298,24 +306,32 @@ class MasterContactsFragment : MasterFragment<ContactMasterFragmentBinding, Cont
Log.i("[Contacts] Found matching contact [${contact.name}]")
adapter.selectedContactEvent.value = Event(contact)
} else {
Log.w("[Contacts] Matching contact not found yet, waiting for contacts updated callback")
Log.w(
"[Contacts] Matching contact not found yet, waiting for contacts updated callback"
)
contactIdToDisplay = id
}
} else if (sipUri != null) {
Log.i("[Contacts] Found sipUri parameter in arguments [$sipUri]")
sipUriToAdd = sipUri
(activity as MainActivity).showSnackBar(R.string.contact_choose_existing_or_new_to_add_number)
(activity as MainActivity).showSnackBar(
R.string.contact_choose_existing_or_new_to_add_number
)
editOnClick = true
} else if (addressString != null) {
val address = Factory.instance().createAddress(addressString)
if (address != null) {
Log.i("[Contacts] Found friend SIP address parameter in arguments [${address.asStringUriOnly()}]")
Log.i(
"[Contacts] Found friend SIP address parameter in arguments [${address.asStringUriOnly()}]"
)
val contact = coreContext.contactsManager.findContactByAddress(address)
if (contact != null) {
Log.i("[Contacts] Found matching contact $contact")
adapter.selectedContactEvent.value = Event(contact)
} else {
Log.w("[Contacts] No matching contact found for SIP address [${address.asStringUriOnly()}]")
Log.w(
"[Contacts] No matching contact found for SIP address [${address.asStringUriOnly()}]"
)
}
}
}
@ -344,7 +360,9 @@ class MasterContactsFragment : MasterFragment<ContactMasterFragmentBinding, Cont
listViewModel.deleteContacts(list)
if (!binding.slidingPane.isSlideable && closeSlidingPane) {
Log.i("[Contacts] Currently displayed contact has been deleted, removing detail fragment")
Log.i(
"[Contacts] Currently displayed contact has been deleted, removing detail fragment"
)
clearDisplayedContact()
}
}

View file

@ -215,10 +215,20 @@ class ContactViewModel(friend: Friend, async: Boolean = false) : MessageNotifier
val presenceModel = friend.getPresenceModelForUriOrTel(value)
val hasPresence = presenceModel?.basicStatus == PresenceBasicStatus.Open
val isMe = coreContext.core.defaultAccount?.params?.identityAddress?.weakEqual(address) ?: false
val hasLimeCapability = corePreferences.allowEndToEndEncryptedChatWithoutPresence || (friend.getPresenceModelForUriOrTel(value)?.hasCapability(FriendCapability.LimeX3Dh) ?: false)
val hasLimeCapability = corePreferences.allowEndToEndEncryptedChatWithoutPresence || (
friend.getPresenceModelForUriOrTel(
value
)?.hasCapability(FriendCapability.LimeX3Dh) ?: false
)
val secureChatAllowed = LinphoneUtils.isEndToEndEncryptedChatAvailable() && !isMe && hasLimeCapability
val displayValue = if (coreContext.core.defaultAccount?.params?.domain == address.domain) (address.username ?: value) else value
val noa = ContactNumberOrAddressData(address, hasPresence, displayValue, showSecureChat = secureChatAllowed, listener = listener)
val noa = ContactNumberOrAddressData(
address,
hasPresence,
displayValue,
showSecureChat = secureChatAllowed,
listener = listener
)
list.add(noa)
}
@ -229,11 +239,32 @@ class ContactViewModel(friend: Friend, async: Boolean = false) : MessageNotifier
val contactAddress = presenceModel?.contact ?: number
val address = coreContext.core.interpretUrl(contactAddress, true)
address?.displayName = displayName.value.orEmpty()
val isMe = if (address != null) coreContext.core.defaultAccount?.params?.identityAddress?.weakEqual(address) ?: false else false
val hasLimeCapability = corePreferences.allowEndToEndEncryptedChatWithoutPresence || (friend.getPresenceModelForUriOrTel(number)?.hasCapability(FriendCapability.LimeX3Dh) ?: false)
val isMe = if (address != null) {
coreContext.core.defaultAccount?.params?.identityAddress?.weakEqual(
address
) ?: false
} else {
false
}
val hasLimeCapability = corePreferences.allowEndToEndEncryptedChatWithoutPresence || (
friend.getPresenceModelForUriOrTel(
number
)?.hasCapability(FriendCapability.LimeX3Dh) ?: false
)
val secureChatAllowed = LinphoneUtils.isEndToEndEncryptedChatAvailable() && !isMe && hasLimeCapability
val label = PhoneNumberUtils.vcardParamStringToAddressBookLabel(coreContext.context.resources, phoneNumber.label ?: "")
val noa = ContactNumberOrAddressData(address, hasPresence, number, isSip = false, showSecureChat = secureChatAllowed, typeLabel = label, listener = listener)
val label = PhoneNumberUtils.vcardParamStringToAddressBookLabel(
coreContext.context.resources,
phoneNumber.label ?: ""
)
val noa = ContactNumberOrAddressData(
address,
hasPresence,
number,
isSip = false,
showSecureChat = secureChatAllowed,
typeLabel = label,
listener = listener
)
list.add(noa)
}
numbersAndAddresses.postValue(list)

View file

@ -123,8 +123,15 @@ class ContactsListViewModel : ViewModel() {
val aggregation = MagicSearchAggregation.Friend
searchResultsPending = true
fastFetchJob?.cancel()
Log.i("[Contacts] Asking Magic search for contacts matching filter [$filterValue], domain [$domain] and in sources [$filter]")
coreContext.contactsManager.magicSearch.getContactsListAsync(filterValue, domain, filter, aggregation)
Log.i(
"[Contacts] Asking Magic search for contacts matching filter [$filterValue], domain [$domain] and in sources [$filter]"
)
coreContext.contactsManager.magicSearch.getContactsListAsync(
filterValue,
domain,
filter,
aggregation
)
val spinnerDelay = corePreferences.delayBeforeShowingContactsSearchSpinner.toLong()
fastFetchJob = viewModelScope.launch {
@ -154,7 +161,9 @@ class ContactsListViewModel : ViewModel() {
ContactViewModel(friend, true)
} else {
Log.w("[Contacts] SearchResult [$result] has no Friend!")
val fakeFriend = coreContext.contactsManager.createFriendFromSearchResult(result)
val fakeFriend = coreContext.contactsManager.createFriendFromSearchResult(
result
)
ContactViewModel(fakeFriend, true)
}

View file

@ -93,13 +93,19 @@ class DialerFragment : SecureFragment<DialerFragmentBinding>() {
}
binding.setNewContactClickListener {
sharedViewModel.updateDialerAnimationsBasedOnDestination.value = Event(R.id.masterContactsFragment)
sharedViewModel.updateContactsAnimationsBasedOnDestination.value = Event(R.id.dialerFragment)
sharedViewModel.updateDialerAnimationsBasedOnDestination.value = Event(
R.id.masterContactsFragment
)
sharedViewModel.updateContactsAnimationsBasedOnDestination.value = Event(
R.id.dialerFragment
)
navigateToContacts(viewModel.enteredUri.value)
}
binding.setNewConferenceClickListener {
sharedViewModel.updateDialerAnimationsBasedOnDestination.value = Event(R.id.conferenceSchedulingFragment)
sharedViewModel.updateDialerAnimationsBasedOnDestination.value = Event(
R.id.conferenceSchedulingFragment
)
navigateToConferenceScheduling()
}
@ -157,7 +163,9 @@ 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")
Log.w(
"[Dialer] First start detected, wait for assistant to be finished to check for update & request permissions"
)
return
}
@ -168,10 +176,14 @@ class DialerFragment : SecureFragment<DialerFragmentBinding>() {
if (corePreferences.skipDialerForNewCallAndTransfer) {
if (sharedViewModel.pendingCallTransfer) {
Log.i("[Dialer] We were asked to skip dialer so starting new call to [$address] now")
Log.i(
"[Dialer] We were asked to skip dialer so starting new call to [$address] now"
)
viewModel.transferCallTo(address)
} else {
Log.i("[Dialer] We were asked to skip dialer so starting transfer to [$address] now")
Log.i(
"[Dialer] We were asked to skip dialer so starting transfer to [$address] now"
)
viewModel.directCall(address)
}
} else if (corePreferences.callRightAway && !skipAutoCall) {
@ -289,7 +301,9 @@ class DialerFragment : SecureFragment<DialerFragmentBinding>() {
if (Compatibility.hasTelecomManagerFeature(requireContext())) {
TelecomHelper.create(requireContext())
} else {
Log.e("[Dialer] Telecom Helper can't be created, device doesn't support connection service!")
Log.e(
"[Dialer] Telecom Helper can't be created, device doesn't support connection service!"
)
return
}
} else {

View file

@ -112,7 +112,9 @@ class DialerViewModel : LogsUploadViewModel() {
if (reachable && address.isNotEmpty()) {
val now = System.currentTimeMillis()
if (now - timeAtWitchWeTriedToCall > 1000) {
Log.e("[Dialer] More than 1 second has passed waiting for network, abort auto call to $address")
Log.e(
"[Dialer] More than 1 second has passed waiting for network, abort auto call to $address"
)
enteredUri.value = address
} else {
Log.i("[Dialer] Network is available, continue auto call to $address")
@ -198,7 +200,9 @@ class DialerViewModel : LogsUploadViewModel() {
if (coreContext.core.isNetworkReachable) {
coreContext.startCall(to)
} else {
Log.w("[Dialer] Network isnt't reachable at the time, wait for network to start call (happens mainly when app is cold started)")
Log.w(
"[Dialer] Network isnt't reachable at the time, wait for network to start call (happens mainly when app is cold started)"
)
timeAtWitchWeTriedToCall = System.currentTimeMillis()
addressWaitingNetworkToBeCalled = to
}

View file

@ -29,7 +29,9 @@ import org.linphone.activities.main.files.viewmodels.PdfFileViewModel
class PdfPagesListAdapter(private val pdfViewModel: PdfFileViewModel) : RecyclerView.Adapter<PdfPagesListAdapter.PdfPageViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PdfPageViewHolder {
val view = LayoutInflater.from(parent.context).inflate(
R.layout.file_pdf_viewer_cell, parent, false
R.layout.file_pdf_viewer_cell,
parent,
false
)
return PdfPageViewHolder(view)
}

View file

@ -98,10 +98,14 @@ class TopBarFragment : GenericFragment<FileViewerTopBarFragmentBinding>() {
Compatibility.addImageToMediaStore(requireContext(), content)
}
if (export.await()) {
Log.i("[File Viewer] Successfully exported image [${content.name}] to Media Store: ${content.userData}")
Log.i(
"[File Viewer] Successfully exported image [${content.name}] to Media Store: ${content.userData}"
)
mediaStoreFilePath = content.userData.toString()
} else {
Log.e("[File Viewer] Something went wrong while copying file to Media Store...")
Log.e(
"[File Viewer] Something went wrong while copying file to Media Store..."
)
}
}
FileUtils.isMimeVideo(mime) -> {
@ -109,10 +113,14 @@ class TopBarFragment : GenericFragment<FileViewerTopBarFragmentBinding>() {
Compatibility.addVideoToMediaStore(requireContext(), content)
}
if (export.await()) {
Log.i("[File Viewer] Successfully exported video [${content.name}] to Media Store: ${content.userData}")
Log.i(
"[File Viewer] Successfully exported video [${content.name}] to Media Store: ${content.userData}"
)
mediaStoreFilePath = content.userData.toString()
} else {
Log.e("[File Viewer] Something went wrong while copying file to Media Store...")
Log.e(
"[File Viewer] Something went wrong while copying file to Media Store..."
)
}
}
FileUtils.isMimeAudio(mime) -> {
@ -120,32 +128,46 @@ class TopBarFragment : GenericFragment<FileViewerTopBarFragmentBinding>() {
Compatibility.addAudioToMediaStore(requireContext(), content)
}
if (export.await()) {
Log.i("[File Viewer] Successfully exported audio [${content.name}] to Media Store: ${content.userData}")
Log.i(
"[File Viewer] Successfully exported audio [${content.name}] to Media Store: ${content.userData}"
)
mediaStoreFilePath = content.userData.toString()
} else {
Log.e("[File Viewer] Something went wrong while copying file to Media Store...")
Log.e(
"[File Viewer] Something went wrong while copying file to Media Store..."
)
}
}
else -> {
Log.w("[File Viewer] File [${content.name}] isn't either an image, an audio file or a video, can't add it to the Media Store")
Log.w(
"[File Viewer] File [${content.name}] isn't either an image, an audio file or a video, can't add it to the Media Store"
)
}
}
} else {
Log.w("[File Viewer] Can't export image through Media Store API (requires Android 10 or WRITE_EXTERNAL permission, using fallback method...")
Log.w(
"[File Viewer] Can't export image through Media Store API (requires Android 10 or WRITE_EXTERNAL permission, using fallback method..."
)
}
withContext(Dispatchers.Main) {
if (mediaStoreFilePath.isEmpty()) {
Log.w("[File Viewer] Media store file path is empty, media store export failed?")
Log.w(
"[File Viewer] Media store file path is empty, media store export failed?"
)
val filePath = content.exportPlainFile().orEmpty()
plainFilePath = filePath.ifEmpty { content.filePath.orEmpty() }
Log.i("[File Viewer] Plain file path is: $plainFilePath")
if (plainFilePath.isNotEmpty()) {
if (!FileUtils.openFileInThirdPartyApp(requireActivity(), plainFilePath)) {
(requireActivity() as SnackBarActivity).showSnackBar(R.string.chat_message_no_app_found_to_handle_file_mime_type)
(requireActivity() as SnackBarActivity).showSnackBar(
R.string.chat_message_no_app_found_to_handle_file_mime_type
)
if (plainFilePath != content.filePath.orEmpty()) {
Log.i("[File Viewer] No app to open plain file path [$plainFilePath], destroying it")
Log.i(
"[File Viewer] No app to open plain file path [$plainFilePath], destroying it"
)
FileUtils.deleteFile(plainFilePath)
}
plainFilePath = ""

View file

@ -41,7 +41,11 @@ class AudioFileViewModel(content: Content) : FileViewerViewModel(content), Media
init {
mediaPlayer.apply {
setAudioAttributes(AudioAttributes.Builder().setContentType(AudioAttributes.CONTENT_TYPE_MUSIC).setUsage(AudioAttributes.USAGE_MEDIA).build())
setAudioAttributes(
AudioAttributes.Builder().setContentType(AudioAttributes.CONTENT_TYPE_MUSIC).setUsage(
AudioAttributes.USAGE_MEDIA
).build()
)
setDataSource(filePath)
prepare()
start()

View file

@ -33,7 +33,9 @@ open class FileViewerViewModel(val content: Content) : ViewModel() {
init {
filePath = if (deleteAfterUse) {
Log.i("[File Viewer] [VFS] Content is encrypted, requesting plain file path for file [${content.filePath}]")
Log.i(
"[File Viewer] [VFS] Content is encrypted, requesting plain file path for file [${content.filePath}]"
)
content.exportPlainFile()
} else {
content.filePath.orEmpty()

View file

@ -136,14 +136,15 @@ abstract class MasterFragment<T : ViewDataBinding, U : SelectionListAdapter<*, *
abstract fun deleteItems(indexesOfItemToDelete: ArrayList<Int>)
class SlidingPaneBackPressedCallback(private val slidingPaneLayout: SlidingPaneLayout) :
OnBackPressedCallback
(
OnBackPressedCallback(
slidingPaneLayout.isSlideable && slidingPaneLayout.isOpen
),
SlidingPaneLayout.PanelSlideListener {
init {
Log.d("[Master Fragment] SlidingPane isSlideable = ${slidingPaneLayout.isSlideable}, isOpen = ${slidingPaneLayout.isOpen}")
Log.d(
"[Master Fragment] SlidingPane isSlideable = ${slidingPaneLayout.isSlideable}, isOpen = ${slidingPaneLayout.isOpen}"
)
slidingPaneLayout.addPanelSlideListener(this)
}

View file

@ -77,7 +77,9 @@ abstract class SecureFragment<T : ViewDataBinding> : GenericFragment<T>() {
if ((enable && flags and WindowManager.LayoutParams.FLAG_SECURE != 0) ||
(!enable && flags and WindowManager.LayoutParams.FLAG_SECURE == 0)
) {
Log.d("[Secure Fragment] Secure flag is already ${if (enable) "enabled" else "disabled"}, skipping...")
Log.d(
"[Secure Fragment] Secure flag is already ${if (enable) "enabled" else "disabled"}, skipping..."
)
return
}

View file

@ -54,32 +54,48 @@ class TabsFragment : GenericFragment<TabsFragmentBinding>(), NavController.OnDes
binding.setHistoryClickListener {
when (findNavController().currentDestination?.id) {
R.id.masterContactsFragment -> sharedViewModel.updateContactsAnimationsBasedOnDestination.value = Event(R.id.masterCallLogsFragment)
R.id.dialerFragment -> sharedViewModel.updateDialerAnimationsBasedOnDestination.value = Event(R.id.masterCallLogsFragment)
R.id.masterContactsFragment -> sharedViewModel.updateContactsAnimationsBasedOnDestination.value = Event(
R.id.masterCallLogsFragment
)
R.id.dialerFragment -> sharedViewModel.updateDialerAnimationsBasedOnDestination.value = Event(
R.id.masterCallLogsFragment
)
}
navigateToCallHistory()
}
binding.setContactsClickListener {
when (findNavController().currentDestination?.id) {
R.id.dialerFragment -> sharedViewModel.updateDialerAnimationsBasedOnDestination.value = Event(R.id.masterContactsFragment)
R.id.dialerFragment -> sharedViewModel.updateDialerAnimationsBasedOnDestination.value = Event(
R.id.masterContactsFragment
)
}
sharedViewModel.updateContactsAnimationsBasedOnDestination.value = Event(findNavController().currentDestination?.id ?: -1)
sharedViewModel.updateContactsAnimationsBasedOnDestination.value = Event(
findNavController().currentDestination?.id ?: -1
)
navigateToContacts()
}
binding.setDialerClickListener {
when (findNavController().currentDestination?.id) {
R.id.masterContactsFragment -> sharedViewModel.updateContactsAnimationsBasedOnDestination.value = Event(R.id.dialerFragment)
R.id.masterContactsFragment -> sharedViewModel.updateContactsAnimationsBasedOnDestination.value = Event(
R.id.dialerFragment
)
}
sharedViewModel.updateDialerAnimationsBasedOnDestination.value = Event(findNavController().currentDestination?.id ?: -1)
sharedViewModel.updateDialerAnimationsBasedOnDestination.value = Event(
findNavController().currentDestination?.id ?: -1
)
navigateToDialer()
}
binding.setChatClickListener {
when (findNavController().currentDestination?.id) {
R.id.masterContactsFragment -> sharedViewModel.updateContactsAnimationsBasedOnDestination.value = Event(R.id.masterChatRoomsFragment)
R.id.dialerFragment -> sharedViewModel.updateDialerAnimationsBasedOnDestination.value = Event(R.id.masterChatRoomsFragment)
R.id.masterContactsFragment -> sharedViewModel.updateContactsAnimationsBasedOnDestination.value = Event(
R.id.masterChatRoomsFragment
)
R.id.dialerFragment -> sharedViewModel.updateDialerAnimationsBasedOnDestination.value = Event(
R.id.masterChatRoomsFragment
)
}
navigateToChatRooms()
}
@ -102,17 +118,30 @@ class TabsFragment : GenericFragment<TabsFragmentBinding>(), NavController.OnDes
) {
if (corePreferences.enableAnimations) {
when (destination.id) {
R.id.masterCallLogsFragment -> binding.motionLayout.transitionToState(R.id.call_history)
R.id.masterCallLogsFragment -> binding.motionLayout.transitionToState(
R.id.call_history
)
R.id.masterContactsFragment -> binding.motionLayout.transitionToState(R.id.contacts)
R.id.dialerFragment -> binding.motionLayout.transitionToState(R.id.dialer)
R.id.masterChatRoomsFragment -> binding.motionLayout.transitionToState(R.id.chat_rooms)
R.id.masterChatRoomsFragment -> binding.motionLayout.transitionToState(
R.id.chat_rooms
)
}
} else {
when (destination.id) {
R.id.masterCallLogsFragment -> binding.motionLayout.setTransition(R.id.call_history, R.id.call_history)
R.id.masterContactsFragment -> binding.motionLayout.setTransition(R.id.contacts, R.id.contacts)
R.id.masterCallLogsFragment -> binding.motionLayout.setTransition(
R.id.call_history,
R.id.call_history
)
R.id.masterContactsFragment -> binding.motionLayout.setTransition(
R.id.contacts,
R.id.contacts
)
R.id.dialerFragment -> binding.motionLayout.setTransition(R.id.dialer, R.id.dialer)
R.id.masterChatRoomsFragment -> binding.motionLayout.setTransition(R.id.chat_rooms, R.id.chat_rooms)
R.id.masterChatRoomsFragment -> binding.motionLayout.setTransition(
R.id.chat_rooms,
R.id.chat_rooms
)
}
}
}

View file

@ -39,7 +39,11 @@ import org.linphone.utils.*
class CallLogsListAdapter(
selectionVM: ListTopBarViewModel,
private val viewLifecycleOwner: LifecycleOwner
) : SelectionListAdapter<GroupedCallLogData, RecyclerView.ViewHolder>(selectionVM, CallLogDiffCallback()), HeaderAdapter {
) : SelectionListAdapter<GroupedCallLogData, RecyclerView.ViewHolder>(
selectionVM,
CallLogDiffCallback()
),
HeaderAdapter {
val selectedCallLogEvent: MutableLiveData<Event<GroupedCallLogData>> by lazy {
MutableLiveData<Event<GroupedCallLogData>>()
}
@ -51,7 +55,9 @@ class CallLogsListAdapter(
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val binding: HistoryListCellBinding = DataBindingUtil.inflate(
LayoutInflater.from(parent.context),
R.layout.history_list_cell, parent, false
R.layout.history_list_cell,
parent,
false
)
return ViewHolder(binding)
}
@ -115,7 +121,9 @@ class CallLogsListAdapter(
return if (previousPosition >= 0) {
val previousItemDate = getItem(previousPosition).lastCallLogStartTimestamp
!TimestampUtils.isSameDay(date, previousItemDate)
} else true
} else {
true
}
}
override fun getHeaderViewForPosition(context: Context, position: Int): View {
@ -123,7 +131,9 @@ class CallLogsListAdapter(
val date = formatDate(context, callLog.lastCallLogStartTimestamp)
val binding: GenericListHeaderBinding = DataBindingUtil.inflate(
LayoutInflater.from(context),
R.layout.generic_list_header, null, false
R.layout.generic_list_header,
null,
false
)
binding.title = date
binding.executePendingBindings()

View file

@ -62,12 +62,16 @@ class DetailCallLogFragment : GenericFragment<HistoryDetailFragmentBinding>() {
copy.clean()
val address = copy.asStringUriOnly()
Log.i("[History] Creating contact with SIP URI [$address]")
sharedViewModel.updateContactsAnimationsBasedOnDestination.value = Event(R.id.masterCallLogsFragment)
sharedViewModel.updateContactsAnimationsBasedOnDestination.value = Event(
R.id.masterCallLogsFragment
)
navigateToContacts(address)
}
binding.setContactClickListener {
sharedViewModel.updateContactsAnimationsBasedOnDestination.value = Event(R.id.masterCallLogsFragment)
sharedViewModel.updateContactsAnimationsBasedOnDestination.value = Event(
R.id.masterCallLogsFragment
)
val contactId = viewModel.contact.value?.refKey
if (contactId != null) {
Log.i("[History] Displaying native contact [$contactId]")
@ -90,7 +94,9 @@ class DetailCallLogFragment : GenericFragment<HistoryDetailFragmentBinding>() {
address.clean()
if (coreContext.core.callsNb > 0) {
Log.i("[History] Starting dialer with pre-filled URI [${address.asStringUriOnly()}], is transfer? ${sharedViewModel.pendingCallTransfer}")
Log.i(
"[History] Starting dialer with pre-filled URI [${address.asStringUriOnly()}], is transfer? ${sharedViewModel.pendingCallTransfer}"
)
sharedViewModel.updateDialerAnimationsBasedOnDestination.value =
Event(R.id.masterCallLogsFragment)
@ -104,7 +110,9 @@ class DetailCallLogFragment : GenericFragment<HistoryDetailFragmentBinding>() {
navigateToDialer(args)
} else {
val localAddress = callLog.localAddress
Log.i("[History] Starting call to ${address.asStringUriOnly()} with local address ${localAddress.asStringUriOnly()}")
Log.i(
"[History] Starting call to ${address.asStringUriOnly()} with local address ${localAddress.asStringUriOnly()}"
)
coreContext.startCall(address, localAddress = localAddress)
}
}

View file

@ -105,7 +105,9 @@ class MasterCallLogsFragment : MasterFragment<HistoryMasterFragmentBinding, Call
val navHostFragment =
childFragmentManager.findFragmentById(R.id.history_nav_container) as NavHostFragment
if (navHostFragment.navController.currentDestination?.id == R.id.emptyCallHistoryFragment) {
Log.i("[History] Foldable device has been folded, closing side pane with empty fragment")
Log.i(
"[History] Foldable device has been folded, closing side pane with empty fragment"
)
binding.slidingPane.closePane()
}
}
@ -162,7 +164,9 @@ class MasterCallLogsFragment : MasterFragment<HistoryMasterFragmentBinding, Call
if (!binding.slidingPane.isSlideable &&
deletedCallGroup.lastCallLogId == sharedViewModel.selectedCallLogGroup.value?.lastCallLogId
) {
Log.i("[History] Currently displayed history has been deleted, removing detail fragment")
Log.i(
"[History] Currently displayed history has been deleted, removing detail fragment"
)
clearDisplayedCallHistory()
}
dialog.dismiss()
@ -178,7 +182,9 @@ class MasterCallLogsFragment : MasterFragment<HistoryMasterFragmentBinding, Call
.attachToRecyclerView(binding.callLogsList)
// Divider between items
binding.callLogsList.addItemDecoration(AppUtils.getDividerDecoration(requireContext(), layoutManager))
binding.callLogsList.addItemDecoration(
AppUtils.getDividerDecoration(requireContext(), layoutManager)
)
// Displays formatted date header
val headerItemDecoration = RecyclerViewHeaderDecoration(requireContext(), adapter)
@ -212,7 +218,7 @@ class MasterCallLogsFragment : MasterFragment<HistoryMasterFragmentBinding, Call
}
adapter.startCallToEvent.observe(
viewLifecycleOwner,
viewLifecycleOwner
) {
it.consume { callLogGroup ->
val callLog = callLogGroup.lastCallLog
@ -244,8 +250,12 @@ class MasterCallLogsFragment : MasterFragment<HistoryMasterFragmentBinding, Call
}
coreContext.core.callsNb > 0 -> {
val cleanAddress = LinphoneUtils.getCleanedAddress(callLog.remoteAddress)
Log.i("[History] Starting dialer with pre-filled URI ${cleanAddress.asStringUriOnly()}, is transfer? ${sharedViewModel.pendingCallTransfer}")
sharedViewModel.updateDialerAnimationsBasedOnDestination.value = Event(R.id.masterCallLogsFragment)
Log.i(
"[History] Starting dialer with pre-filled URI ${cleanAddress.asStringUriOnly()}, is transfer? ${sharedViewModel.pendingCallTransfer}"
)
sharedViewModel.updateDialerAnimationsBasedOnDestination.value = Event(
R.id.masterCallLogsFragment
)
val args = Bundle()
args.putString("URI", cleanAddress.asStringUriOnly())
args.putBoolean("Transfer", sharedViewModel.pendingCallTransfer)
@ -255,7 +265,9 @@ class MasterCallLogsFragment : MasterFragment<HistoryMasterFragmentBinding, Call
else -> {
val cleanAddress = LinphoneUtils.getCleanedAddress(callLog.remoteAddress)
val localAddress = callLogGroup.lastCallLog.localAddress
Log.i("[History] Starting call to ${cleanAddress.asStringUriOnly()} with local address ${localAddress.asStringUriOnly()}")
Log.i(
"[History] Starting call to ${cleanAddress.asStringUriOnly()} with local address ${localAddress.asStringUriOnly()}"
)
coreContext.startCall(cleanAddress, localAddress = localAddress)
}
}
@ -289,7 +301,9 @@ class MasterCallLogsFragment : MasterFragment<HistoryMasterFragmentBinding, Call
listViewModel.deleteCallLogGroups(list)
if (!binding.slidingPane.isSlideable && closeSlidingPane) {
Log.i("[History] Currently displayed history has been deleted, removing detail fragment")
Log.i(
"[History] Currently displayed history has been deleted, removing detail fragment"
)
clearDisplayedCallHistory()
}
}

View file

@ -35,7 +35,9 @@ import org.linphone.utils.Event
import org.linphone.utils.LinphoneUtils
import org.linphone.utils.TimestampUtils
class CallLogViewModel(val callLog: CallLog, private val isRelated: Boolean = false) : GenericContactViewModel(callLog.remoteAddress) {
class CallLogViewModel(val callLog: CallLog, private val isRelated: Boolean = false) : GenericContactViewModel(
callLog.remoteAddress
) {
val peerSipUri: String by lazy {
LinphoneUtils.getDisplayableAddress(callLog.remoteAddress)
}
@ -77,7 +79,10 @@ class CallLogViewModel(val callLog: CallLog, private val isRelated: Boolean = fa
}
val duration: String by lazy {
val dateFormat = SimpleDateFormat(if (callLog.duration >= 3600) "HH:mm:ss" else "mm:ss", Locale.getDefault())
val dateFormat = SimpleDateFormat(
if (callLog.duration >= 3600) "HH:mm:ss" else "mm:ss",
Locale.getDefault()
)
val cal = Calendar.getInstance()
cal[0, 0, 0, 0, 0] = callLog.duration
dateFormat.format(cal.time)
@ -101,14 +106,25 @@ class CallLogViewModel(val callLog: CallLog, private val isRelated: Boolean = fa
val hidePlainChat = corePreferences.forceEndToEndEncryptedChat
val secureChatAllowed = LinphoneUtils.isEndToEndEncryptedChatAvailable() && (corePreferences.allowEndToEndEncryptedChatWithoutPresence || (contact.value?.getPresenceModelForUriOrTel(peerSipUri)?.hasCapability(FriendCapability.LimeX3Dh) ?: false))
val secureChatAllowed = LinphoneUtils.isEndToEndEncryptedChatAvailable() && (
corePreferences.allowEndToEndEncryptedChatWithoutPresence || (
contact.value?.getPresenceModelForUriOrTel(
peerSipUri
)?.hasCapability(FriendCapability.LimeX3Dh) ?: false
)
)
val relatedCallLogs = MutableLiveData<ArrayList<CallLogViewModel>>()
private val listener = object : CoreListenerStub() {
override fun onCallLogUpdated(core: Core, log: CallLog) {
if (callLog.remoteAddress.weakEqual(log.remoteAddress) && callLog.localAddress.weakEqual(log.localAddress)) {
Log.i("[History Detail] New call log for ${callLog.remoteAddress.asStringUriOnly()} with local address ${callLog.localAddress.asStringUriOnly()}")
if (callLog.remoteAddress.weakEqual(log.remoteAddress) && callLog.localAddress.weakEqual(
log.localAddress
)
) {
Log.i(
"[History Detail] New call log for ${callLog.remoteAddress.asStringUriOnly()} with local address ${callLog.localAddress.asStringUriOnly()}"
)
addRelatedCallLogs(arrayListOf(log))
}
}
@ -161,11 +177,21 @@ class CallLogViewModel(val callLog: CallLog, private val isRelated: Boolean = fa
val organizer = conferenceInfo.organizer
if (organizer != null) {
organizerParticipantData.value =
ConferenceSchedulingParticipantData(organizer, showLimeBadge = false, showDivider = false)
ConferenceSchedulingParticipantData(
organizer,
showLimeBadge = false,
showDivider = false
)
}
val list = arrayListOf<ConferenceSchedulingParticipantData>()
for (participant in conferenceInfo.participants) {
list.add(ConferenceSchedulingParticipantData(participant, showLimeBadge = false, showDivider = true))
list.add(
ConferenceSchedulingParticipantData(
participant,
showLimeBadge = false,
showDivider = true
)
)
}
conferenceParticipantsData.value = list
}
@ -205,7 +231,9 @@ class CallLogViewModel(val callLog: CallLog, private val isRelated: Boolean = fa
}
} else {
waitForChatRoomCreation.value = false
Log.e("[History Detail] Couldn't create chat room with address ${callLog.remoteAddress}")
Log.e(
"[History Detail] Couldn't create chat room with address ${callLog.remoteAddress}"
)
onMessageToNotifyEvent.value = Event(R.string.chat_room_creation_failed_snack)
}
}

View file

@ -153,7 +153,11 @@ class CallLogsListViewModel : ViewModel() {
callLogs.value = when (filter.value) {
CallLogsFilter.MISSED -> computeCallLogs(allCallLogs, missed = true, conference = false)
CallLogsFilter.CONFERENCE -> computeCallLogs(allCallLogs, missed = false, conference = true)
CallLogsFilter.CONFERENCE -> computeCallLogs(
allCallLogs,
missed = false,
conference = true
)
else -> computeCallLogs(allCallLogs, missed = false, conference = false)
}
}

View file

@ -39,7 +39,11 @@ import org.linphone.utils.*
class RecordingsListAdapter(
selectionVM: ListTopBarViewModel,
private val viewLifecycleOwner: LifecycleOwner
) : SelectionListAdapter<RecordingData, RecyclerView.ViewHolder>(selectionVM, RecordingDiffCallback()), HeaderAdapter {
) : SelectionListAdapter<RecordingData, RecyclerView.ViewHolder>(
selectionVM,
RecordingDiffCallback()
),
HeaderAdapter {
private lateinit var videoSurface: TextureView
@ -50,7 +54,9 @@ class RecordingsListAdapter(
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val binding: RecordingListCellBinding = DataBindingUtil.inflate(
LayoutInflater.from(parent.context),
R.layout.recording_list_cell, parent, false
R.layout.recording_list_cell,
parent,
false
)
return ViewHolder(binding)
}
@ -104,7 +110,9 @@ class RecordingsListAdapter(
return if (previousPosition >= 0) {
val previousItemDate = getItem(previousPosition).date
!TimestampUtils.isSameDay(date, previousItemDate)
} else true
} else {
true
}
}
override fun getHeaderViewForPosition(context: Context, position: Int): View {
@ -112,7 +120,9 @@ class RecordingsListAdapter(
val date = formatDate(context, recording.date.time)
val binding: GenericListHeaderBinding = DataBindingUtil.inflate(
LayoutInflater.from(context),
R.layout.generic_list_header, null, false
R.layout.generic_list_header,
null,
false
)
binding.title = date
binding.executePendingBindings()
@ -126,7 +136,13 @@ class RecordingsListAdapter(
} else if (TimestampUtils.isYesterday(date, false)) {
return context.getString(R.string.yesterday)
}
return TimestampUtils.toString(date, onlyDate = true, timestampInSecs = false, shortDate = false, hideYear = false)
return TimestampUtils.toString(
date,
onlyDate = true,
timestampInSecs = false,
shortDate = false,
hideYear = false
)
}
}

View file

@ -174,13 +174,18 @@ class RecordingData(val path: String, private val recordingListener: RecordingLi
Log.i("[Recording] Using device $playbackSoundCard to make the call recording playback")
val localPlayer = coreContext.core.createLocalPlayer(playbackSoundCard, null, null)
if (localPlayer != null) player = localPlayer
else Log.e("[Recording] Couldn't create local player!")
if (localPlayer != null) {
player = localPlayer
} else {
Log.e("[Recording] Couldn't create local player!")
}
player.addListener(listener)
player.open(path)
duration.value = player.duration
formattedDuration.value = SimpleDateFormat("mm:ss", Locale.getDefault()).format(player.duration) // is already in milliseconds
formattedDuration.value = SimpleDateFormat("mm:ss", Locale.getDefault()).format(
player.duration
) // is already in milliseconds
formattedDate.value = DateFormat.getTimeInstance(DateFormat.SHORT).format(date)
}

View file

@ -66,7 +66,9 @@ class RecordingsFragment : MasterFragment<RecordingsFragmentBinding, RecordingsL
binding.recordingsList.layoutManager = layoutManager
// Divider between items
binding.recordingsList.addItemDecoration(AppUtils.getDividerDecoration(requireContext(), layoutManager))
binding.recordingsList.addItemDecoration(
AppUtils.getDividerDecoration(requireContext(), layoutManager)
)
// Displays the first letter header
val headerItemDecoration = RecyclerViewHeaderDecoration(requireContext(), adapter)
@ -90,7 +92,9 @@ class RecordingsFragment : MasterFragment<RecordingsFragmentBinding, RecordingsL
intent.putExtra(Intent.EXTRA_TEXT, getString(R.string.recordings_export))
try {
requireActivity().startActivity(Intent.createChooser(intent, getString(R.string.recordings_export)))
requireActivity().startActivity(
Intent.createChooser(intent, getString(R.string.recordings_export))
)
} catch (anfe: ActivityNotFoundException) {
Log.e(anfe)
}
@ -136,7 +140,9 @@ class RecordingsFragment : MasterFragment<RecordingsFragmentBinding, RecordingsL
if (this::viewModel.isInitialized) {
viewModel.updateRecordingsList()
} else {
Log.e("[Recordings] Fragment resuming but viewModel lateinit property isn't initialized!")
Log.e(
"[Recordings] Fragment resuming but viewModel lateinit property isn't initialized!"
)
}
super.onResume()
}

View file

@ -68,7 +68,9 @@ class AccountSettingsFragment : GenericSettingFragment<SettingsAccountFragmentBi
it.consume {
val authInfo = viewModel.account.findAuthInfo()
if (authInfo == null) {
Log.e("[Account Settings] Failed to find auth info for account ${viewModel.account}")
Log.e(
"[Account Settings] Failed to find auth info for account ${viewModel.account}"
)
} else {
val args = Bundle()
args.putString("Username", authInfo.username)
@ -109,11 +111,21 @@ class AccountSettingsFragment : GenericSettingFragment<SettingsAccountFragmentBi
) {
it.consume {
val defaultDomainAccount = viewModel.account.params.identityAddress?.domain == corePreferences.defaultDomain
Log.i("[Account Settings] User clicked on delete account, showing confirmation dialog for ${if (defaultDomainAccount) "default domain account" else "third party account"}")
Log.i(
"[Account Settings] User clicked on delete account, showing confirmation dialog for ${if (defaultDomainAccount) "default domain account" else "third party account"}"
)
val dialogViewModel = if (defaultDomainAccount) {
DialogViewModel(getString(R.string.account_setting_delete_sip_linphone_org_confirmation_dialog), getString(R.string.account_setting_delete_dialog_title))
DialogViewModel(
getString(
R.string.account_setting_delete_sip_linphone_org_confirmation_dialog
),
getString(R.string.account_setting_delete_dialog_title)
)
} else {
DialogViewModel(getString(R.string.account_setting_delete_generic_confirmation_dialog), getString(R.string.account_setting_delete_dialog_title))
DialogViewModel(
getString(R.string.account_setting_delete_generic_confirmation_dialog),
getString(R.string.account_setting_delete_dialog_title)
)
}
val dialog: Dialog = DialogUtils.getDialog(requireContext(), dialogViewModel)

View file

@ -113,7 +113,9 @@ class AdvancedSettingsFragment : GenericSettingFragment<SettingsAdvancedFragment
}
}
viewModel.powerManagerSettingsVisibility.value = PowerManagerUtils.getDevicePowerManagerIntent(requireContext()) != null
viewModel.powerManagerSettingsVisibility.value = PowerManagerUtils.getDevicePowerManagerIntent(
requireContext()
) != null
viewModel.goToPowerManagerSettingsEvent.observe(
viewLifecycleOwner
) {

View file

@ -53,7 +53,9 @@ class AudioSettingsFragment : GenericSettingFragment<SettingsAudioFragmentBindin
viewLifecycleOwner
) {
it.consume {
Log.i("[Audio Settings] Asking for RECORD_AUDIO permission for echo canceller calibration")
Log.i(
"[Audio Settings] Asking for RECORD_AUDIO permission for echo canceller calibration"
)
requestPermissions(arrayOf(android.Manifest.permission.RECORD_AUDIO), 1)
}
}
@ -97,7 +99,12 @@ class AudioSettingsFragment : GenericSettingFragment<SettingsAudioFragmentBindin
private fun initAudioCodecsList() {
val list = arrayListOf<ViewDataBinding>()
for (payload in coreContext.core.audioPayloadTypes) {
val binding = DataBindingUtil.inflate<ViewDataBinding>(LayoutInflater.from(requireContext()), R.layout.settings_widget_switch, null, false)
val binding = DataBindingUtil.inflate<ViewDataBinding>(
LayoutInflater.from(requireContext()),
R.layout.settings_widget_switch,
null,
false
)
binding.setVariable(BR.title, payload.mimeType)
binding.setVariable(BR.subtitle, "${payload.clockRate} Hz")
binding.setVariable(BR.checked, payload.enabled())

View file

@ -99,7 +99,9 @@ class CallSettingsFragment : GenericSettingFragment<SettingsCallFragmentBinding>
updateTelecomManagerAccount()
}
} else {
Log.e("[Telecom Helper] Telecom Helper can't be created, device doesn't support connection service!")
Log.e(
"[Telecom Helper] Telecom Helper can't be created, device doesn't support connection service!"
)
}
}
}
@ -138,7 +140,9 @@ class CallSettingsFragment : GenericSettingFragment<SettingsCallFragmentBinding>
if (Compatibility.hasTelecomManagerFeature(requireContext())) {
TelecomHelper.create(requireContext())
} else {
Log.e("[Telecom Helper] Telecom Helper can't be created, device doesn't support connection service")
Log.e(
"[Telecom Helper] Telecom Helper can't be created, device doesn't support connection service"
)
}
}
updateTelecomManagerAccount()
@ -168,7 +172,9 @@ class CallSettingsFragment : GenericSettingFragment<SettingsCallFragmentBinding>
for (index in grantResults.indices) {
val result = grantResults[index]
if (result != PackageManager.PERMISSION_GRANTED) {
Log.w("[Call Settings] ${permissions[index]} permission denied but required for telecom manager")
Log.w(
"[Call Settings] ${permissions[index]} permission denied but required for telecom manager"
)
viewModel.useTelecomManager.value = false
corePreferences.useTelecomManager = false
return
@ -179,7 +185,9 @@ class CallSettingsFragment : GenericSettingFragment<SettingsCallFragmentBinding>
TelecomHelper.create(requireContext())
updateTelecomManagerAccount()
} else {
Log.e("[Telecom Helper] Telecom Helper can't be created, device doesn't support connection service")
Log.e(
"[Telecom Helper] Telecom Helper can't be created, device doesn't support connection service"
)
}
}
}

View file

@ -67,7 +67,9 @@ class ContactsSettingsFragment : GenericSettingFragment<SettingsContactsFragment
viewLifecycleOwner
) {
it.consume {
Log.i("[Contacts Settings] Asking for WRITE_CONTACTS permission to be able to store presence")
Log.i(
"[Contacts Settings] Asking for WRITE_CONTACTS permission to be able to store presence"
)
requestPermissions(arrayOf(android.Manifest.permission.WRITE_CONTACTS), 1)
}
}

View file

@ -80,7 +80,9 @@ class SettingsFragment : SecureFragment<SettingsFragmentBinding>() {
val navHostFragment =
childFragmentManager.findFragmentById(R.id.settings_nav_container) as NavHostFragment
if (navHostFragment.navController.currentDestination?.id == R.id.emptySettingsFragment) {
Log.i("[Settings] Foldable device has been folded, closing side pane with empty fragment")
Log.i(
"[Settings] Foldable device has been folded, closing side pane with empty fragment"
)
binding.slidingPane.closePane()
}
}

View file

@ -76,7 +76,12 @@ class VideoSettingsFragment : GenericSettingFragment<SettingsVideoFragmentBindin
private fun initVideoCodecsList() {
val list = arrayListOf<ViewDataBinding>()
for (payload in coreContext.core.videoPayloadTypes) {
val binding = DataBindingUtil.inflate<ViewDataBinding>(LayoutInflater.from(requireContext()), R.layout.settings_widget_switch_and_text, null, false)
val binding = DataBindingUtil.inflate<ViewDataBinding>(
LayoutInflater.from(requireContext()),
R.layout.settings_widget_switch_and_text,
null,
false
)
binding.setVariable(BR.switch_title, payload.mimeType)
binding.setVariable(BR.switch_subtitle, "")
binding.setVariable(BR.text_title, "recv-fmtp")

View file

@ -89,7 +89,9 @@ class AccountSettingsViewModel(val account: Account) : GenericSettingsViewModel(
message: String
) {
if (state == RegistrationState.Cleared && account == accountToDelete) {
Log.i("[Account Settings] Account to remove ([${account.params.identityAddress?.asStringUriOnly()}]) registration is now cleared, removing it")
Log.i(
"[Account Settings] Account to remove ([${account.params.identityAddress?.asStringUriOnly()}]) registration is now cleared, removing it"
)
waitForUnregister.value = false
deleteAccount(account)
} else {
@ -121,7 +123,9 @@ class AccountSettingsViewModel(val account: Account) : GenericSettingsViewModel(
params.identityAddress = newIdentityAddress
account.params = params
} else {
Log.e("[Account Settings] Failed to create identity address sip:$newValue@$domain")
Log.e(
"[Account Settings] Failed to create identity address sip:$newValue@$domain"
)
}
}
}
@ -157,10 +161,19 @@ class AccountSettingsViewModel(val account: Account) : GenericSettingsViewModel(
val identity = params.identityAddress
if (identity != null && identity.username != null) {
val newAuthInfo = Factory.instance()
.createAuthInfo(identity.username!!, userId.value, newValue, null, null, identity.domain)
.createAuthInfo(
identity.username!!,
userId.value,
newValue,
null,
null,
identity.domain
)
core.addAuthInfo(newAuthInfo)
} else {
Log.e("[Account Settings] Failed to find the user's identity, can't create a new auth info")
Log.e(
"[Account Settings] Failed to find the user's identity, can't create a new auth info"
)
}
}
}
@ -394,7 +407,9 @@ class AccountSettingsViewModel(val account: Account) : GenericSettingsViewModel(
val conferenceFactoryUriListener = object : SettingListenerStub() {
override fun onTextValueChanged(newValue: String) {
val params = account.params.clone()
Log.i("[Account Settings] Forcing conference factory on proxy config ${params.identityAddress?.asString()} to value: $newValue")
Log.i(
"[Account Settings] Forcing conference factory on proxy config ${params.identityAddress?.asString()} to value: $newValue"
)
params.conferenceFactoryUri = newValue
account.params = params
}
@ -405,7 +420,9 @@ class AccountSettingsViewModel(val account: Account) : GenericSettingsViewModel(
override fun onTextValueChanged(newValue: String) {
val params = account.params.clone()
val uri = coreContext.core.interpretUrl(newValue, false)
Log.i("[Account Settings] Forcing audio/video conference factory on proxy config ${params.identityAddress?.asString()} to value: $newValue")
Log.i(
"[Account Settings] Forcing audio/video conference factory on proxy config ${params.identityAddress?.asString()} to value: $newValue"
)
params.audioVideoConferenceFactoryAddress = uri
account.params = params
}
@ -510,7 +527,9 @@ class AccountSettingsViewModel(val account: Account) : GenericSettingsViewModel(
}
fun startDeleteAccount() {
Log.i("[Account Settings] Starting to delete account [${account.params.identityAddress?.asStringUriOnly()}]")
Log.i(
"[Account Settings] Starting to delete account [${account.params.identityAddress?.asStringUriOnly()}]"
)
accountToDelete = account
val registered = account.state == RegistrationState.Ok
@ -521,7 +540,9 @@ class AccountSettingsViewModel(val account: Account) : GenericSettingsViewModel(
for (accountIterator in core.accountList) {
if (account != accountIterator) {
core.defaultAccount = accountIterator
Log.i("[Account Settings] New default account is [${accountIterator.params.identityAddress?.asStringUriOnly()}]")
Log.i(
"[Account Settings] New default account is [${accountIterator.params.identityAddress?.asStringUriOnly()}]"
)
break
}
}
@ -532,10 +553,14 @@ class AccountSettingsViewModel(val account: Account) : GenericSettingsViewModel(
account.params = params
if (!registered) {
Log.w("[Account Settings] Account isn't registered, don't unregister before removing it")
Log.w(
"[Account Settings] Account isn't registered, don't unregister before removing it"
)
deleteAccount(account)
} else {
Log.i("[Account Settings] Waiting for account registration to be cleared before removing it")
Log.i(
"[Account Settings] Waiting for account registration to be cleared before removing it"
)
}
}
}

View file

@ -44,7 +44,9 @@ class AudioSettingsViewModel : GenericSettingsViewModel() {
core.isEchoCancellationEnabled = newValue
if (!newValue) {
core.resetEchoCancellationCalibration()
softwareEchoCalibration.value = prefs.getString(R.string.audio_settings_echo_canceller_calibration_summary)
softwareEchoCalibration.value = prefs.getString(
R.string.audio_settings_echo_canceller_calibration_summary
)
}
}
}
@ -161,7 +163,9 @@ class AudioSettingsViewModel : GenericSettingsViewModel() {
softwareEchoCanceller.value = core.isEchoCancellationEnabled
adaptiveRateControl.value = core.isAdaptiveRateControlEnabled
softwareEchoCalibration.value = if (core.echoCancellationCalibration > 0) {
prefs.getString(R.string.audio_settings_echo_cancellation_calibration_value).format(core.echoCancellationCalibration)
prefs.getString(R.string.audio_settings_echo_cancellation_calibration_value).format(
core.echoCancellationCalibration
)
} else {
prefs.getString(R.string.audio_settings_echo_canceller_calibration_summary)
}
@ -183,7 +187,9 @@ class AudioSettingsViewModel : GenericSettingsViewModel() {
core.addListener(listener)
core.startEchoCancellerCalibration()
softwareEchoCalibration.value = prefs.getString(R.string.audio_settings_echo_cancellation_calibration_started)
softwareEchoCalibration.value = prefs.getString(
R.string.audio_settings_echo_cancellation_calibration_started
)
}
fun echoCancellerCalibrationFinished(status: EcCalibratorStatus, delay: Int) {
@ -191,31 +197,43 @@ class AudioSettingsViewModel : GenericSettingsViewModel() {
when (status) {
EcCalibratorStatus.DoneNoEcho -> {
softwareEchoCalibration.value = prefs.getString(R.string.audio_settings_echo_cancellation_calibration_no_echo)
softwareEchoCalibration.value = prefs.getString(
R.string.audio_settings_echo_cancellation_calibration_no_echo
)
softwareEchoCanceller.value = false
}
EcCalibratorStatus.Done -> {
softwareEchoCalibration.value = prefs.getString(R.string.audio_settings_echo_cancellation_calibration_value).format(delay)
softwareEchoCalibration.value = prefs.getString(
R.string.audio_settings_echo_cancellation_calibration_value
).format(delay)
softwareEchoCanceller.value = true
}
EcCalibratorStatus.Failed -> {
softwareEchoCalibration.value = prefs.getString(R.string.audio_settings_echo_cancellation_calibration_failed)
softwareEchoCalibration.value = prefs.getString(
R.string.audio_settings_echo_cancellation_calibration_failed
)
}
EcCalibratorStatus.InProgress -> { // We should never get here but still
softwareEchoCalibration.value = prefs.getString(R.string.audio_settings_echo_cancellation_calibration_started)
softwareEchoCalibration.value = prefs.getString(
R.string.audio_settings_echo_cancellation_calibration_started
)
}
}
}
fun startEchoTester() {
echoTesterIsRunning = true
echoTesterStatus.value = prefs.getString(R.string.audio_settings_echo_tester_summary_is_running)
echoTesterStatus.value = prefs.getString(
R.string.audio_settings_echo_tester_summary_is_running
)
core.startEchoTester(0)
}
fun stopEchoTester() {
echoTesterIsRunning = false
echoTesterStatus.value = prefs.getString(R.string.audio_settings_echo_tester_summary_is_stopped)
echoTesterStatus.value = prefs.getString(
R.string.audio_settings_echo_tester_summary_is_stopped
)
core.stopEchoTester()
}

View file

@ -291,7 +291,9 @@ class CallSettingsViewModel : GenericSettingsViewModel() {
}
if (core.mediaEncryptionSupported(MediaEncryption.ZRTP)) {
if (core.postQuantumAvailable) {
labels.add(prefs.getString(R.string.call_settings_media_encryption_zrtp_post_quantum))
labels.add(
prefs.getString(R.string.call_settings_media_encryption_zrtp_post_quantum)
)
} else {
labels.add(prefs.getString(R.string.call_settings_media_encryption_zrtp))
}

View file

@ -123,7 +123,9 @@ class LdapSettingsViewModel(private val ldap: Ldap, val index: String) : Generic
val ldapCertCheckListener = object : SettingListenerStub() {
override fun onListValueChanged(position: Int) {
val params = ldap.params.clone()
params.serverCertificatesVerificationMode = LdapCertVerificationMode.fromInt(ldapCertCheckValues[position])
params.serverCertificatesVerificationMode = LdapCertVerificationMode.fromInt(
ldapCertCheckValues[position]
)
ldap.params = params
ldapCertCheckIndex.value = position
}
@ -291,6 +293,8 @@ class LdapSettingsViewModel(private val ldap: Ldap, val index: String) : Generic
ldapCertCheckValues.add(LdapCertVerificationMode.Enabled.toInt())
ldapCertCheckLabels.value = labels
ldapCertCheckIndex.value = ldapCertCheckValues.indexOf(ldap.params.serverCertificatesVerificationMode.toInt())
ldapCertCheckIndex.value = ldapCertCheckValues.indexOf(
ldap.params.serverCertificatesVerificationMode.toInt()
)
}
}

View file

@ -143,7 +143,9 @@ class VideoSettingsViewModel : GenericSettingsViewModel() {
val index = labels.indexOf(core.videoDevice)
if (index == -1) {
val firstDevice = cameraDeviceLabels.value.orEmpty().firstOrNull()
Log.w("[Video Settings] Device not found in labels list: ${core.videoDevice}, replace it by $firstDevice")
Log.w(
"[Video Settings] Device not found in labels list: ${core.videoDevice}, replace it by $firstDevice"
)
if (firstDevice != null) {
cameraDeviceIndex.value = 0
core.videoDevice = firstDevice

View file

@ -142,7 +142,10 @@ class SideMenuFragment : GenericFragment<SideMenuFragmentBinding>() {
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (resultCode == Activity.RESULT_OK) {
lifecycleScope.launch {
val contactImageFilePath = FileUtils.getFilePathFromPickerIntent(data, temporaryPicturePath)
val contactImageFilePath = FileUtils.getFilePathFromPickerIntent(
data,
temporaryPicturePath
)
if (contactImageFilePath != null) {
viewModel.setPictureFromPath(contactImageFilePath)
}
@ -176,7 +179,10 @@ class SideMenuFragment : GenericFragment<SideMenuFragmentBinding>() {
val chooserIntent =
Intent.createChooser(galleryIntent, getString(R.string.chat_message_pick_file_dialog))
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, cameraIntents.toArray(arrayOf<Parcelable>()))
chooserIntent.putExtra(
Intent.EXTRA_INITIAL_INTENTS,
cameraIntents.toArray(arrayOf<Parcelable>())
)
startActivityForResult(chooserIntent, 0)
}
@ -186,11 +192,15 @@ class SideMenuFragment : GenericFragment<SideMenuFragmentBinding>() {
goToAccountSettings: Boolean = false,
accountIdentity: String = ""
) {
val dialogViewModel = DialogViewModel(getString(R.string.settings_password_protection_dialog_title))
val dialogViewModel = DialogViewModel(
getString(R.string.settings_password_protection_dialog_title)
)
dialogViewModel.showIcon = true
dialogViewModel.iconResource = R.drawable.security_toggle_icon_green
dialogViewModel.showPassword = true
dialogViewModel.passwordTitle = getString(R.string.settings_password_protection_dialog_input_hint)
dialogViewModel.passwordTitle = getString(
R.string.settings_password_protection_dialog_input_hint
)
val dialog = DialogUtils.getDialog(requireContext(), dialogViewModel)
dialogViewModel.showCancelButton {
@ -206,13 +216,19 @@ class SideMenuFragment : GenericFragment<SideMenuFragmentBinding>() {
} else {
val authInfo = defaultAccount.findAuthInfo()
if (authInfo == null) {
Log.e("[Side Menu] No auth info found for account [${defaultAccount.params.identityAddress?.asString()}], can't check password input!")
Log.e(
"[Side Menu] No auth info found for account [${defaultAccount.params.identityAddress?.asString()}], can't check password input!"
)
(requireActivity() as MainActivity).showSnackBar(R.string.error_unexpected)
} else {
val expectedHash = authInfo.ha1
if (expectedHash == null) {
Log.e("[Side Menu] No ha1 found in auth info, can't check password input!")
(requireActivity() as MainActivity).showSnackBar(R.string.error_unexpected)
Log.e(
"[Side Menu] No ha1 found in auth info, can't check password input!"
)
(requireActivity() as MainActivity).showSnackBar(
R.string.error_unexpected
)
} else {
val hashAlgorithm = authInfo.algorithm ?: "MD5"
val userId = (authInfo.userid ?: authInfo.username).orEmpty()
@ -225,8 +241,12 @@ class SideMenuFragment : GenericFragment<SideMenuFragmentBinding>() {
hashAlgorithm
)
if (computedHash != expectedHash) {
Log.e("[Side Menu] Computed hash [$computedHash] using userId [$userId], realm [$realm] and algorithm [$hashAlgorithm] doesn't match expected hash!")
(requireActivity() as MainActivity).showSnackBar(R.string.settings_password_protection_dialog_invalid_input)
Log.e(
"[Side Menu] Computed hash [$computedHash] using userId [$userId], realm [$realm] and algorithm [$hashAlgorithm] doesn't match expected hash!"
)
(requireActivity() as MainActivity).showSnackBar(
R.string.settings_password_protection_dialog_invalid_input
)
} else {
if (goToSettings) {
navigateToSettings()

View file

@ -41,7 +41,10 @@ class TabsViewModel : ViewModel() {
val chatUnreadCountTranslateY = MutableLiveData<Float>()
private val bounceAnimator: ValueAnimator by lazy {
ValueAnimator.ofFloat(AppUtils.getDimension(R.dimen.tabs_fragment_unread_count_bounce_offset), 0f).apply {
ValueAnimator.ofFloat(
AppUtils.getDimension(R.dimen.tabs_fragment_unread_count_bounce_offset),
0f
).apply {
addUpdateListener {
val value = it.animatedValue as Float
historyMissedCountTranslateY.value = -value

View file

@ -104,14 +104,20 @@ class CallActivity : ProximitySensorActivity() {
controlsViewModel.proximitySensorEnabled.observe(
this
) { enabled ->
Log.i("[Call Activity] ${if (enabled) "Enabling" else "Disabling"} proximity sensor (if possible)")
Log.i(
"[Call Activity] ${if (enabled) "Enabling" else "Disabling"} proximity sensor (if possible)"
)
enableProximitySensor(enabled)
}
controlsViewModel.isVideoEnabled.observe(
this
) { enabled ->
Compatibility.enableAutoEnterPiP(this, enabled, conferenceViewModel.conferenceExists.value == true)
Compatibility.enableAutoEnterPiP(
this,
enabled,
conferenceViewModel.conferenceExists.value == true
)
}
controlsViewModel.callStatsVisible.observe(
@ -136,10 +142,14 @@ class CallActivity : ProximitySensorActivity() {
) { callData ->
val call = callData.call
if (call.conference == null) {
Log.i("[Call Activity] Current call isn't linked to a conference, switching to SingleCall fragment")
Log.i(
"[Call Activity] Current call isn't linked to a conference, switching to SingleCall fragment"
)
navigateToActiveCall()
} else {
Log.i("[Call Activity] Current call is linked to a conference, switching to ConferenceCall fragment")
Log.i(
"[Call Activity] Current call is linked to a conference, switching to ConferenceCall fragment"
)
navigateToConferenceCall()
}
}
@ -157,10 +167,14 @@ class CallActivity : ProximitySensorActivity() {
this
) { exists ->
if (exists) {
Log.i("[Call Activity] Found active conference, changing switching to ConferenceCall fragment")
Log.i(
"[Call Activity] Found active conference, changing switching to ConferenceCall fragment"
)
navigateToConferenceCall()
} else if (coreContext.core.callsNb > 0) {
Log.i("[Call Activity] Conference no longer exists, switching to SingleCall fragment")
Log.i(
"[Call Activity] Conference no longer exists, switching to SingleCall fragment"
)
navigateToActiveCall()
}
}
@ -193,7 +207,9 @@ class CallActivity : ProximitySensorActivity() {
) {
super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig)
Log.i("[Call Activity] onPictureInPictureModeChanged: is in PiP mode? $isInPictureInPictureMode")
Log.i(
"[Call Activity] onPictureInPictureModeChanged: is in PiP mode? $isInPictureInPictureMode"
)
if (::controlsViewModel.isInitialized) {
// To hide UI except for TextureViews
controlsViewModel.pipMode.value = isInPictureInPictureMode
@ -299,7 +315,9 @@ class CallActivity : ProximitySensorActivity() {
Log.i("[Call Activity] BLUETOOTH_CONNECT permission has been granted")
}
Manifest.permission.WRITE_EXTERNAL_STORAGE -> if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
Log.i("[Call Activity] WRITE_EXTERNAL_STORAGE permission has been granted, taking snapshot")
Log.i(
"[Call Activity] WRITE_EXTERNAL_STORAGE permission has been granted, taking snapshot"
)
controlsViewModel.takeSnapshot()
}
}
@ -310,7 +328,9 @@ class CallActivity : ProximitySensorActivity() {
override fun onLayoutChanges(foldingFeature: FoldingFeature?) {
foldingFeature ?: return
Log.i("[Call Activity] Folding feature state changed: ${foldingFeature.state}, orientation is ${foldingFeature.orientation}")
Log.i(
"[Call Activity] Folding feature state changed: ${foldingFeature.state}, orientation is ${foldingFeature.orientation}"
)
controlsViewModel.foldingState.value = foldingFeature
}

View file

@ -101,7 +101,10 @@ open class CallData(val call: Call) : GenericContactData(call.remoteAddress) {
scope.launch {
if (Compatibility.addImageToMediaStore(coreContext.context, content)) {
Log.i("[Call] Added snapshot ${content.name} to Media Store")
val message = String.format(AppUtils.getString(R.string.call_screenshot_taken), content.name)
val message = String.format(
AppUtils.getString(R.string.call_screenshot_taken),
content.name
)
Toast.makeText(coreContext.context, message, Toast.LENGTH_SHORT).show()
} else {
Log.e("[Call] Something went wrong while copying file to Media Store...")
@ -188,7 +191,9 @@ open class CallData(val call: Call) : GenericContactData(call.remoteAddress) {
Call.State.PausedByRemote -> {
val conference = call.conference
if (conference != null && conference.me.isFocus) {
Log.w("[Call] State is paused by remote but we are the focus of the conference, so considering call as active")
Log.w(
"[Call] State is paused by remote but we are the focus of the conference, so considering call as active"
)
false
} else {
true
@ -237,7 +242,9 @@ open class CallData(val call: Call) : GenericContactData(call.remoteAddress) {
if (conference != null) {
Log.d("[Call] Found conference attached to call")
remoteConferenceSubject.value = LinphoneUtils.getConferenceSubject(conference)
Log.d("[Call] Found conference related to this call with subject [${remoteConferenceSubject.value}]")
Log.d(
"[Call] Found conference related to this call with subject [${remoteConferenceSubject.value}]"
)
val participantsList = arrayListOf<ConferenceInfoParticipantData>()
for (participant in conference.participantList) {
@ -246,12 +253,23 @@ open class CallData(val call: Call) : GenericContactData(call.remoteAddress) {
}
conferenceParticipants.value = participantsList
conferenceParticipantsCountLabel.value = coreContext.context.getString(R.string.conference_participants_title, participantsList.size)
conferenceParticipantsCountLabel.value = coreContext.context.getString(
R.string.conference_participants_title,
participantsList.size
)
} else {
val conferenceAddress = LinphoneUtils.getConferenceAddress(call)
val conferenceInfo = if (conferenceAddress != null) coreContext.core.findConferenceInformationFromUri(conferenceAddress) else null
val conferenceInfo = if (conferenceAddress != null) {
coreContext.core.findConferenceInformationFromUri(
conferenceAddress
)
} else {
null
}
if (conferenceInfo != null) {
Log.d("[Call] Found matching conference info with subject: ${conferenceInfo.subject}")
Log.d(
"[Call] Found matching conference info with subject: ${conferenceInfo.subject}"
)
remoteConferenceSubject.value = conferenceInfo.subject
val participantsList = arrayListOf<ConferenceInfoParticipantData>()
@ -271,7 +289,10 @@ open class CallData(val call: Call) : GenericContactData(call.remoteAddress) {
}
conferenceParticipants.value = participantsList
conferenceParticipantsCountLabel.value = coreContext.context.getString(R.string.conference_participants_title, participantsList.size)
conferenceParticipantsCountLabel.value = coreContext.context.getString(
R.string.conference_participants_title,
participantsList.size
)
}
}
}

Some files were not shown because too many files have changed in this diff Show more