diff --git a/app/src/main/java/org/linphone/core/CorePreferences.kt b/app/src/main/java/org/linphone/core/CorePreferences.kt
index 3c4f92856..7c07b8dd6 100644
--- a/app/src/main/java/org/linphone/core/CorePreferences.kt
+++ b/app/src/main/java/org/linphone/core/CorePreferences.kt
@@ -62,6 +62,20 @@ class CorePreferences @UiThread constructor(private val context: Context) {
config.setBool("app", "publish_presence", value)
}
+ // Calls settings
+
+ var routeAudioToBluetoothIfAvailable: Boolean
+ get() = config.getBool("app", "route_audio_to_bluetooth_if_available", true)
+ set(value) {
+ config.setBool("app", "route_audio_to_bluetooth_if_available", value)
+ }
+
+ var automaticallyStartCallRecording: Boolean
+ get() = config.getBool("app", "auto_start_call_record", false)
+ set(value) {
+ config.setBool("app", "auto_start_call_record", value)
+ }
+
// Will disable chat feature completely
@get:WorkerThread
val disableChat: Boolean
@@ -91,6 +105,12 @@ class CorePreferences @UiThread constructor(private val context: Context) {
val thirdPartyDefaultValuesPath: String
get() = context.filesDir.absolutePath + "/assistant_third_party_default_values"
+ private val ringtonesPath: String
+ get() = context.filesDir.absolutePath + "/share/sounds/linphone/rings/"
+
+ val defaultRingtonePath: String
+ get() = ringtonesPath + "notes_of_the_optimistic.mkv"
+
@UiThread
fun copyAssetsFromPackage() {
copy("linphonerc_default", configPath)
diff --git a/app/src/main/java/org/linphone/ui/main/settings/fragment/SettingsFragment.kt b/app/src/main/java/org/linphone/ui/main/settings/fragment/SettingsFragment.kt
index 8ec0e54ad..aa0ea0122 100644
--- a/app/src/main/java/org/linphone/ui/main/settings/fragment/SettingsFragment.kt
+++ b/app/src/main/java/org/linphone/ui/main/settings/fragment/SettingsFragment.kt
@@ -5,13 +5,20 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.annotation.UiThread
+import androidx.navigation.navGraphViewModels
+import org.linphone.R
import org.linphone.databinding.SettingsFragmentBinding
import org.linphone.ui.main.fragment.GenericFragment
+import org.linphone.ui.main.settings.viewmodel.SettingsViewModel
@UiThread
class SettingsFragment : GenericFragment() {
private lateinit var binding: SettingsFragmentBinding
+ private val viewModel: SettingsViewModel by navGraphViewModels(
+ R.id.main_nav_graph
+ )
+
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
@@ -24,6 +31,7 @@ class SettingsFragment : GenericFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.lifecycleOwner = viewLifecycleOwner
+ binding.viewModel = viewModel
binding.setBackClickListener {
goBack()
diff --git a/app/src/main/java/org/linphone/ui/main/settings/viewmodel/SettingsViewModel.kt b/app/src/main/java/org/linphone/ui/main/settings/viewmodel/SettingsViewModel.kt
new file mode 100644
index 000000000..a544b0c98
--- /dev/null
+++ b/app/src/main/java/org/linphone/ui/main/settings/viewmodel/SettingsViewModel.kt
@@ -0,0 +1,181 @@
+/*
+ * Copyright (c) 2010-2023 Belledonne Communications SARL.
+ *
+ * This file is part of linphone-android
+ * (see https://www.linphone.org).
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.linphone.ui.main.settings.viewmodel
+
+import android.os.Vibrator
+import androidx.annotation.UiThread
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import org.linphone.LinphoneApplication.Companion.coreContext
+import org.linphone.LinphoneApplication.Companion.corePreferences
+import org.linphone.core.tools.Log
+
+class SettingsViewModel @UiThread constructor() : ViewModel() {
+ companion object {
+ private const val TAG = "[Settings ViewModel]"
+ }
+
+ val expandCalls = MutableLiveData()
+ val expandNetwork = MutableLiveData()
+ val expandUserInterface = MutableLiveData()
+
+ // Calls settings
+ val echoCancellerEnabled = MutableLiveData()
+ val routeAudioToBluetooth = MutableLiveData()
+ val videoEnabled = MutableLiveData()
+ val autoInitiateVideoCalls = MutableLiveData()
+ val autoAcceptVideoRequests = MutableLiveData()
+ val useDeviceRingtone = MutableLiveData()
+ val vibrateDuringIncomingCall = MutableLiveData()
+ val autoRecordCalls = MutableLiveData()
+
+ // Network settings
+ val useWifiOnly = MutableLiveData()
+
+ // User Interface settings
+
+ val isVibrationAvailable = MutableLiveData()
+
+ init {
+ expandCalls.value = true
+ expandNetwork.value = true
+ expandUserInterface.value = false
+
+ val vibrator = coreContext.context.getSystemService(Vibrator::class.java)
+ isVibrationAvailable.value = vibrator.hasVibrator()
+ if (isVibrationAvailable.value == false) {
+ Log.w("$TAG Device doesn't seem to have a vibrator, hiding related setting")
+ }
+
+ coreContext.postOnCoreThread { core ->
+ echoCancellerEnabled.postValue(core.isEchoCancellationEnabled)
+ routeAudioToBluetooth.postValue(corePreferences.routeAudioToBluetoothIfAvailable)
+ videoEnabled.postValue(core.isVideoEnabled)
+ autoInitiateVideoCalls.postValue(core.videoActivationPolicy.automaticallyInitiate)
+ autoAcceptVideoRequests.postValue(core.videoActivationPolicy.automaticallyAccept)
+ useDeviceRingtone.postValue(core.ring == null)
+ vibrateDuringIncomingCall.postValue(core.isVibrationOnIncomingCallEnabled)
+ autoRecordCalls.postValue(corePreferences.automaticallyStartCallRecording)
+
+ useWifiOnly.postValue(core.isWifiOnlyEnabled)
+ }
+ }
+
+ @UiThread
+ fun toggleEchoCanceller() {
+ val newValue = echoCancellerEnabled.value == false
+ coreContext.postOnCoreThread { core ->
+ core.isEchoCancellationEnabled = newValue
+ echoCancellerEnabled.postValue(newValue)
+ }
+ }
+
+ @UiThread
+ fun toggleRouteAudioToBluetooth() {
+ val newValue = routeAudioToBluetooth.value == false
+ coreContext.postOnCoreThread {
+ corePreferences.routeAudioToBluetoothIfAvailable = newValue
+ routeAudioToBluetooth.postValue(newValue)
+ }
+ }
+
+ @UiThread
+ fun toggleEnableVideo() {
+ val newValue = videoEnabled.value == false
+ coreContext.postOnCoreThread { core ->
+ core.isVideoCaptureEnabled = newValue
+ core.isVideoDisplayEnabled = newValue
+ videoEnabled.postValue(newValue)
+ }
+ }
+
+ @UiThread
+ fun toggleAutoInitiateVideoCalls() {
+ val newValue = autoInitiateVideoCalls.value == false
+ coreContext.postOnCoreThread { core ->
+ val policy = core.videoActivationPolicy
+ policy.automaticallyInitiate = newValue
+ core.videoActivationPolicy = policy
+ autoInitiateVideoCalls.postValue(newValue)
+ }
+ }
+
+ @UiThread
+ fun toggleAutoAcceptVideoRequests() {
+ val newValue = autoAcceptVideoRequests.value == false
+ coreContext.postOnCoreThread { core ->
+ val policy = core.videoActivationPolicy
+ policy.automaticallyAccept = newValue
+ core.videoActivationPolicy = policy
+ autoAcceptVideoRequests.postValue(newValue)
+ }
+ }
+
+ @UiThread
+ fun toggleDeviceRingtone() {
+ val newValue = useDeviceRingtone.value == false
+ coreContext.postOnCoreThread { core ->
+ core.ring = if (newValue) null else corePreferences.defaultRingtonePath
+ useDeviceRingtone.postValue(newValue)
+ }
+ }
+
+ @UiThread
+ fun toggleVibrateOnIncomingCalls() {
+ val newValue = vibrateDuringIncomingCall.value == false
+ coreContext.postOnCoreThread { core ->
+ core.isVibrationOnIncomingCallEnabled = newValue
+ vibrateDuringIncomingCall.postValue(newValue)
+ }
+ }
+
+ @UiThread
+ fun toggleAutoRecordCall() {
+ val newValue = autoRecordCalls.value == false
+ coreContext.postOnCoreThread {
+ corePreferences.automaticallyStartCallRecording = newValue
+ autoRecordCalls.postValue(newValue)
+ }
+ }
+
+ @UiThread
+ fun toggleUseWifiOnly() {
+ val newValue = useWifiOnly.value == false
+ coreContext.postOnCoreThread { core ->
+ core.isWifiOnlyEnabled = newValue
+ useWifiOnly.postValue(newValue)
+ }
+ }
+
+ @UiThread
+ fun toggleCallsExpand() {
+ expandCalls.value = expandCalls.value == false
+ }
+
+ @UiThread
+ fun toggleNetworkExpand() {
+ expandNetwork.value = expandNetwork.value == false
+ }
+
+ @UiThread
+ fun toggleUserInterfaceExpand() {
+ expandUserInterface.value = expandUserInterface.value == false
+ }
+}
diff --git a/app/src/main/res/drawable/caret_up.xml b/app/src/main/res/drawable/caret_up.xml
index b1fdf9889..a8354428f 100644
--- a/app/src/main/res/drawable/caret_up.xml
+++ b/app/src/main/res/drawable/caret_up.xml
@@ -1,13 +1,9 @@
-
-
+ android:viewportWidth="256"
+ android:viewportHeight="256">
+
diff --git a/app/src/main/res/layout/account_profile_fragment.xml b/app/src/main/res/layout/account_profile_fragment.xml
index 455b9fe1f..6e2580a36 100644
--- a/app/src/main/res/layout/account_profile_fragment.xml
+++ b/app/src/main/res/layout/account_profile_fragment.xml
@@ -145,7 +145,7 @@
android:id="@+id/details_background"
android:layout_width="0dp"
android:layout_height="0dp"
- android:layout_marginTop="16dp"
+ android:layout_marginTop="8dp"
android:layout_marginStart="17dp"
android:layout_marginEnd="17dp"
android:src="@drawable/shape_squircle_gray_2_background"
@@ -351,7 +351,7 @@
android:id="@+id/actions_background"
android:layout_width="0dp"
android:layout_height="0dp"
- android:layout_marginTop="10dp"
+ android:layout_marginTop="8dp"
android:layout_marginStart="17dp"
android:layout_marginEnd="17dp"
android:src="@drawable/shape_squircle_gray_2_background"
diff --git a/app/src/main/res/layout/account_profile_secure_mode_fragment.xml b/app/src/main/res/layout/account_profile_secure_mode_fragment.xml
index 0bcc7091d..8cd66f892 100644
--- a/app/src/main/res/layout/account_profile_secure_mode_fragment.xml
+++ b/app/src/main/res/layout/account_profile_secure_mode_fragment.xml
@@ -63,7 +63,7 @@
style="@style/default_text_style_800"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginTop="30dp"
+ android:layout_marginTop="32dp"
android:layout_marginStart="16dp"
android:text="@string/assistant_secure_mode_default_title"
android:textSize="16sp"
@@ -197,7 +197,7 @@
style="@style/default_text_style_800"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginTop="30dp"
+ android:layout_marginTop="32dp"
android:layout_marginStart="16dp"
android:text="@string/assistant_secure_mode_interoperable_title"
android:textSize="16sp"
diff --git a/app/src/main/res/layout/assistant_secure_mode_fragment.xml b/app/src/main/res/layout/assistant_secure_mode_fragment.xml
index eae4fdd0b..50ed69ed9 100644
--- a/app/src/main/res/layout/assistant_secure_mode_fragment.xml
+++ b/app/src/main/res/layout/assistant_secure_mode_fragment.xml
@@ -86,7 +86,7 @@
style="@style/default_text_style_800"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginTop="30dp"
+ android:layout_marginTop="32dp"
android:layout_marginStart="16dp"
android:text="@string/assistant_secure_mode_default_title"
android:textSize="16sp"
@@ -220,7 +220,7 @@
style="@style/default_text_style_800"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginTop="30dp"
+ android:layout_marginTop="32dp"
android:layout_marginStart="16dp"
android:text="@string/assistant_secure_mode_interoperable_title"
android:textSize="16sp"
diff --git a/app/src/main/res/layout/contact_fragment.xml b/app/src/main/res/layout/contact_fragment.xml
index adf66b4ce..9418196cc 100644
--- a/app/src/main/res/layout/contact_fragment.xml
+++ b/app/src/main/res/layout/contact_fragment.xml
@@ -260,7 +260,7 @@
android:orientation="vertical"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
- android:layout_marginTop="10dp"
+ android:layout_marginTop="8dp"
android:padding="10dp"
android:background="@drawable/shape_squircle_gray_2_background"
app:layout_constraintStart_toStartOf="parent"
@@ -378,7 +378,7 @@
android:id="@+id/trust_background"
android:layout_width="0dp"
android:layout_height="0dp"
- android:layout_marginTop="10dp"
+ android:layout_marginTop="8dp"
android:layout_marginStart="17dp"
android:layout_marginEnd="17dp"
android:src="@drawable/shape_squircle_gray_2_background"
@@ -465,7 +465,7 @@
android:id="@+id/actions_background"
android:layout_width="0dp"
android:layout_height="0dp"
- android:layout_marginTop="10dp"
+ android:layout_marginTop="8dp"
android:layout_marginStart="17dp"
android:layout_marginEnd="17dp"
android:src="@drawable/shape_squircle_gray_2_background"
diff --git a/app/src/main/res/layout/settings_fragment.xml b/app/src/main/res/layout/settings_fragment.xml
index 159772c0c..99b281838 100644
--- a/app/src/main/res/layout/settings_fragment.xml
+++ b/app/src/main/res/layout/settings_fragment.xml
@@ -8,42 +8,529 @@
+
-
-
+
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/voip_call_main_actions.xml b/app/src/main/res/layout/voip_call_main_actions.xml
index 35977dbec..647937733 100644
--- a/app/src/main/res/layout/voip_call_main_actions.xml
+++ b/app/src/main/res/layout/voip_call_main_actions.xml
@@ -15,7 +15,7 @@
#EEF7F8
- #6C7A870xFF
+ #6C7A87
#F9F9F9
#EEF6F8
#949494
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 63d06b6bd..708040bc6 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -138,6 +138,23 @@
Debug logs URL copied into clipboard
Failed to upload debug logs
+ Calls
+ Use echo canceller
+ Prevents echo from being heard by remote end
+ Route audio to bluetooth device, if any
+ Enable video
+ Start video calls
+ Always initiate calls as video
+ Accept video calls
+ Always accept video calls
+ Use this device\'s ringtone
+ Vibrate while incoming call is ringing
+ Automatically start recording calls
+ Network
+ Use only Wi-Fi networks
+ User interface
+ Advanced settings
+
Manage account
Details
Add a picture