From fb9acf8da4259b14718d799480eac5025d46187c Mon Sep 17 00:00:00 2001 From: Sylvain Berfini Date: Thu, 10 Aug 2023 14:39:18 +0200 Subject: [PATCH] Added green & blue toasts + animation (added copy number to clipboard feature) --- .../main/contacts/fragment/ContactFragment.kt | 31 +++- ...ontactNumberOrAddressMenuDialogFragment.kt | 8 +- .../ContactsListMenuDialogFragment.kt | 1 - .../ui/voip/viewmodel/CallViewModel.kt | 11 +- .../org/linphone/utils/AnimationsUtils.kt | 57 ++++++++ app/src/main/res/drawable/incoming_call.xml | 13 ++ ...dow.xml => shape_dialog_orange_shadow.xml} | 0 .../res/drawable/shape_toast_background.xml | 5 + .../res/drawable/shape_toast_blue_shadow.xml | 5 + .../res/drawable/shape_toast_green_shadow.xml | 5 + app/src/main/res/layout/contact_fragment.xml | 50 ++++--- .../layout/dialog_pick_number_or_address.xml | 2 +- app/src/main/res/layout/toast_blue.xml | 76 ++++++++++ app/src/main/res/layout/toast_green.xml | 77 ++++++++++ .../res/layout/voip_active_call_fragment.xml | 133 ++++++++++++++++++ .../main/res/layout/voip_call_bottom_bar.xml | 2 +- .../layout/voip_incoming_call_fragment.xml | 131 +++++++++++++++++ .../layout/voip_outgoing_call_fragment.xml | 5 +- app/src/main/res/values/dimen.xml | 2 +- app/src/main/res/values/styles.xml | 4 +- 20 files changed, 583 insertions(+), 35 deletions(-) create mode 100644 app/src/main/java/org/linphone/utils/AnimationsUtils.kt create mode 100644 app/src/main/res/drawable/incoming_call.xml rename app/src/main/res/drawable/{shape_orange_shadow.xml => shape_dialog_orange_shadow.xml} (100%) create mode 100644 app/src/main/res/drawable/shape_toast_background.xml create mode 100644 app/src/main/res/drawable/shape_toast_blue_shadow.xml create mode 100644 app/src/main/res/drawable/shape_toast_green_shadow.xml create mode 100644 app/src/main/res/layout/toast_blue.xml create mode 100644 app/src/main/res/layout/toast_green.xml create mode 100644 app/src/main/res/layout/voip_active_call_fragment.xml create mode 100644 app/src/main/res/layout/voip_incoming_call_fragment.xml diff --git a/app/src/main/java/org/linphone/ui/main/contacts/fragment/ContactFragment.kt b/app/src/main/java/org/linphone/ui/main/contacts/fragment/ContactFragment.kt index 7c6b1edd1..fe15fabd5 100644 --- a/app/src/main/java/org/linphone/ui/main/contacts/fragment/ContactFragment.kt +++ b/app/src/main/java/org/linphone/ui/main/contacts/fragment/ContactFragment.kt @@ -19,14 +19,19 @@ */ package org.linphone.ui.main.contacts.fragment +import android.content.ClipData +import android.content.ClipboardManager +import android.content.Context import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.core.view.doOnPreDraw import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.navArgs import androidx.transition.ChangeBounds +import org.linphone.R import org.linphone.core.tools.Log import org.linphone.databinding.ContactFragmentBinding import org.linphone.ui.main.contacts.model.NumberOrAddressPickerDialogModel @@ -34,6 +39,7 @@ import org.linphone.ui.main.contacts.viewmodel.ContactViewModel import org.linphone.ui.main.fragment.GenericFragment import org.linphone.utils.DialogUtils import org.linphone.utils.Event +import org.linphone.utils.slideInToastFromTop class ContactFragment : GenericFragment() { private lateinit var binding: ContactFragmentBinding @@ -91,9 +97,14 @@ class ContactFragment : GenericFragment() { viewModel.showLongPressMenuForNumberOrAddressEvent.observe(viewLifecycleOwner) { it.consume { model -> - val modalBottomSheet = ContactNumberOrAddressMenuDialogFragment() { + val modalBottomSheet = ContactNumberOrAddressMenuDialogFragment({ + // onDismiss model.selected.value = false - } + }, { + // onCopyNumberOrAddressToClipboard + copyNumberOrAddressToClipboard(model.displayedValue, model.isSip) + }) + modalBottomSheet.show( parentFragmentManager, ContactNumberOrAddressMenuDialogFragment.TAG @@ -106,8 +117,8 @@ class ContactFragment : GenericFragment() { val model = NumberOrAddressPickerDialogModel(viewModel) val dialog = DialogUtils.getNumberOrAddressPickerDialog(requireActivity(), model) - model.dismissEvent.observe(viewLifecycleOwner) { - it.consume { + model.dismissEvent.observe(viewLifecycleOwner) { event -> + event.consume { dialog.dismiss() } } @@ -116,4 +127,16 @@ class ContactFragment : GenericFragment() { } } } + + private fun copyNumberOrAddressToClipboard(value: String, isSip: Boolean) { + val clipboard = requireContext().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager + val label = if (isSip) "SIP address" else "Phone number" + clipboard.setPrimaryClip(ClipData.newPlainText(label, value)) + + binding.greenToast.message.text = "Numéro copié dans le presse-papier" + binding.greenToast.icon.setImageResource(R.drawable.check) + + val target = binding.greenToast.root + target.slideInToastFromTop(binding.root as ViewGroup, lifecycleScope) + } } diff --git a/app/src/main/java/org/linphone/ui/main/contacts/fragment/ContactNumberOrAddressMenuDialogFragment.kt b/app/src/main/java/org/linphone/ui/main/contacts/fragment/ContactNumberOrAddressMenuDialogFragment.kt index 12614b380..c8cac3473 100644 --- a/app/src/main/java/org/linphone/ui/main/contacts/fragment/ContactNumberOrAddressMenuDialogFragment.kt +++ b/app/src/main/java/org/linphone/ui/main/contacts/fragment/ContactNumberOrAddressMenuDialogFragment.kt @@ -28,7 +28,8 @@ import com.google.android.material.bottomsheet.BottomSheetDialogFragment import org.linphone.databinding.ContactNumberOrAddressLongPressMenuBinding class ContactNumberOrAddressMenuDialogFragment( - private val onDismiss: (() -> Unit)? = null + private val onDismiss: (() -> Unit)? = null, + private val onCopyNumberOrAddressToClipboard: (() -> Unit)? = null ) : BottomSheetDialogFragment() { companion object { const val TAG = "ContactNumberOrAddressMenuDialogFragment" @@ -51,6 +52,11 @@ class ContactNumberOrAddressMenuDialogFragment( ): View { val view = ContactNumberOrAddressLongPressMenuBinding.inflate(layoutInflater) + view.setCopyNumberOrAddressClickListener { + onCopyNumberOrAddressToClipboard?.invoke() + dismiss() + } + return view.root } } diff --git a/app/src/main/java/org/linphone/ui/main/contacts/fragment/ContactsListMenuDialogFragment.kt b/app/src/main/java/org/linphone/ui/main/contacts/fragment/ContactsListMenuDialogFragment.kt index 8660784d5..525234e7d 100644 --- a/app/src/main/java/org/linphone/ui/main/contacts/fragment/ContactsListMenuDialogFragment.kt +++ b/app/src/main/java/org/linphone/ui/main/contacts/fragment/ContactsListMenuDialogFragment.kt @@ -52,7 +52,6 @@ class ContactsListMenuDialogFragment( savedInstanceState: Bundle? ): View { val view = ContactsListLongPressMenuBinding.inflate(layoutInflater) - return view.root } } diff --git a/app/src/main/java/org/linphone/ui/voip/viewmodel/CallViewModel.kt b/app/src/main/java/org/linphone/ui/voip/viewmodel/CallViewModel.kt index eba3ed160..0c55e062f 100644 --- a/app/src/main/java/org/linphone/ui/voip/viewmodel/CallViewModel.kt +++ b/app/src/main/java/org/linphone/ui/voip/viewmodel/CallViewModel.kt @@ -30,12 +30,14 @@ class CallViewModel() : ViewModel() { const val TAG = "[Call ViewModel]" } - val videoEnabled = MutableLiveData() + val isVideoEnabled = MutableLiveData() + + val isOutgoing = MutableLiveData() private lateinit var call: Call init { - videoEnabled.value = false + isVideoEnabled.value = false coreContext.postOnCoreThread { core -> val currentCall = core.currentCall ?: core.calls.firstOrNull() @@ -45,10 +47,11 @@ class CallViewModel() : ViewModel() { Log.i("$TAG Found call [$call]") if (call.state == Call.State.StreamsRunning) { - videoEnabled.postValue(call.currentParams.isVideoEnabled) + isVideoEnabled.postValue(call.currentParams.isVideoEnabled) } else { - videoEnabled.postValue(call.params.isVideoEnabled) + isVideoEnabled.postValue(call.params.isVideoEnabled) } + isOutgoing.postValue(call.dir == Call.Dir.Outgoing) } else { Log.e("$TAG Failed to find outgoing call!") } diff --git a/app/src/main/java/org/linphone/utils/AnimationsUtils.kt b/app/src/main/java/org/linphone/utils/AnimationsUtils.kt new file mode 100644 index 000000000..a3d63f72a --- /dev/null +++ b/app/src/main/java/org/linphone/utils/AnimationsUtils.kt @@ -0,0 +1,57 @@ +/* + * 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.utils + +import android.view.Gravity +import android.view.View +import android.view.ViewGroup +import androidx.lifecycle.LifecycleCoroutineScope +import androidx.transition.Slide +import androidx.transition.Transition +import androidx.transition.TransitionManager +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext + +fun View.slideInToastFromTop( + root: ViewGroup, + lifecycleScope: LifecycleCoroutineScope, + duration: Long = 5000 +) { + val view = this + + val transition: Transition = Slide(Gravity.TOP) + transition.duration = 600 + transition.addTarget(view) + + TransitionManager.beginDelayedTransition(root, transition) + view.visibility = View.VISIBLE + + lifecycleScope.launch { + withContext(Dispatchers.IO) { + delay(duration) + withContext(Dispatchers.Main) { + TransitionManager.beginDelayedTransition(root, transition) + view.visibility = View.GONE + } + } + } +} diff --git a/app/src/main/res/drawable/incoming_call.xml b/app/src/main/res/drawable/incoming_call.xml new file mode 100644 index 000000000..180b3616d --- /dev/null +++ b/app/src/main/res/drawable/incoming_call.xml @@ -0,0 +1,13 @@ + + + diff --git a/app/src/main/res/drawable/shape_orange_shadow.xml b/app/src/main/res/drawable/shape_dialog_orange_shadow.xml similarity index 100% rename from app/src/main/res/drawable/shape_orange_shadow.xml rename to app/src/main/res/drawable/shape_dialog_orange_shadow.xml diff --git a/app/src/main/res/drawable/shape_toast_background.xml b/app/src/main/res/drawable/shape_toast_background.xml new file mode 100644 index 000000000..3906225f0 --- /dev/null +++ b/app/src/main/res/drawable/shape_toast_background.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_toast_blue_shadow.xml b/app/src/main/res/drawable/shape_toast_blue_shadow.xml new file mode 100644 index 000000000..3033c4346 --- /dev/null +++ b/app/src/main/res/drawable/shape_toast_blue_shadow.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_toast_green_shadow.xml b/app/src/main/res/drawable/shape_toast_green_shadow.xml new file mode 100644 index 000000000..29b2f4106 --- /dev/null +++ b/app/src/main/res/drawable/shape_toast_green_shadow.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/contact_fragment.xml b/app/src/main/res/layout/contact_fragment.xml index fa950bd72..893a61df2 100644 --- a/app/src/main/res/layout/contact_fragment.xml +++ b/app/src/main/res/layout/contact_fragment.xml @@ -17,7 +17,8 @@ + android:background="@color/white" + android:animateLayoutChanges="true"> + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_pick_number_or_address.xml b/app/src/main/res/layout/dialog_pick_number_or_address.xml index 97d78b087..deb3745c4 100644 --- a/app/src/main/res/layout/dialog_pick_number_or_address.xml +++ b/app/src/main/res/layout/dialog_pick_number_or_address.xml @@ -22,7 +22,7 @@ android:id="@+id/dialog_background_shadow" android:layout_width="0dp" android:layout_height="0dp" - android:src="@drawable/shape_orange_shadow" + android:src="@drawable/shape_dialog_orange_shadow" app:layout_constraintBottom_toBottomOf="@id/numbers_and_addresses" app:layout_constraintEnd_toEndOf="@id/dialog_background" app:layout_constraintStart_toStartOf="@id/dialog_background" diff --git a/app/src/main/res/layout/toast_blue.xml b/app/src/main/res/layout/toast_blue.xml new file mode 100644 index 000000000..b52f88075 --- /dev/null +++ b/app/src/main/res/layout/toast_blue.xml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/toast_green.xml b/app/src/main/res/layout/toast_green.xml new file mode 100644 index 000000000..fb8694e63 --- /dev/null +++ b/app/src/main/res/layout/toast_green.xml @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/voip_active_call_fragment.xml b/app/src/main/res/layout/voip_active_call_fragment.xml new file mode 100644 index 000000000..f9fc403e4 --- /dev/null +++ b/app/src/main/res/layout/voip_active_call_fragment.xml @@ -0,0 +1,133 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/voip_call_bottom_bar.xml b/app/src/main/res/layout/voip_call_bottom_bar.xml index 125017e42..562047206 100644 --- a/app/src/main/res/layout/voip_call_bottom_bar.xml +++ b/app/src/main/res/layout/voip_call_bottom_bar.xml @@ -34,7 +34,7 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/voip_outgoing_call_fragment.xml b/app/src/main/res/layout/voip_outgoing_call_fragment.xml index c73ccd7eb..868b09d2d 100644 --- a/app/src/main/res/layout/voip_outgoing_call_fragment.xml +++ b/app/src/main/res/layout/voip_outgoing_call_fragment.xml @@ -44,10 +44,11 @@ android:layout_width="24dp" android:layout_height="24dp" android:layout_marginEnd="10dp" - android:layout_marginTop="7dp" android:adjustViewBounds="true" android:src="@drawable/switch_camera" - app:layout_constraintTop_toTopOf="parent" + android:enabled="@{viewModel.isVideoEnabled()}" + app:layout_constraintTop_toTopOf="@id/call_direction_label" + app:layout_constraintBottom_toBottomOf="@id/call_direction_label" app:layout_constraintEnd_toEndOf="parent" app:tint="@color/white" /> diff --git a/app/src/main/res/values/dimen.xml b/app/src/main/res/values/dimen.xml index 80c3dbc43..4f6427125 100644 --- a/app/src/main/res/values/dimen.xml +++ b/app/src/main/res/values/dimen.xml @@ -8,6 +8,6 @@ 45dp 50dp - 72dp + 100dp 120dp \ No newline at end of file diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index bb05aedcf..0c8d6ab95 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -30,7 +30,7 @@