mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-02-24 11:45:28 +00:00
Add verified updates and unverified banner.
This commit is contained in:
@@ -2,7 +2,6 @@ package org.thoughtcrime.securesms.components.identity;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
@@ -12,7 +11,6 @@ import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
@@ -24,9 +22,10 @@ public class UnverifiedBannerView extends LinearLayout {
|
||||
|
||||
private static final String TAG = Log.tag(UnverifiedBannerView.class);
|
||||
|
||||
private View container;
|
||||
private TextView text;
|
||||
private ImageView closeButton;
|
||||
private View container;
|
||||
private TextView text;
|
||||
private ImageView closeButton;
|
||||
private OnHideListener onHideListener;
|
||||
|
||||
public UnverifiedBannerView(Context context) {
|
||||
super(context);
|
||||
@@ -38,13 +37,11 @@ public class UnverifiedBannerView extends LinearLayout {
|
||||
initialize();
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.HONEYCOMB)
|
||||
public UnverifiedBannerView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
initialize();
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
|
||||
public UnverifiedBannerView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
initialize();
|
||||
@@ -82,16 +79,27 @@ public class UnverifiedBannerView extends LinearLayout {
|
||||
});
|
||||
}
|
||||
|
||||
public void setOnHideListener(@Nullable OnHideListener onHideListener) {
|
||||
this.onHideListener = onHideListener;
|
||||
}
|
||||
|
||||
public void hide() {
|
||||
if (onHideListener != null && onHideListener.onHide()) {
|
||||
return;
|
||||
}
|
||||
|
||||
setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
public interface DismissListener {
|
||||
public void onDismissed(List<IdentityRecord> unverifiedIdentities);
|
||||
void onDismissed(List<IdentityRecord> unverifiedIdentities);
|
||||
}
|
||||
|
||||
public interface ClickListener {
|
||||
public void onClicked(List<IdentityRecord> unverifiedIdentities);
|
||||
void onClicked(List<IdentityRecord> unverifiedIdentities);
|
||||
}
|
||||
|
||||
public interface OnHideListener {
|
||||
boolean onHide();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4393,7 +4393,7 @@ public class ConversationParentFragment extends Fragment
|
||||
|
||||
AlertDialog.Builder builder = new MaterialAlertDialogBuilder(requireContext());
|
||||
builder.setIcon(R.drawable.ic_warning);
|
||||
builder.setTitle("No longer verified");
|
||||
builder.setTitle(R.string.ConversationFragment__no_longer_verified);
|
||||
builder.setItems(unverifiedNames, (dialog, which) -> {
|
||||
startActivity(VerifyIdentityActivity.newIntent(requireContext(), unverifiedIdentities.get(which), false));
|
||||
});
|
||||
|
||||
@@ -71,7 +71,6 @@ public class ConversationTitleView extends ConstraintLayout {
|
||||
ViewUtil.setTextViewGravityStart(this.subtitle, getContext());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected @NonNull Parcelable onSaveInstanceState() {
|
||||
Bundle bundle = new Bundle();
|
||||
@@ -94,7 +93,6 @@ public class ConversationTitleView extends ConstraintLayout {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void showExpiring(@NonNull Recipient recipient) {
|
||||
isSelf = recipient.isSelf();
|
||||
|
||||
|
||||
@@ -6,8 +6,10 @@
|
||||
package org.thoughtcrime.securesms.conversation.v2
|
||||
|
||||
import android.content.Context
|
||||
import android.transition.ChangeBounds
|
||||
import android.transition.Slide
|
||||
import android.transition.TransitionManager
|
||||
import android.transition.TransitionSet
|
||||
import android.util.AttributeSet
|
||||
import android.view.Gravity
|
||||
import android.view.LayoutInflater
|
||||
@@ -15,8 +17,12 @@ import android.view.View
|
||||
import androidx.appcompat.widget.LinearLayoutCompat
|
||||
import androidx.core.transition.addListener
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.components.identity.UnverifiedBannerView
|
||||
import org.thoughtcrime.securesms.components.reminder.Reminder
|
||||
import org.thoughtcrime.securesms.components.reminder.ReminderView
|
||||
import org.thoughtcrime.securesms.database.identity.IdentityRecordList
|
||||
import org.thoughtcrime.securesms.database.model.IdentityRecord
|
||||
import org.thoughtcrime.securesms.util.IdentityUtil
|
||||
|
||||
/**
|
||||
* Responsible for showing the various "banner" views at the top of a conversation
|
||||
@@ -37,7 +43,7 @@ class ConversationBannerView @JvmOverloads constructor(
|
||||
private val inflater: LayoutInflater by lazy { LayoutInflater.from(context) }
|
||||
|
||||
private var reminderView: ReminderView? = null
|
||||
private var currentView: View? = null
|
||||
private var unverifiedBannerView: UnverifiedBannerView? = null
|
||||
|
||||
var listener: Listener? = null
|
||||
|
||||
@@ -45,8 +51,9 @@ class ConversationBannerView @JvmOverloads constructor(
|
||||
orientation = VERTICAL
|
||||
}
|
||||
|
||||
fun showAsReminder(reminder: Reminder) {
|
||||
fun showReminder(reminder: Reminder) {
|
||||
reminderView = show(
|
||||
position = -1,
|
||||
existingView = reminderView,
|
||||
create = { ReminderView(context) },
|
||||
bind = {
|
||||
@@ -63,39 +70,60 @@ class ConversationBannerView @JvmOverloads constructor(
|
||||
}
|
||||
}
|
||||
setOnHideListener {
|
||||
removeIfNotNull(reminderView)
|
||||
reminderView = null
|
||||
clearReminder()
|
||||
true
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
removeAllViews()
|
||||
fun clearReminder() {
|
||||
removeIfNotNull(reminderView)
|
||||
reminderView = null
|
||||
currentView = null
|
||||
}
|
||||
|
||||
private fun <V : View> show(existingView: V?, create: () -> V, bind: V.() -> Unit = {}): V {
|
||||
if (existingView != currentView) {
|
||||
removeIfNotNull(currentView)
|
||||
}
|
||||
fun showUnverifiedBanner(identityRecords: IdentityRecordList) {
|
||||
unverifiedBannerView = show(
|
||||
position = 0,
|
||||
existingView = null,
|
||||
create = { UnverifiedBannerView(context) },
|
||||
bind = {
|
||||
setOnHideListener {
|
||||
clearUnverifiedBanner()
|
||||
true
|
||||
}
|
||||
display(
|
||||
IdentityUtil.getUnverifiedBannerDescription(context, identityRecords.unverifiedRecipients)!!,
|
||||
identityRecords.unverifiedRecords,
|
||||
{ listener?.onUnverifiedBannerClicked(identityRecords.unverifiedRecords) },
|
||||
{ listener?.onUnverifiedBannerDismissed(identityRecords.unverifiedRecords) }
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fun clearUnverifiedBanner() {
|
||||
removeIfNotNull(unverifiedBannerView)
|
||||
unverifiedBannerView = null
|
||||
}
|
||||
|
||||
private fun <V : View> show(position: Int, existingView: V?, create: () -> V, bind: V.() -> Unit = {}): V {
|
||||
val view: V = if (existingView != null) {
|
||||
existingView
|
||||
} else {
|
||||
val newView: V = create()
|
||||
|
||||
TransitionManager.beginDelayedTransition(this, Slide(Gravity.TOP))
|
||||
addView(newView, defaultLayoutParams())
|
||||
if (position in 0..childCount) {
|
||||
addView(newView, position, defaultLayoutParams())
|
||||
} else {
|
||||
addView(newView, defaultLayoutParams())
|
||||
}
|
||||
newView
|
||||
}
|
||||
|
||||
view.bind()
|
||||
|
||||
currentView = view
|
||||
|
||||
return view
|
||||
}
|
||||
|
||||
@@ -105,13 +133,29 @@ class ConversationBannerView @JvmOverloads constructor(
|
||||
|
||||
private fun removeIfNotNull(view: View?) {
|
||||
if (view != null) {
|
||||
val transition = Slide(Gravity.TOP).apply {
|
||||
val slideTransition = Slide(Gravity.TOP).apply {
|
||||
addListener(
|
||||
onEnd = {
|
||||
layoutParams = layoutParams.apply { height = LayoutParams.WRAP_CONTENT }
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
val changeTransition = ChangeBounds().apply {
|
||||
if (reminderView != null) {
|
||||
addTarget(reminderView)
|
||||
}
|
||||
|
||||
if (unverifiedBannerView != null) {
|
||||
addTarget(unverifiedBannerView)
|
||||
}
|
||||
}
|
||||
|
||||
val transition = TransitionSet().apply {
|
||||
addTransition(slideTransition)
|
||||
addTransition(changeTransition)
|
||||
}
|
||||
|
||||
layoutParams = layoutParams.apply { height = this@ConversationBannerView.height }
|
||||
TransitionManager.beginDelayedTransition(this, transition)
|
||||
removeView(view)
|
||||
@@ -124,5 +168,7 @@ class ConversationBannerView @JvmOverloads constructor(
|
||||
fun reviewJoinRequestsAction()
|
||||
fun gv1SuggestionsAction(actionId: Int)
|
||||
fun changeBubbleSettingAction(disableSetting: Boolean)
|
||||
fun onUnverifiedBannerClicked(unverifiedIdentities: List<IdentityRecord>)
|
||||
fun onUnverifiedBannerDismissed(unverifiedIdentities: List<IdentityRecord>)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,6 +60,8 @@ import io.reactivex.rxjava3.disposables.Disposable
|
||||
import io.reactivex.rxjava3.kotlin.subscribeBy
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import org.greenrobot.eventbus.ThreadMode
|
||||
import org.signal.core.util.Result
|
||||
import org.signal.core.util.ThreadUtil
|
||||
import org.signal.core.util.concurrent.LifecycleDisposable
|
||||
@@ -125,6 +127,7 @@ import org.thoughtcrime.securesms.conversation.ui.error.EnableCallNotificationSe
|
||||
import org.thoughtcrime.securesms.conversation.v2.groups.ConversationGroupCallViewModel
|
||||
import org.thoughtcrime.securesms.conversation.v2.groups.ConversationGroupViewModel
|
||||
import org.thoughtcrime.securesms.conversation.v2.keyboard.AttachmentKeyboardFragment
|
||||
import org.thoughtcrime.securesms.database.model.IdentityRecord
|
||||
import org.thoughtcrime.securesms.database.model.InMemoryMessageRecord
|
||||
import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord
|
||||
import org.thoughtcrime.securesms.database.model.MessageId
|
||||
@@ -198,6 +201,7 @@ import org.thoughtcrime.securesms.util.hasGiftBadge
|
||||
import org.thoughtcrime.securesms.util.viewModel
|
||||
import org.thoughtcrime.securesms.util.views.Stub
|
||||
import org.thoughtcrime.securesms.util.visible
|
||||
import org.thoughtcrime.securesms.verify.VerifyIdentityActivity
|
||||
import org.thoughtcrime.securesms.wallpaper.ChatWallpaper
|
||||
import org.thoughtcrime.securesms.wallpaper.ChatWallpaperDimLevelUtil
|
||||
import java.util.Locale
|
||||
@@ -345,6 +349,9 @@ class ConversationFragment : LoggingFragment(R.layout.v2_conversation_fragment)
|
||||
|
||||
WindowUtil.setLightNavigationBarFromTheme(requireActivity())
|
||||
WindowUtil.setLightStatusBarFromTheme(requireActivity())
|
||||
|
||||
EventBus.getDefault().register(this)
|
||||
|
||||
groupCallViewModel.peekGroupCall()
|
||||
|
||||
if (!args.conversationScreenType.isInBubble) {
|
||||
@@ -352,12 +359,20 @@ class ConversationFragment : LoggingFragment(R.layout.v2_conversation_fragment)
|
||||
}
|
||||
|
||||
motionEventRelay.setDrain(MotionEventRelayDrain())
|
||||
|
||||
viewModel.updateIdentityRecords()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
ApplicationDependencies.getMessageNotifier().clearVisibleThread()
|
||||
motionEventRelay.setDrain(null)
|
||||
EventBus.getDefault().unregister(this)
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
EventBus.getDefault().unregister(this)
|
||||
}
|
||||
|
||||
private fun observeConversationThread() {
|
||||
@@ -485,12 +500,30 @@ class ConversationFragment : LoggingFragment(R.layout.v2_conversation_fragment)
|
||||
.reminder
|
||||
.subscribeBy { reminder ->
|
||||
if (reminder.isPresent) {
|
||||
binding.conversationBanner.showAsReminder(reminder.get())
|
||||
binding.conversationBanner.showReminder(reminder.get())
|
||||
} else {
|
||||
binding.conversationBanner.clear()
|
||||
binding.conversationBanner.clearReminder()
|
||||
}
|
||||
}
|
||||
.addTo(disposables)
|
||||
|
||||
viewModel
|
||||
.identityRecords
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribeBy { presentIdentityRecordsState(it) }
|
||||
.addTo(disposables)
|
||||
}
|
||||
|
||||
private fun presentIdentityRecordsState(identityRecordsState: IdentityRecordsState) {
|
||||
if (!identityRecordsState.isGroup) {
|
||||
binding.conversationTitleView.root.setVerified(identityRecordsState.isVerified)
|
||||
}
|
||||
|
||||
if (identityRecordsState.isUnverified) {
|
||||
binding.conversationBanner.showUnverifiedBanner(identityRecordsState.identityRecords)
|
||||
} else {
|
||||
binding.conversationBanner.clearUnverifiedBanner()
|
||||
}
|
||||
}
|
||||
|
||||
private fun presentInputReadyState(inputReadyState: InputReadyState) {
|
||||
@@ -589,7 +622,14 @@ class ConversationFragment : LoggingFragment(R.layout.v2_conversation_fragment)
|
||||
return
|
||||
}
|
||||
|
||||
binding.conversationTitleView.root.setTitle(GlideApp.with(this), recipient)
|
||||
val titleView = binding.conversationTitleView.root
|
||||
|
||||
titleView.setTitle(GlideApp.with(this), recipient)
|
||||
if (recipient.expiresInSeconds > 0) {
|
||||
titleView.showExpiring(recipient)
|
||||
} else {
|
||||
titleView.clearExpiring()
|
||||
}
|
||||
}
|
||||
|
||||
private fun presentWallpaper(chatWallpaper: ChatWallpaper?) {
|
||||
@@ -1714,13 +1754,8 @@ class ConversationFragment : LoggingFragment(R.layout.v2_conversation_fragment)
|
||||
// TODO [alex] - ("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun showExpiring(recipient: Recipient) {
|
||||
binding.conversationTitleView.root.showExpiring(recipient)
|
||||
}
|
||||
|
||||
override fun clearExpiring() {
|
||||
binding.conversationTitleView.root.clearExpiring()
|
||||
}
|
||||
override fun showExpiring(recipient: Recipient) = Unit
|
||||
override fun clearExpiring() = Unit
|
||||
|
||||
override fun showGroupCallingTooltip() {
|
||||
conversationTooltips.displayGroupCallingTooltip(requireView().findViewById(R.id.menu_video_secure))
|
||||
@@ -1841,6 +1876,26 @@ class ConversationFragment : LoggingFragment(R.layout.v2_conversation_fragment)
|
||||
startActivity(intent)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onUnverifiedBannerClicked(unverifiedIdentities: List<IdentityRecord>) {
|
||||
if (unverifiedIdentities.size == 1) {
|
||||
startActivity(VerifyIdentityActivity.newIntent(requireContext(), unverifiedIdentities[0], false))
|
||||
} else {
|
||||
val unverifiedNames = unverifiedIdentities
|
||||
.map { Recipient.resolved(it.recipientId).getDisplayName(requireContext()) }
|
||||
.toTypedArray()
|
||||
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
.setIcon(R.drawable.ic_warning)
|
||||
.setTitle(R.string.ConversationFragment__no_longer_verified)
|
||||
.setItems(unverifiedNames) { _, which: Int -> startActivity(VerifyIdentityActivity.newIntent(requireContext(), unverifiedIdentities[which], false)) }
|
||||
.show()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onUnverifiedBannerDismissed(unverifiedIdentities: List<IdentityRecord>) {
|
||||
viewModel.resetVerifiedStatusToDefault(unverifiedIdentities)
|
||||
}
|
||||
}
|
||||
|
||||
//endregion
|
||||
@@ -2100,4 +2155,13 @@ class ConversationFragment : LoggingFragment(R.layout.v2_conversation_fragment)
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
//region Event Bus
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.POSTING)
|
||||
fun onIdentityRecordUpdate(event: IdentityRecord?) {
|
||||
viewModel.updateIdentityRecords()
|
||||
}
|
||||
|
||||
//endregion
|
||||
}
|
||||
|
||||
@@ -28,9 +28,14 @@ import org.thoughtcrime.securesms.components.reminder.UnauthorizedReminder
|
||||
import org.thoughtcrime.securesms.conversation.colors.GroupAuthorNameColorHelper
|
||||
import org.thoughtcrime.securesms.conversation.colors.NameColor
|
||||
import org.thoughtcrime.securesms.conversation.v2.data.ConversationDataSource
|
||||
import org.thoughtcrime.securesms.crypto.ReentrantSessionLock
|
||||
import org.thoughtcrime.securesms.database.GroupTable
|
||||
import org.thoughtcrime.securesms.database.IdentityTable.VerifiedStatus
|
||||
import org.thoughtcrime.securesms.database.RecipientTable
|
||||
import org.thoughtcrime.securesms.database.RxDatabaseObserver
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase
|
||||
import org.thoughtcrime.securesms.database.model.GroupRecord
|
||||
import org.thoughtcrime.securesms.database.model.IdentityRecord
|
||||
import org.thoughtcrime.securesms.database.model.Mention
|
||||
import org.thoughtcrime.securesms.database.model.MessageId
|
||||
import org.thoughtcrime.securesms.database.model.Quote
|
||||
@@ -226,6 +231,36 @@ class ConversationRepository(
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("IfThenToElvis")
|
||||
fun getIdentityRecords(recipient: Recipient, groupRecord: GroupRecord?): Single<IdentityRecordsState> {
|
||||
return Single.fromCallable {
|
||||
val recipients = if (groupRecord == null) {
|
||||
listOf(recipient)
|
||||
} else {
|
||||
groupRecord.requireV2GroupProperties().getMemberRecipients(GroupTable.MemberSet.FULL_MEMBERS_EXCLUDING_SELF)
|
||||
}
|
||||
|
||||
val records = ApplicationDependencies.getProtocolStore().aci().identities().getIdentityRecords(recipients)
|
||||
val isVerified = recipient.registered == RecipientTable.RegisteredState.REGISTERED &&
|
||||
Recipient.self().isRegistered &&
|
||||
records.isVerified &&
|
||||
!recipient.isSelf
|
||||
|
||||
IdentityRecordsState(isVerified, records, isGroup = groupRecord != null)
|
||||
}.subscribeOn(Schedulers.io())
|
||||
}
|
||||
|
||||
fun resetVerifiedStatusToDefault(unverifiedIdentities: List<IdentityRecord>): Completable {
|
||||
return Completable.fromCallable {
|
||||
ReentrantSessionLock.INSTANCE.acquire().use {
|
||||
val identityStore = ApplicationDependencies.getProtocolStore().aci().identities()
|
||||
for ((recipientId, identityKey) in unverifiedIdentities) {
|
||||
identityStore.setVerified(recipientId, identityKey, VerifiedStatus.DEFAULT)
|
||||
}
|
||||
}
|
||||
}.subscribeOn(Schedulers.io())
|
||||
}
|
||||
|
||||
data class MessageCounts(
|
||||
val unread: Int,
|
||||
val mentions: Int
|
||||
|
||||
@@ -27,6 +27,7 @@ import org.thoughtcrime.securesms.conversation.colors.GroupAuthorNameColorHelper
|
||||
import org.thoughtcrime.securesms.conversation.colors.NameColor
|
||||
import org.thoughtcrime.securesms.conversation.v2.data.ConversationElementKey
|
||||
import org.thoughtcrime.securesms.database.DatabaseObserver
|
||||
import org.thoughtcrime.securesms.database.model.IdentityRecord
|
||||
import org.thoughtcrime.securesms.database.model.Mention
|
||||
import org.thoughtcrime.securesms.database.model.MessageId
|
||||
import org.thoughtcrime.securesms.database.model.MessageRecord
|
||||
@@ -95,9 +96,11 @@ class ConversationViewModel(
|
||||
get() = hasMessageRequestStateSubject.value ?: false
|
||||
|
||||
private val refreshReminder: Subject<Unit> = PublishSubject.create()
|
||||
|
||||
val reminder: Observable<Optional<Reminder>>
|
||||
|
||||
private val refreshIdentityRecords: Subject<Unit> = PublishSubject.create()
|
||||
val identityRecords: Observable<IdentityRecordsState>
|
||||
|
||||
init {
|
||||
disposables += recipient
|
||||
.subscribeBy {
|
||||
@@ -170,6 +173,14 @@ class ConversationViewModel(
|
||||
.subscribeOn(Schedulers.io())
|
||||
.flatMapMaybe { groupRecord -> repository.getReminder(groupRecord.orNull()) }
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
|
||||
identityRecords = Observable.combineLatest(
|
||||
refreshIdentityRecords.startWithItem(Unit).observeOn(Schedulers.io()),
|
||||
recipient,
|
||||
recipientRepository.groupRecord
|
||||
) { _, r, g -> Pair(r, g) }
|
||||
.flatMapSingle { (r, g) -> repository.getIdentityRecords(r, g.orNull()) }
|
||||
.distinctUntilChanged()
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
@@ -229,4 +240,15 @@ class ConversationViewModel(
|
||||
bodyRanges = bodyRanges
|
||||
).observeOn(AndroidSchedulers.mainThread())
|
||||
}
|
||||
|
||||
fun resetVerifiedStatusToDefault(unverifiedIdentities: List<IdentityRecord>) {
|
||||
disposables += repository.resetVerifiedStatusToDefault(unverifiedIdentities)
|
||||
.subscribe {
|
||||
refreshIdentityRecords.onNext(Unit)
|
||||
}
|
||||
}
|
||||
|
||||
fun updateIdentityRecords() {
|
||||
refreshIdentityRecords.onNext(Unit)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright 2023 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.thoughtcrime.securesms.conversation.v2
|
||||
|
||||
import org.thoughtcrime.securesms.database.identity.IdentityRecordList
|
||||
|
||||
/**
|
||||
* Current state for all participants identity keys in a conversation excluding self.
|
||||
*/
|
||||
data class IdentityRecordsState(
|
||||
val isVerified: Boolean,
|
||||
val identityRecords: IdentityRecordList,
|
||||
val isGroup: Boolean
|
||||
) {
|
||||
val isUnverified: Boolean = identityRecords.isUnverified
|
||||
}
|
||||
@@ -10,6 +10,7 @@ import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public final class IdentityRecordList {
|
||||
@@ -145,4 +146,16 @@ public final class IdentityRecordList {
|
||||
System.currentTimeMillis() - identityRecord.getTimestamp() < untrustedWindowMillis;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
final IdentityRecordList that = (IdentityRecordList) o;
|
||||
return Objects.equals(identityRecords, that.identityRecords);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(identityRecords);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user