From 8226f6e1b343b7ae5c390f7c4941a606c5dbbc1b Mon Sep 17 00:00:00 2001 From: Sylvain Berfini Date: Wed, 13 Mar 2024 09:58:04 +0100 Subject: [PATCH] Factorized avatar + presence & trust badges --- .../linphone/contacts/AbstractAvatarModel.kt | 3 + .../org/linphone/contacts/ContactsManager.kt | 16 ++++ .../main/contacts/model/ContactAvatarModel.kt | 2 - .../linphone/ui/main/model/AccountModel.kt | 2 + .../org/linphone/utils/DataBindingUtils.kt | 57 ++++--------- .../shape_circle_trusted_contact_overlay.xml | 5 ++ .../shape_circle_unsafe_contact_overlay.xml | 5 ++ .../contact_new_or_edit_fragment.xml | 2 +- .../meeting_waiting_room_fragment.xml | 11 +-- app/src/main/res/layout/account_list_cell.xml | 26 ++---- .../res/layout/account_profile_fragment.xml | 29 ++----- .../account_profile_secure_mode_fragment.xml | 82 +------------------ .../res/layout/address_selected_list_cell.xml | 42 ++-------- .../layout/assistant_secure_mode_fragment.xml | 78 +----------------- .../call_active_conference_fragment.xml | 1 - .../main/res/layout/call_active_fragment.xml | 19 +---- .../call_conference_active_speaker_cell.xml | 29 ++----- ...all_conference_active_speaker_fragment.xml | 12 +-- .../call_conference_audio_only_cell.xml | 29 ++----- .../res/layout/call_conference_grid_cell.xml | 29 ++----- .../call_conference_participant_list_cell.xml | 27 ++---- .../main/res/layout/call_ended_fragment.xml | 19 +---- .../res/layout/call_incoming_fragment.xml | 19 +---- app/src/main/res/layout/call_list_cell.xml | 16 ++-- .../res/layout/call_outgoing_fragment.xml | 19 +---- .../main/res/layout/chat_bubble_incoming.xml | 36 ++------ .../main/res/layout/chat_bubble_outgoing.xml | 1 - .../res/layout/chat_conversation_fragment.xml | 36 ++------ .../main/res/layout/chat_info_fragment.xml | 38 ++------- app/src/main/res/layout/chat_list_cell.xml | 40 ++------- .../chat_message_bottom_sheet_list_cell.xml | 40 ++------- .../res/layout/chat_participant_list_cell.xml | 40 ++------- app/src/main/res/layout/contact_avatar.xml | 67 +++++++++++++++ .../main/res/layout/contact_avatar_big.xml | 66 +++++++++++++++ .../main/res/layout/contact_avatar_huge.xml | 55 +++++++++++++ .../main/res/layout/contact_avatar_medium.xml | 66 +++++++++++++++ .../main/res/layout/contact_avatar_small.xml | 66 +++++++++++++++ .../layout/contact_favourite_list_cell.xml | 42 ++-------- app/src/main/res/layout/contact_fragment.xml | 34 ++------ app/src/main/res/layout/contact_list_cell.xml | 40 ++------- .../layout/contact_new_or_edit_fragment.xml | 2 +- .../res/layout/history_contact_fragment.xml | 34 ++------ app/src/main/res/layout/history_list_cell.xml | 42 ++-------- .../layout/meeting_participant_list_cell.xml | 40 ++------- ...meeting_schedule_participant_list_cell.xml | 29 ++----- .../layout/meeting_waiting_room_fragment.xml | 11 +-- .../start_call_suggestion_list_cell.xml | 2 +- app/src/main/res/layout/top_bar.xml | 28 ++----- app/src/main/res/values-fr/strings.xml | 2 + app/src/main/res/values/dimen.xml | 3 +- app/src/main/res/values/strings.xml | 2 + app/src/main/res/values/styles.xml | 4 - 52 files changed, 580 insertions(+), 865 deletions(-) create mode 100644 app/src/main/res/drawable/shape_circle_trusted_contact_overlay.xml create mode 100644 app/src/main/res/drawable/shape_circle_unsafe_contact_overlay.xml create mode 100644 app/src/main/res/layout/contact_avatar.xml create mode 100644 app/src/main/res/layout/contact_avatar_big.xml create mode 100644 app/src/main/res/layout/contact_avatar_huge.xml create mode 100644 app/src/main/res/layout/contact_avatar_medium.xml create mode 100644 app/src/main/res/layout/contact_avatar_small.xml diff --git a/app/src/main/java/org/linphone/contacts/AbstractAvatarModel.kt b/app/src/main/java/org/linphone/contacts/AbstractAvatarModel.kt index e6b1dde4c..30232d800 100644 --- a/app/src/main/java/org/linphone/contacts/AbstractAvatarModel.kt +++ b/app/src/main/java/org/linphone/contacts/AbstractAvatarModel.kt @@ -1,6 +1,7 @@ package org.linphone.contacts import androidx.lifecycle.MutableLiveData +import org.linphone.core.ConsolidatedPresence import org.linphone.core.SecurityLevel abstract class AbstractAvatarModel { @@ -21,4 +22,6 @@ abstract class AbstractAvatarModel { val defaultToConferenceIcon = MutableLiveData() val skipInitials = MutableLiveData() + + val presenceStatus = MutableLiveData() } diff --git a/app/src/main/java/org/linphone/contacts/ContactsManager.kt b/app/src/main/java/org/linphone/contacts/ContactsManager.kt index 35479133d..cfd2fcddf 100644 --- a/app/src/main/java/org/linphone/contacts/ContactsManager.kt +++ b/app/src/main/java/org/linphone/contacts/ContactsManager.kt @@ -42,6 +42,7 @@ import kotlinx.coroutines.cancel import kotlinx.coroutines.delay import kotlinx.coroutines.launch import org.linphone.LinphoneApplication.Companion.coreContext +import org.linphone.core.Account import org.linphone.core.Address import org.linphone.core.ConferenceInfo import org.linphone.core.Core @@ -134,6 +135,21 @@ class ContactsManager @UiThread constructor() { Log.i("$TAG Friend list [${friendList.displayName}] removed") friendList.removeListener(friendListListener) } + + @WorkerThread + override fun onDefaultAccountChanged(core: Core, account: Account?) { + Log.i("$TAG Default account changed, update all contact models showTrust value") + val showTrust = account?.isInSecureMode() + knownContactsAvatarsMap.forEach { (_, contactAvatarModel) -> + contactAvatarModel.showTrust.postValue(showTrust) + } + unknownContactsAvatarsMap.forEach { (_, contactAvatarModel) -> + contactAvatarModel.showTrust.postValue(showTrust) + } + conferenceAvatarMap.forEach { (_, contactAvatarModel) -> + contactAvatarModel.showTrust.postValue(showTrust) + } + } } @MainThread diff --git a/app/src/main/java/org/linphone/ui/main/contacts/model/ContactAvatarModel.kt b/app/src/main/java/org/linphone/ui/main/contacts/model/ContactAvatarModel.kt index 08ca160a8..faaaf5df6 100644 --- a/app/src/main/java/org/linphone/ui/main/contacts/model/ContactAvatarModel.kt +++ b/app/src/main/java/org/linphone/ui/main/contacts/model/ContactAvatarModel.kt @@ -48,8 +48,6 @@ class ContactAvatarModel @WorkerThread constructor(val friend: Friend, val addre val lastPresenceInfo = MutableLiveData() - val presenceStatus = MutableLiveData() - val name = MutableLiveData() val firstLetter: String = AppUtils.getFirstLetter(friend.name.orEmpty()) diff --git a/app/src/main/java/org/linphone/ui/main/model/AccountModel.kt b/app/src/main/java/org/linphone/ui/main/model/AccountModel.kt index 1fe95d9ea..315b2057f 100644 --- a/app/src/main/java/org/linphone/ui/main/model/AccountModel.kt +++ b/app/src/main/java/org/linphone/ui/main/model/AccountModel.kt @@ -30,6 +30,7 @@ import org.linphone.core.Account import org.linphone.core.AccountListenerStub import org.linphone.core.ChatMessage import org.linphone.core.ChatRoom +import org.linphone.core.ConsolidatedPresence import org.linphone.core.Core import org.linphone.core.CoreListenerStub import org.linphone.core.RegistrationState @@ -96,6 +97,7 @@ class AccountModel @WorkerThread constructor( trust.postValue(SecurityLevel.EndToEndEncryptedAndVerified) showTrust.postValue(account.isInSecureMode()) + presenceStatus.postValue(ConsolidatedPresence.Offline) update() } diff --git a/app/src/main/java/org/linphone/utils/DataBindingUtils.kt b/app/src/main/java/org/linphone/utils/DataBindingUtils.kt index 3db5dc928..8fcba1ef8 100644 --- a/app/src/main/java/org/linphone/utils/DataBindingUtils.kt +++ b/app/src/main/java/org/linphone/utils/DataBindingUtils.kt @@ -54,14 +54,13 @@ import coil.dispose import coil.load import coil.request.videoFrameMillis import coil.size.Dimension +import coil.transform.CircleCropTransformation import coil.transform.RoundedCornersTransformation -import com.google.android.material.imageview.ShapeableImageView import org.linphone.BR import org.linphone.R import org.linphone.contacts.AbstractAvatarModel import org.linphone.contacts.AvatarGenerator import org.linphone.core.ConsolidatedPresence -import org.linphone.core.SecurityLevel import org.linphone.core.tools.Log import org.linphone.ui.call.model.ConferenceParticipantDeviceModel @@ -310,7 +309,7 @@ fun ImageView.startAnimatedDrawable(start: Boolean = true) { @UiThread @BindingAdapter("coil") -fun ShapeableImageView.loadCircleFileWithCoil(file: String?) { +fun ImageView.loadCircleFileWithCoil(file: String?) { if (file != null) { load(file) } @@ -318,19 +317,13 @@ fun ShapeableImageView.loadCircleFileWithCoil(file: String?) { @UiThread @BindingAdapter("coilAvatar") -fun ShapeableImageView.loadAvatarWithCoil(model: AbstractAvatarModel?) { +fun ImageView.loadAvatarWithCoil(model: AbstractAvatarModel?) { loadContactPictureWithCoil(this, model) } -@UiThread -@BindingAdapter("coilAvatarNoTrust") -fun ShapeableImageView.loadAvatarWithCoilWithoutTrust(model: AbstractAvatarModel?) { - loadContactPictureWithCoil(this, model, skipTrust = true) -} - @UiThread @BindingAdapter("coilBubbleAvatar") -fun ShapeableImageView.loadBubbleAvatarWithCoil(model: AbstractAvatarModel?) { +fun ImageView.loadBubbleAvatarWithCoil(model: AbstractAvatarModel?) { val size = R.dimen.avatar_bubble_size val initialsSize = R.dimen.avatar_initials_bubble_text_size loadContactPictureWithCoil(this, model, size = size, textSize = initialsSize) @@ -338,15 +331,15 @@ fun ShapeableImageView.loadBubbleAvatarWithCoil(model: AbstractAvatarModel?) { @UiThread @BindingAdapter("coilBigAvatar") -fun ShapeableImageView.loadBigAvatarWithCoil(model: AbstractAvatarModel?) { +fun ImageView.loadBigAvatarWithCoil(model: AbstractAvatarModel?) { val size = R.dimen.avatar_big_size val initialsSize = R.dimen.avatar_initials_big_text_size loadContactPictureWithCoil(this, model, size = size, textSize = initialsSize) } @UiThread -@BindingAdapter("coilCallAvatar") -fun ShapeableImageView.loadCallAvatarWithCoil(model: AbstractAvatarModel?) { +@BindingAdapter("coilHugeAvatar") +fun ImageView.loadCallAvatarWithCoil(model: AbstractAvatarModel?) { val size = R.dimen.avatar_in_call_size val initialsSize = R.dimen.avatar_initials_call_text_size loadContactPictureWithCoil(this, model, size = size, textSize = initialsSize) @@ -354,7 +347,7 @@ fun ShapeableImageView.loadCallAvatarWithCoil(model: AbstractAvatarModel?) { @UiThread @BindingAdapter("coilInitials") -fun ShapeableImageView.loadInitialsAvatarWithCoil(initials: String?) { +fun ImageView.loadInitialsAvatarWithCoil(initials: String?) { val builder = AvatarGenerator(context) builder.setInitials(initials.orEmpty()) load(builder.build()) @@ -362,11 +355,10 @@ fun ShapeableImageView.loadInitialsAvatarWithCoil(initials: String?) { @SuppressLint("ResourceType") private fun loadContactPictureWithCoil( - imageView: ShapeableImageView, + imageView: ImageView, model: AbstractAvatarModel?, @DimenRes size: Int = 0, - @DimenRes textSize: Int = 0, - skipTrust: Boolean = false + @DimenRes textSize: Int = 0 ) { imageView.dispose() @@ -380,36 +372,13 @@ private fun loadContactPictureWithCoil( return } - if (!skipTrust) { - if (model.showTrust.value == true) { - when (model.trust.value) { - SecurityLevel.EndToEndEncryptedAndVerified -> { - imageView.setStrokeColorResource(R.color.info_500) - imageView.setStrokeWidthResource(R.dimen.avatar_trust_border_width) - } - - SecurityLevel.Unsafe -> { - imageView.setStrokeColorResource(R.color.danger_500) - imageView.setStrokeWidthResource(R.dimen.avatar_trust_border_width) - } - - else -> { - imageView.setStrokeColorResource(R.color.transparent_color) - imageView.setStrokeWidthResource(R.dimen.zero) - } - } - } else { - imageView.setStrokeColorResource(R.color.transparent_color) - imageView.setStrokeWidthResource(R.dimen.zero) - } - } - val images = model.images.value.orEmpty() val count = images.size if (count == 1) { val image = images.firstOrNull() if (image != null) { imageView.load(image) { + transformations(CircleCropTransformation()) listener( onError = { _, _ -> imageView.load(getErrorImageLoader(context, model, size, textSize)) @@ -426,7 +395,9 @@ private fun loadContactPictureWithCoil( AppUtils.getDimension(R.dimen.avatar_list_cell_size).toInt() } val bitmap = ImageUtils.getBitmapFromMultipleAvatars(imageView.context, w, images) - imageView.load(bitmap) + imageView.load(bitmap) { + transformations(CircleCropTransformation()) + } } } else { imageView.load(R.drawable.smiley) diff --git a/app/src/main/res/drawable/shape_circle_trusted_contact_overlay.xml b/app/src/main/res/drawable/shape_circle_trusted_contact_overlay.xml new file mode 100644 index 000000000..5a1465f37 --- /dev/null +++ b/app/src/main/res/drawable/shape_circle_trusted_contact_overlay.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_circle_unsafe_contact_overlay.xml b/app/src/main/res/drawable/shape_circle_unsafe_contact_overlay.xml new file mode 100644 index 000000000..aba2edee4 --- /dev/null +++ b/app/src/main/res/drawable/shape_circle_unsafe_contact_overlay.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout-land/contact_new_or_edit_fragment.xml b/app/src/main/res/layout-land/contact_new_or_edit_fragment.xml index 4a96a57ea..d496d166f 100644 --- a/app/src/main/res/layout-land/contact_new_or_edit_fragment.xml +++ b/app/src/main/res/layout-land/contact_new_or_edit_fragment.xml @@ -66,7 +66,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content"> - + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:bind="http://schemas.android.com/tools"> @@ -76,16 +77,16 @@ app:layout_constraintEnd_toEndOf="@id/video_preview" app:layout_constraintBottom_toBottomOf="@id/video_preview" /> - + app:layout_constraintBottom_toBottomOf="@id/no_video_background" /> - + @@ -25,29 +25,19 @@ app:barrierDirection="right" app:constraint_referenced_ids="name, register_status" /> - - - - + - @@ -93,28 +92,18 @@ app:constraint_referenced_ids="mode_background, current_mode, change_mode" android:visibility="@{viewModel.showModeSelection ? View.VISIBLE : View.GONE}" /> - - - - - - - - - - - - - - - - + - - @@ -18,39 +16,17 @@ android:padding="5dp" android:background="@drawable/primary_cell_background"> - - - - - - - - - - - - - - - - - - - @@ -56,12 +55,12 @@ android:orientation="horizontal" app:layout_constraintGuide_percent="1" /> - - - + app:layout_constraintBottom_toTopOf="@id/display_name" /> + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:bind="http://schemas.android.com/tools"> - @@ -23,31 +23,18 @@ android:contentDescription="@null" android:background="@drawable/shape_round_in_call_cell_gray_background" /> - - - + app:layout_constraintBottom_toBottomOf="parent" /> + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:bind="http://schemas.android.com/tools"> - - + app:layout_constraintBottom_toBottomOf="parent" /> + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:bind="http://schemas.android.com/tools"> - @@ -26,29 +26,16 @@ app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="parent"/> - - - + app:layout_constraintBottom_toBottomOf="parent" /> + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:bind="http://schemas.android.com/tools"> - @@ -23,31 +23,18 @@ android:contentDescription="@null" android:background="@drawable/shape_round_in_call_cell_gray_background" /> - - - + app:layout_constraintBottom_toBottomOf="parent" /> - + - @@ -26,29 +25,19 @@ android:layout_marginEnd="16dp" android:background="@drawable/primary_cell_background"> - - - - @@ -90,12 +89,12 @@ app:layout_constraintStart_toEndOf="@id/media_encryption_icon" app:layout_constraintTop_toBottomOf="@id/call_direction_label"/> - - - + app:layout_constraintBottom_toTopOf="@id/name" /> - @@ -39,27 +38,17 @@ app:layout_constraintStart_toEndOf="@id/call_direction_icon" app:layout_constraintTop_toTopOf="parent"/> - - - + app:layout_constraintBottom_toTopOf="@id/name" /> - + @@ -25,15 +25,15 @@ android:layout_marginEnd="16dp" android:background="@drawable/primary_cell_background"> - diff --git a/app/src/main/res/layout/call_outgoing_fragment.xml b/app/src/main/res/layout/call_outgoing_fragment.xml index d892312e7..b941eb702 100644 --- a/app/src/main/res/layout/call_outgoing_fragment.xml +++ b/app/src/main/res/layout/call_outgoing_fragment.xml @@ -5,7 +5,6 @@ - @@ -39,27 +38,17 @@ app:layout_constraintStart_toEndOf="@id/call_direction_icon" app:layout_constraintTop_toTopOf="parent"/> - - - + app:layout_constraintBottom_toTopOf="@id/name" /> - - - - - - - - - - @@ -78,40 +76,18 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent"/> - - - - - + app:layout_constraintTop_toTopOf="parent" /> + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:bind="http://schemas.android.com/tools"> - @@ -77,41 +77,17 @@ android:layout_width="match_parent" android:layout_height="wrap_content"> - - - - - - + - - @@ -30,39 +28,17 @@ android:paddingEnd="16dp" android:background="@drawable/primary_cell_background"> - - - - - - + - - @@ -20,40 +18,18 @@ android:layout_marginEnd="16dp" android:background="@drawable/primary_cell_background"> - - - - - - + - - @@ -20,40 +18,18 @@ android:layout_marginStart="16dp" android:layout_marginEnd="16dp"> - - - - - + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/contact_avatar_big.xml b/app/src/main/res/layout/contact_avatar_big.xml new file mode 100644 index 000000000..0067f7c65 --- /dev/null +++ b/app/src/main/res/layout/contact_avatar_big.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/contact_avatar_huge.xml b/app/src/main/res/layout/contact_avatar_huge.xml new file mode 100644 index 000000000..d2eceadbe --- /dev/null +++ b/app/src/main/res/layout/contact_avatar_huge.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/contact_avatar_medium.xml b/app/src/main/res/layout/contact_avatar_medium.xml new file mode 100644 index 000000000..915c697fd --- /dev/null +++ b/app/src/main/res/layout/contact_avatar_medium.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/contact_avatar_small.xml b/app/src/main/res/layout/contact_avatar_small.xml new file mode 100644 index 000000000..ab3ee2c9a --- /dev/null +++ b/app/src/main/res/layout/contact_avatar_small.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/contact_favourite_list_cell.xml b/app/src/main/res/layout/contact_favourite_list_cell.xml index 731b7de58..07ed8c17a 100644 --- a/app/src/main/res/layout/contact_favourite_list_cell.xml +++ b/app/src/main/res/layout/contact_favourite_list_cell.xml @@ -1,12 +1,10 @@ - + - - @@ -27,38 +25,16 @@ android:padding="5dp" android:background="@drawable/primary_cell_background"> - - - - - - @@ -103,40 +102,17 @@ app:constraint_referenced_ids="trusted_devices_count, trusted_devices_progress, devices, trusted_devices_progress_label" android:visibility="@{viewModel.expandDevicesTrust && viewModel.devices.size() > 0 ? View.VISIBLE : View.GONE}" /> - - - - - - + - - @@ -56,41 +54,19 @@ app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="parent"/> - - - - - - - @@ -89,40 +88,17 @@ app:constraint_referenced_ids="call, call_label, chat, chat_label, video_call, video_call_label, meeting, meeting_label" app:barrierDirection="bottom" /> - - - - - - + - - @@ -31,41 +29,19 @@ android:paddingEnd="16dp" android:background="@drawable/primary_cell_background"> - - - - - - + - - @@ -16,41 +14,19 @@ android:layout_width="match_parent" android:layout_height="wrap_content"> - - - - - - + - @@ -15,31 +14,19 @@ android:layout_width="match_parent" android:layout_height="wrap_content"> - - - + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:bind="http://schemas.android.com/tools"> @@ -69,16 +70,16 @@ app:layout_constraintEnd_toEndOf="@id/video_preview" app:layout_constraintBottom_toBottomOf="@id/video_preview" /> - + app:layout_constraintBottom_toBottomOf="@id/no_video_background" /> - - + @@ -34,28 +34,18 @@ app:constraint_referenced_ids="title, search" app:barrierDirection="bottom" /> - - - Contact is trusted Contact is not trusted! + Contact is online + Contact is not online Open drawer menu Go back \ No newline at end of file diff --git a/app/src/main/res/values/dimen.xml b/app/src/main/res/values/dimen.xml index 1bf91b989..009cf9590 100644 --- a/app/src/main/res/values/dimen.xml +++ b/app/src/main/res/values/dimen.xml @@ -28,9 +28,10 @@ 22dp 3dp 5dp + 6dp 14dp - 2dp + 3dp 16sp 14sp diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 38a2165c9..5fd785c15 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -633,6 +633,8 @@ Contact is trusted Contact is not trusted! + Contact is online + Contact is not online Open drawer menu Go back \ 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 2132b2b10..d35a3e4b4 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -166,12 +166,8 @@ 50%