diff --git a/app/src/main/java/org/linphone/core/CoreContext.kt b/app/src/main/java/org/linphone/core/CoreContext.kt
index e660c91c9..779694aff 100644
--- a/app/src/main/java/org/linphone/core/CoreContext.kt
+++ b/app/src/main/java/org/linphone/core/CoreContext.kt
@@ -208,6 +208,14 @@ class CoreContext @UiThread constructor(val context: Context) : HandlerThread("C
return
}
+ val currentCall = core.currentCall
+ if (currentCall != null) {
+ Log.w(
+ "$TAG Found current call [${currentCall.remoteAddress.asStringUriOnly()}], pausing it first"
+ )
+ currentCall.pause()
+ }
+
val params = callParams ?: core.createCallParams(null)
if (params == null) {
val call = core.inviteAddress(address)
diff --git a/app/src/main/java/org/linphone/ui/main/viewmodel/AbstractTopBarViewModel.kt b/app/src/main/java/org/linphone/ui/main/viewmodel/AbstractTopBarViewModel.kt
index 5ec42bc6a..0a8c9d61a 100644
--- a/app/src/main/java/org/linphone/ui/main/viewmodel/AbstractTopBarViewModel.kt
+++ b/app/src/main/java/org/linphone/ui/main/viewmodel/AbstractTopBarViewModel.kt
@@ -24,13 +24,11 @@ import androidx.annotation.WorkerThread
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import org.linphone.LinphoneApplication.Companion.coreContext
-import org.linphone.R
import org.linphone.core.Call
import org.linphone.core.Core
import org.linphone.core.CoreListenerStub
import org.linphone.core.tools.Log
import org.linphone.ui.main.model.AccountModel
-import org.linphone.utils.AppUtils
import org.linphone.utils.Event
import org.linphone.utils.LinphoneUtils
@@ -169,7 +167,7 @@ open class AbstractTopBarViewModel @UiThread constructor() : ViewModel() {
callDisplayName.postValue(
contact?.name ?: LinphoneUtils.getDisplayName(currentCall.remoteAddress)
)
- callStatus.postValue(callStateToString(currentCall.state))
+ callStatus.postValue(LinphoneUtils.callStateToString(currentCall.state))
} else {
val firstCall = core.calls.firstOrNull()
if (firstCall != null) {
@@ -179,38 +177,10 @@ open class AbstractTopBarViewModel @UiThread constructor() : ViewModel() {
callDisplayName.postValue(
contact?.name ?: LinphoneUtils.getDisplayName(firstCall.remoteAddress)
)
- callStatus.postValue(callStateToString(firstCall.state))
+ callStatus.postValue(LinphoneUtils.callStateToString(firstCall.state))
}
}
Log.i("$TAG At least a call, asking fragment to change status bar color")
changeSystemTopBarColorToInCallEvent.postValue(Event(true))
}
-
- @WorkerThread
- private fun callStateToString(state: Call.State): String {
- return when (state) {
- Call.State.IncomingEarlyMedia, Call.State.IncomingReceived -> {
- AppUtils.getString(R.string.voip_call_state_incoming_received)
- }
- Call.State.OutgoingInit, Call.State.OutgoingProgress -> {
- AppUtils.getString(R.string.voip_call_state_outgoing_progress)
- }
- Call.State.OutgoingRinging, Call.State.OutgoingEarlyMedia -> {
- AppUtils.getString(R.string.voip_call_state_outgoing_ringing)
- }
- Call.State.Connected, Call.State.StreamsRunning, Call.State.Updating, Call.State.UpdatedByRemote -> {
- AppUtils.getString(R.string.voip_call_state_connected)
- }
- Call.State.Pausing, Call.State.Paused, Call.State.PausedByRemote -> {
- AppUtils.getString(R.string.voip_call_state_paused)
- }
- Call.State.End, Call.State.Released, Call.State.Error -> {
- AppUtils.getString(R.string.voip_call_state_ended)
- }
- else -> {
- // TODO: handle other states
- ""
- }
- }
- }
}
diff --git a/app/src/main/java/org/linphone/ui/voip/fragment/ActiveCallFragment.kt b/app/src/main/java/org/linphone/ui/voip/fragment/ActiveCallFragment.kt
index cb285f645..ddd21718b 100644
--- a/app/src/main/java/org/linphone/ui/voip/fragment/ActiveCallFragment.kt
+++ b/app/src/main/java/org/linphone/ui/voip/fragment/ActiveCallFragment.kt
@@ -118,6 +118,11 @@ class ActiveCallFragment : GenericCallFragment() {
findNavController().navigate(action)
}
+ binding.setCallsListClickListener {
+ val action = ActiveCallFragmentDirections.actionActiveCallFragmentToCallsListFragment()
+ findNavController().navigate(action)
+ }
+
sharedViewModel = requireActivity().run {
ViewModelProvider(this)[SharedCallViewModel::class.java]
}
diff --git a/app/src/main/java/org/linphone/ui/voip/fragment/CallsListFragment.kt b/app/src/main/java/org/linphone/ui/voip/fragment/CallsListFragment.kt
new file mode 100644
index 000000000..68065bb50
--- /dev/null
+++ b/app/src/main/java/org/linphone/ui/voip/fragment/CallsListFragment.kt
@@ -0,0 +1,62 @@
+/*
+ * 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.voip.fragment
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.navigation.fragment.findNavController
+import androidx.navigation.navGraphViewModels
+import org.linphone.R
+import org.linphone.databinding.VoipCallsListFragmentBinding
+import org.linphone.ui.voip.viewmodel.CallsViewModel
+
+class CallsListFragment : GenericCallFragment() {
+ companion object {
+ private const val TAG = "[Calls List Fragment]"
+ }
+
+ private lateinit var binding: VoipCallsListFragmentBinding
+
+ private val viewModel: CallsViewModel by navGraphViewModels(
+ R.id.voip_nav_graph
+ )
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+ binding = VoipCallsListFragmentBinding.inflate(layoutInflater)
+ return binding.root
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+
+ binding.lifecycleOwner = viewLifecycleOwner
+ binding.viewModel = viewModel
+
+ binding.setBackClickListener {
+ findNavController().popBackStack()
+ }
+ }
+}
diff --git a/app/src/main/java/org/linphone/ui/voip/model/CallModel.kt b/app/src/main/java/org/linphone/ui/voip/model/CallModel.kt
new file mode 100644
index 000000000..902ae262a
--- /dev/null
+++ b/app/src/main/java/org/linphone/ui/voip/model/CallModel.kt
@@ -0,0 +1,64 @@
+/*
+ * 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.voip.model
+
+import androidx.annotation.WorkerThread
+import androidx.lifecycle.MutableLiveData
+import org.linphone.LinphoneApplication.Companion.coreContext
+import org.linphone.core.Call
+import org.linphone.core.CallListenerStub
+import org.linphone.ui.main.contacts.model.ContactAvatarModel
+import org.linphone.utils.LinphoneUtils
+
+class CallModel @WorkerThread constructor(val call: Call) {
+ val displayName = MutableLiveData()
+
+ val state = MutableLiveData()
+
+ val isPaused = MutableLiveData()
+
+ val friend = coreContext.contactsManager.findContactByAddress(call.remoteAddress)
+
+ val contact = MutableLiveData()
+
+ private val callListener = object : CallListenerStub() {
+ override fun onStateChanged(call: Call, state: Call.State, message: String) {
+ this@CallModel.state.postValue(LinphoneUtils.callStateToString(state))
+ isPaused.postValue(LinphoneUtils.isCallPaused(state))
+ }
+ }
+
+ init {
+ call.addListener(callListener)
+
+ displayName.postValue(friend?.name ?: LinphoneUtils.getDisplayName(call.remoteAddress))
+ if (friend != null) {
+ contact.postValue(ContactAvatarModel(friend))
+ }
+
+ state.postValue(LinphoneUtils.callStateToString(call.state))
+ isPaused.postValue(LinphoneUtils.isCallPaused(call.state))
+ }
+
+ @WorkerThread
+ fun destroy() {
+ call.removeListener(callListener)
+ }
+}
diff --git a/app/src/main/java/org/linphone/ui/voip/viewmodel/CallsViewModel.kt b/app/src/main/java/org/linphone/ui/voip/viewmodel/CallsViewModel.kt
index a5063d875..11334ad7f 100644
--- a/app/src/main/java/org/linphone/ui/voip/viewmodel/CallsViewModel.kt
+++ b/app/src/main/java/org/linphone/ui/voip/viewmodel/CallsViewModel.kt
@@ -30,6 +30,7 @@ import org.linphone.core.Call
import org.linphone.core.Core
import org.linphone.core.CoreListenerStub
import org.linphone.core.tools.Log
+import org.linphone.ui.voip.model.CallModel
import org.linphone.utils.Event
class CallsViewModel @UiThread constructor() : ViewModel() {
@@ -41,6 +42,8 @@ class CallsViewModel @UiThread constructor() : ViewModel() {
private const val ALERT_NETWORK_TYPE_CELLULAR = "mobile"
}
+ val calls = MutableLiveData>()
+
val goToActiveCallEvent = MutableLiveData>()
val showIncomingCallEvent = MutableLiveData>()
@@ -94,6 +97,34 @@ class CallsViewModel @UiThread constructor() : ViewModel() {
state: Call.State,
message: String
) {
+ // Update calls list if needed
+ val found = calls.value.orEmpty().find {
+ it.call == call
+ }
+ if (found == null) {
+ if (state != Call.State.End && state != Call.State.Released && state != Call.State.Error) {
+ Log.i(
+ "$TAG Found a call [${call.remoteAddress.asStringUriOnly()}] not yet in calls list, let's add it"
+ )
+ val list = arrayListOf()
+ list.addAll(calls.value.orEmpty())
+ val model = CallModel(call)
+ list.add(model)
+ calls.postValue(list)
+ }
+ } else {
+ if (state == Call.State.End || state == Call.State.Released || state == Call.State.Error) {
+ Log.i(
+ "$TAG Call [${call.remoteAddress.asStringUriOnly()}] shouldn't be in calls list anymore, let's remove it"
+ )
+ val list = arrayListOf()
+ list.addAll(calls.value.orEmpty())
+ list.remove(found)
+ calls.postValue(list)
+ found.destroy()
+ }
+ }
+
if (call == core.currentCall || core.currentCall == null) {
Log.i(
"$TAG Current call [${call.remoteAddress.asStringUriOnly()}] state changed [$state]"
@@ -139,6 +170,13 @@ class CallsViewModel @UiThread constructor() : ViewModel() {
core.addListener(coreListener)
if (core.callsNb > 0) {
+ val list = arrayListOf()
+ for (call in core.calls) {
+ val model = CallModel(call)
+ list.add(model)
+ }
+ calls.postValue(list)
+
val currentCall = core.currentCall ?: core.calls.first()
when (currentCall.state) {
@@ -165,6 +203,7 @@ class CallsViewModel @UiThread constructor() : ViewModel() {
super.onCleared()
coreContext.postOnCoreThread { core ->
+ calls.value.orEmpty().forEach(CallModel::destroy)
core.removeListener(coreListener)
}
}
diff --git a/app/src/main/java/org/linphone/utils/LinphoneUtils.kt b/app/src/main/java/org/linphone/utils/LinphoneUtils.kt
index 26f15998a..558158c13 100644
--- a/app/src/main/java/org/linphone/utils/LinphoneUtils.kt
+++ b/app/src/main/java/org/linphone/utils/LinphoneUtils.kt
@@ -118,6 +118,14 @@ class LinphoneUtils {
}
}
+ @AnyThread
+ fun isCallPaused(callState: Call.State): Boolean {
+ return when (callState) {
+ Call.State.Pausing, Call.State.Paused, Call.State.PausedByRemote, Call.State.Resuming -> true
+ else -> false
+ }
+ }
+
@AnyThread
@IntegerRes
fun getIconResId(callStatus: Status, callDir: Dir): Int {
@@ -189,5 +197,33 @@ class LinphoneUtils {
remoteSipUri.clean()
return "${localSipUri.asStringUriOnly()}~${remoteSipUri.asStringUriOnly()}"
}
+
+ @WorkerThread
+ fun callStateToString(state: Call.State): String {
+ return when (state) {
+ Call.State.IncomingEarlyMedia, Call.State.IncomingReceived -> {
+ AppUtils.getString(R.string.voip_call_state_incoming_received)
+ }
+ Call.State.OutgoingInit, Call.State.OutgoingProgress -> {
+ AppUtils.getString(R.string.voip_call_state_outgoing_progress)
+ }
+ Call.State.OutgoingRinging, Call.State.OutgoingEarlyMedia -> {
+ AppUtils.getString(R.string.voip_call_state_outgoing_ringing)
+ }
+ Call.State.Connected, Call.State.StreamsRunning, Call.State.Updating, Call.State.UpdatedByRemote -> {
+ AppUtils.getString(R.string.voip_call_state_connected)
+ }
+ Call.State.Pausing, Call.State.Paused, Call.State.PausedByRemote -> {
+ AppUtils.getString(R.string.voip_call_state_paused)
+ }
+ Call.State.End, Call.State.Released, Call.State.Error -> {
+ AppUtils.getString(R.string.voip_call_state_ended)
+ }
+ else -> {
+ // TODO: handle other states
+ ""
+ }
+ }
+ }
}
}
diff --git a/app/src/main/res/drawable/shape_round_in_call_pressed_button_background.xml b/app/src/main/res/drawable/shape_round_in_call_pressed_button_background.xml
index f64d098d4..2ce614341 100644
--- a/app/src/main/res/drawable/shape_round_in_call_pressed_button_background.xml
+++ b/app/src/main/res/drawable/shape_round_in_call_pressed_button_background.xml
@@ -1,5 +1,5 @@
-
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/account_profile_fragment.xml b/app/src/main/res/layout/account_profile_fragment.xml
index 00424bd23..9ef9324a0 100644
--- a/app/src/main/res/layout/account_profile_fragment.xml
+++ b/app/src/main/res/layout/account_profile_fragment.xml
@@ -38,7 +38,7 @@
android:adjustViewBounds="true"
android:padding="15dp"
android:src="@drawable/caret_left"
- app:tint="@color/gray_main2_500"
+ app:tint="@color/orange_main_500"
app:layout_constraintBottom_toBottomOf="@id/title"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/title" />
@@ -83,7 +83,7 @@
android:id="@+id/avatar"
android:layout_width="@dimen/avatar_big_size"
android:layout_height="@dimen/avatar_big_size"
- android:layout_marginTop="21dp"
+ android:layout_marginTop="20dp"
android:adjustViewBounds="true"
android:background="@drawable/shape_circle_light_blue_background"
accountAvatar="@{viewModel.accountModel}"
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 a2bef6980..2b53789d9 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
@@ -32,7 +32,7 @@
android:adjustViewBounds="true"
android:padding="15dp"
android:src="@drawable/caret_left"
- app:tint="@color/gray_main2_500"
+ app:tint="@color/orange_main_500"
app:layout_constraintBottom_toBottomOf="@id/title"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/title" />
diff --git a/app/src/main/res/layout/call_fragment.xml b/app/src/main/res/layout/call_fragment.xml
index c778e6e4b..65b538da7 100644
--- a/app/src/main/res/layout/call_fragment.xml
+++ b/app/src/main/res/layout/call_fragment.xml
@@ -31,7 +31,7 @@
android:onClick="@{backClickListener}"
android:visibility="@{viewModel.showBackButton ? View.VISIBLE : View.GONE}"
android:src="@drawable/caret_left"
- app:tint="@color/gray_main2_500"
+ app:tint="@color/orange_main_500"
app:layout_constraintBottom_toBottomOf="@id/title"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/title"/>
diff --git a/app/src/main/res/layout/call_start_fragment.xml b/app/src/main/res/layout/call_start_fragment.xml
index e43ffac66..162783060 100644
--- a/app/src/main/res/layout/call_start_fragment.xml
+++ b/app/src/main/res/layout/call_start_fragment.xml
@@ -36,7 +36,7 @@
android:onClick="@{backClickListener}"
android:padding="15dp"
android:src="@drawable/caret_left"
- app:tint="@color/gray_main2_500"
+ app:tint="@color/orange_main_500"
app:layout_constraintBottom_toBottomOf="@id/title"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/title" />
diff --git a/app/src/main/res/layout/contact_fragment.xml b/app/src/main/res/layout/contact_fragment.xml
index 3a437950e..28308e001 100644
--- a/app/src/main/res/layout/contact_fragment.xml
+++ b/app/src/main/res/layout/contact_fragment.xml
@@ -34,7 +34,7 @@
android:onClick="@{backClickListener}"
android:visibility="@{viewModel.showBackButton ? View.VISIBLE : View.GONE}"
android:src="@drawable/caret_left"
- app:tint="@color/gray_main2_500"
+ app:tint="@color/orange_main_500"
app:layout_constraintBottom_toBottomOf="@id/invisible_title"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/invisible_title"/>
diff --git a/app/src/main/res/layout/contact_new_or_edit_fragment.xml b/app/src/main/res/layout/contact_new_or_edit_fragment.xml
index ee21db3dd..b15911b45 100644
--- a/app/src/main/res/layout/contact_new_or_edit_fragment.xml
+++ b/app/src/main/res/layout/contact_new_or_edit_fragment.xml
@@ -29,7 +29,7 @@
android:adjustViewBounds="true"
android:padding="15dp"
android:src="@drawable/caret_left"
- app:tint="@color/gray_main2_500"
+ app:tint="@color/orange_main_500"
app:layout_constraintBottom_toBottomOf="@id/title"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/title" />
diff --git a/app/src/main/res/layout/help_debug_fragment.xml b/app/src/main/res/layout/help_debug_fragment.xml
index 85356bc60..7ef11b539 100644
--- a/app/src/main/res/layout/help_debug_fragment.xml
+++ b/app/src/main/res/layout/help_debug_fragment.xml
@@ -26,7 +26,7 @@
android:adjustViewBounds="true"
android:padding="15dp"
android:src="@drawable/caret_left"
- app:tint="@color/gray_main2_500"
+ app:tint="@color/orange_main_500"
app:layout_constraintBottom_toBottomOf="@id/title"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/title" />
diff --git a/app/src/main/res/layout/help_fragment.xml b/app/src/main/res/layout/help_fragment.xml
index 98e7f86ba..2a9cd12e4 100644
--- a/app/src/main/res/layout/help_fragment.xml
+++ b/app/src/main/res/layout/help_fragment.xml
@@ -43,7 +43,7 @@
android:adjustViewBounds="true"
android:padding="15dp"
android:src="@drawable/caret_left"
- app:tint="@color/gray_main2_500"
+ app:tint="@color/orange_main_500"
app:layout_constraintBottom_toBottomOf="@id/title"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/title" />
diff --git a/app/src/main/res/layout/recordings_fragment.xml b/app/src/main/res/layout/recordings_fragment.xml
index bc2cca3f1..2092a8944 100644
--- a/app/src/main/res/layout/recordings_fragment.xml
+++ b/app/src/main/res/layout/recordings_fragment.xml
@@ -23,7 +23,7 @@
android:adjustViewBounds="true"
android:padding="15dp"
android:src="@drawable/caret_left"
- app:tint="@color/gray_main2_500"
+ app:tint="@color/orange_main_500"
app:layout_constraintBottom_toBottomOf="@id/title"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/title" />
diff --git a/app/src/main/res/layout/settings_fragment.xml b/app/src/main/res/layout/settings_fragment.xml
index 45f59c6d1..505a5c1ac 100644
--- a/app/src/main/res/layout/settings_fragment.xml
+++ b/app/src/main/res/layout/settings_fragment.xml
@@ -25,7 +25,7 @@
android:adjustViewBounds="true"
android:padding="15dp"
android:src="@drawable/caret_left"
- app:tint="@color/gray_main2_500"
+ app:tint="@color/orange_main_500"
app:layout_constraintBottom_toBottomOf="@id/title"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/title" />
diff --git a/app/src/main/res/layout/voip_call_list_cell.xml b/app/src/main/res/layout/voip_call_list_cell.xml
new file mode 100644
index 000000000..66c2d9d11
--- /dev/null
+++ b/app/src/main/res/layout/voip_call_list_cell.xml
@@ -0,0 +1,100 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/voip_calls_list_fragment.xml b/app/src/main/res/layout/voip_calls_list_fragment.xml
new file mode 100644
index 000000000..9787c5f65
--- /dev/null
+++ b/app/src/main/res/layout/voip_calls_list_fragment.xml
@@ -0,0 +1,61 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/navigation/voip_nav_graph.xml b/app/src/main/res/navigation/voip_nav_graph.xml
index f196f70da..4cfe435c2 100644
--- a/app/src/main/res/navigation/voip_nav_graph.xml
+++ b/app/src/main/res/navigation/voip_nav_graph.xml
@@ -54,6 +54,12 @@
app:enterAnim="@anim/slide_in"
app:popExitAnim="@anim/slide_out"
app:launchSingleTop="true" />
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index f6c7e411d..e363f057f 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -290,6 +290,8 @@
Paused
Ended
+ Calls list
+
Skip
Forgotten password?