This commit is contained in:
Sylvain Berfini 2023-08-24 16:17:48 +02:00
parent 409e658f80
commit a02896333a
37 changed files with 211 additions and 58 deletions

View file

@ -1,6 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<!-- To be able to display contacts list & match calling/called numbers -->
<uses-permission android:name="android.permission.READ_CONTACTS" />

View file

@ -41,7 +41,7 @@ import org.linphone.utils.ImageUtils
class ContactsManager @UiThread constructor(context: Context) {
companion object {
const val TAG = "[Contacts Manager]"
private const val TAG = "[Contacts Manager]"
}
val contactAvatar: IconCompat

View file

@ -42,7 +42,7 @@ import org.linphone.utils.LinphoneUtils
class CoreContext @UiThread constructor(val context: Context) : HandlerThread("Core Thread") {
companion object {
const val TAG = "[Core Context]"
private const val TAG = "[Core Context]"
}
lateinit var core: Core

View file

@ -26,7 +26,7 @@ import org.linphone.core.tools.service.CoreService
class CoreForegroundService : CoreService() {
companion object {
const val TAG = "[Core Foreground Service]"
private const val TAG = "[Core Foreground Service]"
}
override fun onCreate() {

View file

@ -28,7 +28,7 @@ import org.linphone.core.tools.Log
class NotificationBroadcastReceiver : BroadcastReceiver() {
companion object {
const val TAG = "[NotificationBroadcastReceiver]"
private const val TAG = "[NotificationBroadcastReceiver]"
}
override fun onReceive(context: Context, intent: Intent) {

View file

@ -56,7 +56,7 @@ import org.linphone.utils.LinphoneUtils
class NotificationsManager @MainThread constructor(private val context: Context) {
companion object {
const val TAG = "[Notifications Manager]"
private const val TAG = "[Notifications Manager]"
const val INTENT_HANGUP_CALL_NOTIF_ACTION = "org.linphone.HANGUP_CALL_ACTION"
const val INTENT_ANSWER_CALL_NOTIF_ACTION = "org.linphone.ANSWER_CALL_ACTION"

View file

@ -37,7 +37,7 @@ import org.linphone.utils.Event
class AssistantViewModel @UiThread constructor() : ViewModel() {
companion object {
const val TAG = "[Assistant ViewModel]"
private const val TAG = "[Assistant ViewModel]"
}
val username = MutableLiveData<String>()

View file

@ -52,7 +52,7 @@ import org.linphone.utils.Event
@UiThread
class CallsListFragment : GenericFragment() {
companion object {
const val TAG = "[Calls List Fragment]"
private const val TAG = "[Calls List Fragment]"
}
private lateinit var binding: CallsListFragmentBinding

View file

@ -37,7 +37,7 @@ import org.linphone.ui.main.model.isInSecureMode
class SuggestionsListViewModel @UiThread constructor() : ViewModel() {
companion object {
const val TAG = "[Suggestions List ViewModel]"
private const val TAG = "[Suggestions List ViewModel]"
}
val suggestionsList = MutableLiveData<ArrayList<ContactAvatarModel>>()

View file

@ -49,7 +49,7 @@ import org.linphone.utils.Event
@UiThread
class ContactFragment : GenericFragment() {
companion object {
const val TAG = "[Contact Fragment]"
private const val TAG = "[Contact Fragment]"
}
private lateinit var binding: ContactFragmentBinding

View file

@ -42,7 +42,7 @@ import org.linphone.utils.Event
@UiThread
class ContactsListFragment : GenericFragment() {
companion object {
const val TAG = "[Contacts List Fragment]"
private const val TAG = "[Contacts List Fragment]"
}
private lateinit var binding: ContactsListFragmentBinding

View file

@ -50,7 +50,7 @@ import org.linphone.utils.FileUtils
@UiThread
class EditContactFragment : GenericFragment() {
companion object {
const val TAG = "[Edit Contact Fragment]"
private const val TAG = "[Edit Contact Fragment]"
}
private lateinit var binding: ContactNewOrEditFragmentBinding

View file

@ -50,7 +50,7 @@ import org.linphone.utils.FileUtils
@UiThread
class NewContactFragment : GenericFragment() {
companion object {
const val TAG = "[New Contact Fragment]"
private const val TAG = "[New Contact Fragment]"
}
private lateinit var binding: ContactNewOrEditFragmentBinding

View file

@ -32,7 +32,7 @@ import org.linphone.utils.LinphoneUtils
class ContactAvatarModel @WorkerThread constructor(val friend: Friend) {
companion object {
const val TAG = "[Contact Avatar Model]"
private const val TAG = "[Contact Avatar Model]"
}
val id = friend.refKey

View file

@ -34,7 +34,7 @@ import org.linphone.utils.FileUtils
class ContactNewOrEditViewModel @UiThread constructor() : ViewModel() {
companion object {
const val TAG = "[Contact New/Edit View Model]"
private const val TAG = "[Contact New/Edit View Model]"
}
private lateinit var friend: Friend

View file

@ -41,7 +41,7 @@ import org.linphone.utils.PhoneNumberUtils
class ContactViewModel @UiThread constructor() : ViewModel() {
companion object {
const val TAG = "[Contact ViewModel]"
private const val TAG = "[Contact ViewModel]"
}
val contact = MutableLiveData<ContactAvatarModel>()

View file

@ -37,7 +37,7 @@ import org.linphone.ui.main.model.isInSecureMode
class ContactsListViewModel @UiThread constructor() : ViewModel() {
companion object {
const val TAG = "[Contacts List ViewModel]"
private const val TAG = "[Contacts List ViewModel]"
}
val contactsList = MutableLiveData<ArrayList<ContactAvatarModel>>()

View file

@ -33,18 +33,22 @@ import org.linphone.ui.main.viewmodel.SharedMainViewModel
@UiThread
abstract class GenericFragment : Fragment() {
companion object {
private const val TAG = "[Generic Fragment]"
}
protected lateinit var sharedViewModel: SharedMainViewModel
private val onBackPressedCallback = object : OnBackPressedCallback(false) {
override fun handleOnBackPressed() {
try {
val navController = findNavController()
Log.d("[Generic Fragment] ${getFragmentRealClassName()} handleOnBackPressed")
Log.d("$TAG ${getFragmentRealClassName()} handleOnBackPressed")
if (!navController.popBackStack()) {
Log.d("[Generic Fragment] ${getFragmentRealClassName()} couldn't pop")
Log.d("$TAG ${getFragmentRealClassName()} couldn't pop")
if (!navController.navigateUp()) {
Log.d(
"[Generic Fragment] ${getFragmentRealClassName()} couldn't navigate up"
"$TAG ${getFragmentRealClassName()} couldn't navigate up"
)
// Disable this callback & start a new back press event
isEnabled = false
@ -53,7 +57,7 @@ abstract class GenericFragment : Fragment() {
}
} catch (ise: IllegalStateException) {
Log.e(
"[Generic Fragment] ${getFragmentRealClassName()}.handleOnBackPressed() Can't go back: $ise"
"$TAG ${getFragmentRealClassName()}.handleOnBackPressed() Can't go back: $ise"
)
}
}
@ -68,7 +72,7 @@ abstract class GenericFragment : Fragment() {
sharedViewModel.isSlidingPaneSlideable.observe(viewLifecycleOwner) {
Log.d(
"[Generic Fragment] ${getFragmentRealClassName()} shared main VM sliding pane has changed"
"$TAG ${getFragmentRealClassName()} shared main VM sliding pane has changed"
)
onBackPressedCallback.isEnabled = backPressedCallBackEnabled()
}
@ -90,17 +94,17 @@ abstract class GenericFragment : Fragment() {
try {
requireActivity().onBackPressedDispatcher.onBackPressed()
} catch (ise: IllegalStateException) {
Log.w("[Generic Fragment] ${getFragmentRealClassName()}.goBack() can't go back: $ise")
Log.w("$TAG ${getFragmentRealClassName()}.goBack() can't go back: $ise")
onBackPressedCallback.handleOnBackPressed()
}
}
private fun setupBackPressCallback() {
Log.d("[Generic Fragment] ${getFragmentRealClassName()} setupBackPressCallback")
Log.d("$TAG ${getFragmentRealClassName()} setupBackPressCallback")
val backButton = view?.findViewById<ImageView>(R.id.back)
if (backButton != null && backButton.visibility == View.VISIBLE) {
Log.d("[Generic Fragment] ${getFragmentRealClassName()} found back button")
Log.d("$TAG ${getFragmentRealClassName()} found back button")
// If popping navigation back stack entry would bring us to an "empty" fragment
// then don't do it if sliding pane layout isn't "flat"
onBackPressedCallback.isEnabled = backPressedCallBackEnabled()
@ -123,14 +127,14 @@ abstract class GenericFragment : Fragment() {
val isSlidingPaneFlat = sharedViewModel.isSlidingPaneSlideable.value == false
Log.d(
"[Generic Fragment] ${getFragmentRealClassName()} isSlidingPaneFlat ? $isSlidingPaneFlat"
"$TAG ${getFragmentRealClassName()} isSlidingPaneFlat ? $isSlidingPaneFlat"
)
val isPreviousFragmentEmpty = findNavController().previousBackStackEntry?.destination?.id == R.id.emptyFragment
Log.d(
"[Generic Fragment] ${getFragmentRealClassName()} isPreviousFragmentEmpty ? $isPreviousFragmentEmpty"
"$TAG ${getFragmentRealClassName()} isPreviousFragmentEmpty ? $isPreviousFragmentEmpty"
)
val popBackStack = isSlidingPaneFlat || !isPreviousFragmentEmpty
Log.d("[Generic Fragment] ${getFragmentRealClassName()} popBackStack ? $popBackStack")
Log.d("$TAG ${getFragmentRealClassName()} popBackStack ? $popBackStack")
return popBackStack
}
}

View file

@ -18,7 +18,6 @@ import kotlinx.coroutines.withContext
import org.linphone.R
import org.linphone.core.tools.Log
import org.linphone.databinding.AccountProfileFragmentBinding
import org.linphone.ui.main.contacts.fragment.EditContactFragment
import org.linphone.ui.main.fragment.GenericFragment
import org.linphone.ui.main.settings.viewmodel.AccountProfileViewModel
import org.linphone.utils.FileUtils
@ -26,7 +25,7 @@ import org.linphone.utils.FileUtils
@UiThread
class AccountProfileFragment : GenericFragment() {
companion object {
const val TAG = "[Account Profile Fragment]"
private const val TAG = "[Account Profile Fragment]"
}
private lateinit var binding: AccountProfileFragmentBinding
@ -54,12 +53,12 @@ class AccountProfileFragment : GenericFragment() {
}
} else {
Log.e(
"${EditContactFragment.TAG} Failed to copy file from [$uri] to [${localFileName.absolutePath}]"
"$TAG Failed to copy file from [$uri] to [${localFileName.absolutePath}]"
)
}
}
} else {
Log.w("${EditContactFragment.TAG} No picture picked")
Log.w("$TAG No picture picked")
}
}

View file

@ -13,7 +13,7 @@ import org.linphone.utils.FileUtils
class AccountProfileViewModel @UiThread constructor() : ViewModel() {
companion object {
const val TAG = "[Account Profile ViewModel]"
private const val TAG = "[Account Profile ViewModel]"
}
val picturePath = MutableLiveData<String>()

View file

@ -32,7 +32,7 @@ import org.linphone.core.tools.Log
class BottomNavBarViewModel @UiThread constructor() : ViewModel() {
companion object {
const val TAG = "[Bottom Navigation Bar ViewModel]"
private const val TAG = "[Bottom Navigation Bar ViewModel]"
}
val contactsSelected = MutableLiveData<Boolean>()

View file

@ -32,7 +32,7 @@ import org.linphone.utils.Event
class DrawerMenuViewModel @UiThread constructor() : ViewModel() {
companion object {
const val TAG = "[Drawer Menu ViewModel]"
private const val TAG = "[Drawer Menu ViewModel]"
}
val accounts = MutableLiveData<ArrayList<AccountModel>>()

View file

@ -30,7 +30,7 @@ import org.linphone.utils.Event
class TopBarViewModel @UiThread constructor() : ViewModel() {
companion object {
const val TAG = "[Top Bar ViewModel]"
private const val TAG = "[Top Bar ViewModel]"
}
val title = MutableLiveData<String>()

View file

@ -26,6 +26,8 @@ import androidx.annotation.UiThread
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
@ -33,17 +35,24 @@ import androidx.navigation.findNavController
import androidx.navigation.fragment.findNavController
import org.linphone.LinphoneApplication
import org.linphone.R
import org.linphone.core.tools.Log
import org.linphone.databinding.VoipActivityBinding
import org.linphone.ui.voip.fragment.ActiveCallFragmentDirections
import org.linphone.ui.voip.fragment.IncomingCallFragmentDirections
import org.linphone.ui.voip.fragment.OutgoingCallFragmentDirections
import org.linphone.ui.voip.viewmodel.CallsViewModel
import org.linphone.ui.voip.viewmodel.SharedCallViewModel
import org.linphone.utils.slideInToastFromTopForDuration
@UiThread
class VoipActivity : AppCompatActivity() {
companion object {
private const val TAG = "[VoIP Activity]"
}
private lateinit var binding: VoipActivityBinding
private lateinit var sharedViewModel: SharedCallViewModel
private lateinit var callsViewModel: CallsViewModel
override fun onCreate(savedInstanceState: Bundle?) {
@ -63,6 +72,10 @@ class VoipActivity : AppCompatActivity() {
binding = DataBindingUtil.setContentView(this, R.layout.voip_activity)
binding.lifecycleOwner = this
sharedViewModel = run {
ViewModelProvider(this)[SharedCallViewModel::class.java]
}
callsViewModel = run {
ViewModelProvider(this)[CallsViewModel::class.java]
}
@ -100,6 +113,12 @@ class VoipActivity : AppCompatActivity() {
finish()
}
}
sharedViewModel.toggleFullScreenEvent.observe(this) {
it.consume { hide ->
hideUI(hide)
}
}
}
fun showBlueToast(message: String, @DrawableRes icon: Int) {
@ -109,4 +128,20 @@ class VoipActivity : AppCompatActivity() {
val target = binding.blueToast.root
target.slideInToastFromTopForDuration(binding.root as ViewGroup, lifecycleScope)
}
private fun hideUI(hide: Boolean) {
Log.i("$TAG Switching full screen mode to ${if (hide) "ON" else "OFF"}")
val windowInsetsCompat = WindowInsetsControllerCompat(window, window.decorView)
if (hide) {
WindowCompat.setDecorFitsSystemWindows(window, false)
windowInsetsCompat.let {
it.hide(WindowInsetsCompat.Type.systemBars())
it.systemBarsBehavior =
WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
}
} else {
windowInsetsCompat.show(WindowInsetsCompat.Type.systemBars())
WindowCompat.setDecorFitsSystemWindows(window, true)
}
}
}

View file

@ -30,15 +30,16 @@ import androidx.lifecycle.ViewModelProvider
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.R
import org.linphone.databinding.VoipActiveCallFragmentBinding
import org.linphone.ui.main.fragment.GenericFragment
import org.linphone.ui.voip.VoipActivity
import org.linphone.ui.voip.model.ZrtpSasConfirmationDialogModel
import org.linphone.ui.voip.viewmodel.CurrentCallViewModel
import org.linphone.ui.voip.viewmodel.SharedCallViewModel
import org.linphone.utils.AppUtils
import org.linphone.utils.DialogUtils
import org.linphone.utils.Event
@UiThread
class ActiveCallFragment : GenericFragment() {
class ActiveCallFragment : GenericCallFragment() {
private lateinit var binding: VoipActiveCallFragmentBinding
private lateinit var callViewModel: CurrentCallViewModel
@ -89,11 +90,12 @@ class ActiveCallFragment : GenericFragment() {
binding.lifecycleOwner = viewLifecycleOwner
binding.viewModel = callViewModel
callViewModel.toggleExtraActionMenuVisibilityEvent.observe(viewLifecycleOwner) {
/*it.consume { opened ->
val visibility = if (opened) View.VISIBLE else View.GONE
binding.extraActions.slideInExtraActionsMenu(binding.root as ViewGroup, visibility)
}*/
sharedViewModel = requireActivity().run {
ViewModelProvider(this)[SharedCallViewModel::class.java]
}
callViewModel.fullScreenMode.observe(viewLifecycleOwner) { hide ->
sharedViewModel.toggleFullScreenEvent.value = Event(hide)
}
callViewModel.isRemoteDeviceTrusted.observe(viewLifecycleOwner) { trusted ->

View file

@ -0,0 +1,44 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.linphone.ui.voip.fragment
import android.os.Bundle
import android.view.View
import androidx.annotation.UiThread
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import org.linphone.ui.voip.viewmodel.SharedCallViewModel
@UiThread
abstract class GenericCallFragment : Fragment() {
companion object {
private const val TAG = "[Generic Call Fragment]"
}
protected lateinit var sharedViewModel: SharedCallViewModel
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
sharedViewModel = requireActivity().run {
ViewModelProvider(this)[SharedCallViewModel::class.java]
}
}
}

View file

@ -31,8 +31,8 @@ class ZrtpSasConfirmationDialogModel @UiThread constructor(
private val authTokenToListen: String
) : ViewModel() {
companion object {
const val TAG = "[ZRTP SAS Confirmation Dialog]"
const val alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
private const val TAG = "[ZRTP SAS Confirmation Dialog]"
private const val alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
}
val message = MutableLiveData<String>()

View file

@ -19,9 +19,12 @@
*/
package org.linphone.ui.voip.viewmodel
import android.Manifest
import android.animation.ValueAnimator
import android.content.pm.PackageManager
import androidx.annotation.UiThread
import androidx.annotation.WorkerThread
import androidx.core.app.ActivityCompat
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import java.util.Locale
@ -38,7 +41,7 @@ import org.linphone.utils.LinphoneUtils
class CurrentCallViewModel @UiThread constructor() : ViewModel() {
companion object {
const val TAG = "[Current Call ViewModel]"
private const val TAG = "[Current Call ViewModel]"
}
val contact = MutableLiveData<ContactAvatarModel>()
@ -49,6 +52,8 @@ class CurrentCallViewModel @UiThread constructor() : ViewModel() {
val isVideoEnabled = MutableLiveData<Boolean>()
val showSwitchCamera = MutableLiveData<Boolean>()
val isOutgoing = MutableLiveData<Boolean>()
val isMicrophoneMuted = MutableLiveData<Boolean>()
@ -90,8 +95,6 @@ class CurrentCallViewModel @UiThread constructor() : ViewModel() {
}
}
val toggleExtraActionMenuVisibilityEvent = MutableLiveData<Event<Boolean>>()
private lateinit var call: Call
private val callListener = object : CallListenerStub() {
@ -105,7 +108,13 @@ class CurrentCallViewModel @UiThread constructor() : ViewModel() {
if (LinphoneUtils.isCallOutgoing(call.state)) {
isVideoEnabled.postValue(call.params.isVideoEnabled)
} else {
isVideoEnabled.postValue(call.currentParams.isVideoEnabled)
val videoEnabled = call.currentParams.isVideoEnabled
isVideoEnabled.postValue(videoEnabled)
// Toggle full screen OFF when remote disables video
if (!videoEnabled && fullScreenMode.value == true) {
fullScreenMode.postValue(false)
}
}
}
}
@ -127,6 +136,8 @@ class CurrentCallViewModel @UiThread constructor() : ViewModel() {
} else {
Log.e("$TAG Failed to find outgoing call!")
}
showSwitchCamera.postValue(coreContext.showSwitchCameraButton())
}
}
@ -168,7 +179,14 @@ class CurrentCallViewModel @UiThread constructor() : ViewModel() {
@UiThread
fun toggleMuteMicrophone() {
// TODO: check record audio permission
if (ActivityCompat.checkSelfPermission(
coreContext.context,
Manifest.permission.RECORD_AUDIO
) != PackageManager.PERMISSION_GRANTED
) {
// TODO: request record audio permission
return
}
coreContext.postOnCoreThread {
call.microphoneMuted = !call.microphoneMuted
isMicrophoneMuted.postValue(call.microphoneMuted)
@ -182,7 +200,14 @@ class CurrentCallViewModel @UiThread constructor() : ViewModel() {
@UiThread
fun toggleVideo() {
// TODO: check video permission
if (ActivityCompat.checkSelfPermission(
coreContext.context,
Manifest.permission.CAMERA
) != PackageManager.PERMISSION_GRANTED
) {
// TODO: request video permission
return
}
coreContext.postOnCoreThread { core ->
if (::call.isInitialized) {

View file

@ -0,0 +1,29 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.linphone.ui.voip.viewmodel
import androidx.annotation.UiThread
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import org.linphone.utils.Event
class SharedCallViewModel @UiThread constructor() : ViewModel() {
val toggleFullScreenEvent = MutableLiveData<Event<Boolean>>()
}

View file

@ -33,7 +33,7 @@ import org.linphone.core.tools.Log
class FileUtils {
companion object {
const val TAG = "[File Utils]"
private const val TAG = "[File Utils]"
@AnyThread
fun getProperFilePath(path: String): String {

View file

@ -34,7 +34,7 @@ import org.linphone.core.tools.Log
class ImageUtils {
companion object {
const val TAG = "[Image Utils]"
private const val TAG = "[Image Utils]"
@AnyThread
fun getRoundBitmapFromUri(

View file

@ -2,6 +2,8 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false"
android:drawable="@drawable/shape_round_in_call_disabled_button_background" />
<item android:state_pressed="true"
android:drawable="@drawable/shape_round_in_call_pressed_button_background" />
<item
android:drawable="@drawable/shape_round_in_call_button_background" />
</selector>

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
<size android:width="55dp" android:height="55dp" />
<solid android:color="@color/in_call_button_background_gray"/>
<solid android:color="@color/gray_5"/>
</shape>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
<size android:width="55dp" android:height="55dp" />
<solid android:color="@color/gray_4"/>
</shape>

View file

@ -82,6 +82,7 @@
android:padding="10dp"
android:src="@drawable/users_three"
android:background="@drawable/shape_orange_round"
app:tint="@color/white"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/search_bar" />

View file

@ -21,6 +21,7 @@
android:layout_height="24dp"
android:layout_marginStart="10dp"
android:adjustViewBounds="true"
android:visibility="@{viewModel.fullScreenMode ? View.GONE : View.VISIBLE}"
android:src="@{viewModel.isOutgoing ? @drawable/outgoing_call : @drawable/incoming_call, default=@drawable/outgoing_call}"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/call_direction_label"
@ -33,6 +34,7 @@
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginTop="7dp"
android:visibility="@{viewModel.fullScreenMode ? View.GONE : View.VISIBLE}"
android:text="@{viewModel.isOutgoing ? `Outgoing call` : `Incoming call`, default=`Outgoing call`}"
android:textColor="@color/white"
android:textSize="16sp"
@ -48,6 +50,7 @@
android:textColor="@color/white"
android:textSize="16sp"
android:text="@string/vertical_separator"
android:visibility="@{viewModel.fullScreenMode ? View.GONE : View.VISIBLE}"
app:layout_constraintStart_toEndOf="@id/call_direction_label"
app:layout_constraintTop_toTopOf="@id/call_direction_label"
app:layout_constraintBottom_toBottomOf="@id/call_direction_label"/>
@ -60,6 +63,7 @@
android:layout_marginStart="5dp"
android:textColor="@color/white"
android:textSize="16sp"
android:visibility="@{viewModel.fullScreenMode ? View.GONE : View.VISIBLE}"
app:layout_constraintStart_toEndOf="@id/separator"
app:layout_constraintTop_toTopOf="@id/call_direction_label"
app:layout_constraintBottom_toBottomOf="@id/call_direction_label"/>
@ -67,12 +71,12 @@
<ImageView
android:onClick="@{() -> viewModel.switchCamera()}"
android:id="@+id/switch_camera"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_width="34dp"
android:layout_height="34dp"
android:padding="5dp"
android:layout_marginEnd="10dp"
android:adjustViewBounds="true"
android:src="@drawable/camera_rotate"
android:visibility="@{viewModel.isVideoEnabled() ? View.VISIBLE : View.GONE}"
android:visibility="@{!viewModel.fullScreenMode &amp;&amp; viewModel.isVideoEnabled &amp;&amp; viewModel.showSwitchCamera ? View.VISIBLE : View.GONE}"
app:layout_constraintTop_toTopOf="@id/call_direction_label"
app:layout_constraintBottom_toBottomOf="@id/call_direction_label"
app:layout_constraintEnd_toEndOf="parent"
@ -176,6 +180,7 @@
android:layout_width="0dp"
android:layout_height="@dimen/in_call_all_actions_menu_height"
android:translationY="@{viewModel.extraActionsMenuTranslateY}"
android:visibility="@{viewModel.fullScreenMode ? View.GONE : View.VISIBLE}"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>

View file

@ -34,5 +34,4 @@
<color name="in_call_black">#070707</color>
<color name="in_call_gray">#2E3030</color>
<color name="in_call_button_background_gray">#4E4E4E</color>
</resources>