diff --git a/app/src/main/java/org/linphone/core/CorePreferences.kt b/app/src/main/java/org/linphone/core/CorePreferences.kt index 8753e9c69..7c6c2db1b 100644 --- a/app/src/main/java/org/linphone/core/CorePreferences.kt +++ b/app/src/main/java/org/linphone/core/CorePreferences.kt @@ -121,7 +121,7 @@ class CorePreferences @UiThread constructor(private val context: Context) { get() = context.filesDir.absolutePath + "/assistant_third_party_default_values" @get:AnyThread - private val ringtonesPath: String + val ringtonesPath: String get() = context.filesDir.absolutePath + "/share/sounds/linphone/rings/" @get:AnyThread 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 aa0ea0122..c56d2fca7 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 @@ -4,21 +4,40 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.AdapterView +import android.widget.ArrayAdapter import androidx.annotation.UiThread import androidx.navigation.navGraphViewModels import org.linphone.R +import org.linphone.core.tools.Log import org.linphone.databinding.SettingsFragmentBinding import org.linphone.ui.main.fragment.GenericFragment import org.linphone.ui.main.settings.viewmodel.SettingsViewModel @UiThread class SettingsFragment : GenericFragment() { + companion object { + private const val TAG = "[Settings Fragment]" + } + private lateinit var binding: SettingsFragmentBinding private val viewModel: SettingsViewModel by navGraphViewModels( R.id.main_nav_graph ) + private val ringtoneListener = object : AdapterView.OnItemSelectedListener { + override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { + val path = viewModel.availableRingtonesPaths[position] + val label = viewModel.availableRingtonesNames[position] + Log.i("$TAG Selected ringtone is now [$label] ($path)") + viewModel.setRingtone(path) + } + + override fun onNothingSelected(parent: AdapterView<*>?) { + } + } + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -36,5 +55,24 @@ class SettingsFragment : GenericFragment() { binding.setBackClickListener { goBack() } + + // Ringtone related + val ringtonesAdapter = ArrayAdapter( + requireContext(), + R.layout.drop_down_item, + viewModel.availableRingtonesNames + ) + ringtonesAdapter.setDropDownViewResource(R.layout.assistant_transport_dropdown_cell) + binding.deviceRingtoneSpinner.adapter = ringtonesAdapter + + viewModel.selectedRingtone.observe(viewLifecycleOwner) { ringtone -> + binding.deviceRingtoneSpinner.setSelection( + viewModel.availableRingtonesPaths.indexOf( + ringtone + ) + ) + } + + binding.deviceRingtoneSpinner.onItemSelectedListener = ringtoneListener } } 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 index 54b7ca6d2..31ae13353 100644 --- 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 @@ -23,9 +23,13 @@ import android.os.Vibrator import androidx.annotation.UiThread import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel +import java.io.File +import java.util.Locale import org.linphone.LinphoneApplication.Companion.coreContext import org.linphone.LinphoneApplication.Companion.corePreferences +import org.linphone.R import org.linphone.core.tools.Log +import org.linphone.utils.AppUtils class SettingsViewModel @UiThread constructor() : ViewModel() { companion object { @@ -42,7 +46,9 @@ class SettingsViewModel @UiThread constructor() : ViewModel() { val videoEnabled = MutableLiveData() val autoInitiateVideoCalls = MutableLiveData() val autoAcceptVideoRequests = MutableLiveData() - val useDeviceRingtone = MutableLiveData() + val availableRingtonesPaths = arrayListOf() + val availableRingtonesNames = arrayListOf() + val selectedRingtone = MutableLiveData() val vibrateDuringIncomingCall = MutableLiveData() val autoRecordCalls = MutableLiveData() @@ -64,17 +70,19 @@ class SettingsViewModel @UiThread constructor() : ViewModel() { Log.w("$TAG Device doesn't seem to have a vibrator, hiding related setting") } + computeAvailableRingtones() + 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) + selectedRingtone.postValue(core.ring.orEmpty()) } } @@ -129,11 +137,9 @@ class SettingsViewModel @UiThread constructor() : ViewModel() { } @UiThread - fun toggleDeviceRingtone() { - val newValue = useDeviceRingtone.value == false + fun setRingtone(ringtone: String) { coreContext.postOnCoreThread { core -> - core.ring = if (newValue) null else corePreferences.defaultRingtonePath - useDeviceRingtone.postValue(newValue) + core.ring = ringtone } } @@ -178,4 +184,25 @@ class SettingsViewModel @UiThread constructor() : ViewModel() { fun toggleUserInterfaceExpand() { expandUserInterface.value = expandUserInterface.value == false } + + @UiThread + private fun computeAvailableRingtones() { + availableRingtonesNames.add( + AppUtils.getString(R.string.settings_calls_use_device_ringtone_label) + ) + availableRingtonesPaths.add("") + + val directory = File(corePreferences.ringtonesPath) + val files = directory.listFiles() + for (ringtone in files.orEmpty()) { + if (ringtone.absolutePath.endsWith(".mkv")) { + val name = ringtone.name + .substringBefore(".") + .replace("_", " ") + .capitalize(Locale.getDefault()) + availableRingtonesNames.add(name) + availableRingtonesPaths.add(ringtone.absolutePath) + } + } + } } diff --git a/app/src/main/res/layout/help_fragment.xml b/app/src/main/res/layout/help_fragment.xml index 2a9cd12e4..4161b26ab 100644 --- a/app/src/main/res/layout/help_fragment.xml +++ b/app/src/main/res/layout/help_fragment.xml @@ -279,6 +279,9 @@ android:layout_marginEnd="16dp" android:layout_marginTop="24dp" android:text="@string/help_troubleshooting_title" + android:drawableEnd="@drawable/caret_right" + android:drawablePadding="8dp" + app:drawableTint="@color/gray_main2_500" app:layout_constraintTop_toBottomOf="@id/advanced_title" app:layout_constraintStart_toEndOf="@id/debug_icon" app:layout_constraintEnd_toEndOf="parent"/> diff --git a/app/src/main/res/layout/settings_fragment.xml b/app/src/main/res/layout/settings_fragment.xml index 505a5c1ac..24a77b0e3 100644 --- a/app/src/main/res/layout/settings_fragment.xml +++ b/app/src/main/res/layout/settings_fragment.xml @@ -285,32 +285,42 @@ + app:layout_constraintEnd_toEndOf="@id/calls_background"/> - + + + android:layout_marginEnd="20dp" + android:src="@drawable/caret_down" + app:layout_constraintTop_toTopOf="@id/device_ringtone_spinner" + app:layout_constraintBottom_toBottomOf="@id/device_ringtone_spinner" + app:layout_constraintEnd_toEndOf="@id/device_ringtone_spinner"/> + app:layout_constraintTop_toBottomOf="@id/device_ringtone_spinner" /> Always initiate calls as video Accept video calls Always accept video calls - Use this device\'s ringtone + Choose your ringtone: + Use this device\'s ringtone Vibrate while incoming call is ringing Automatically start recording calls Network