From 33cc8363f99be6652584b38091c2d2ad18f78967 Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Fri, 31 Jul 2020 14:10:20 -0400 Subject: [PATCH] Add internal setting to see recipient details. --- .../securesms/keyvalue/InternalValues.java | 8 +++ .../InternalOptionsPreferenceFragment.java | 1 + .../ManageRecipientFragment.java | 10 ++++ .../ManageRecipientViewModel.java | 59 +++++++++++++++---- .../res/layout/recipient_manage_fragment.xml | 12 ++++ app/src/main/res/values/strings.xml | 3 + app/src/main/res/xml/preferences_internal.xml | 12 ++++ 7 files changed, 92 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/InternalValues.java b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/InternalValues.java index ba16e53b9..93f4236f5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/InternalValues.java +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/InternalValues.java @@ -8,6 +8,7 @@ public final class InternalValues extends SignalStoreValues { public static final String GV2_FORCE_INVITES = "internal.gv2.force_invites"; public static final String GV2_IGNORE_SERVER_CHANGES = "internal.gv2.ignore_server_changes"; public static final String GV2_IGNORE_P2P_CHANGES = "internal.gv2.ignore_p2p_changes"; + public static final String RECIPIENT_DETAILS = "internal.recipient_details"; InternalValues(KeyValueStore store) { super(store); @@ -51,4 +52,11 @@ public final class InternalValues extends SignalStoreValues { public synchronized boolean gv2IgnoreP2PChanges() { return FeatureFlags.internalUser() && getBoolean(GV2_IGNORE_P2P_CHANGES, false); } + + /** + * Show detailed recipient info in the {@link org.thoughtcrime.securesms.recipients.ui.managerecipient.ManageRecipientFragment}. + */ + public synchronized boolean recipientDetails() { + return FeatureFlags.internalUser() && getBoolean(RECIPIENT_DETAILS, false); + } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/preferences/InternalOptionsPreferenceFragment.java b/app/src/main/java/org/thoughtcrime/securesms/preferences/InternalOptionsPreferenceFragment.java index b86bcfd3e..ebafcd412 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/preferences/InternalOptionsPreferenceFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/preferences/InternalOptionsPreferenceFragment.java @@ -34,6 +34,7 @@ public class InternalOptionsPreferenceFragment extends CorrectedPreferenceFragme PreferenceDataStore preferenceDataStore = SignalStore.getPreferenceDataStore(); + initializeSwitchPreference(preferenceDataStore, InternalValues.RECIPIENT_DETAILS, SignalStore.internalValues().recipientDetails()); initializeSwitchPreference(preferenceDataStore, InternalValues.GV2_DO_NOT_CREATE_GV2, SignalStore.internalValues().gv2DoNotCreateGv2Groups()); initializeSwitchPreference(preferenceDataStore, InternalValues.GV2_FORCE_INVITES, SignalStore.internalValues().gv2ForceInvites()); initializeSwitchPreference(preferenceDataStore, InternalValues.GV2_IGNORE_SERVER_CHANGES, SignalStore.internalValues().gv2IgnoreServerChanges()); diff --git a/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/managerecipient/ManageRecipientFragment.java b/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/managerecipient/ManageRecipientFragment.java index 0a2b9d2e7..8d78c118b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/managerecipient/ManageRecipientFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/managerecipient/ManageRecipientFragment.java @@ -39,6 +39,7 @@ import org.thoughtcrime.securesms.components.ThreadPhotoRailView; import org.thoughtcrime.securesms.contacts.avatars.FallbackContactPhoto; import org.thoughtcrime.securesms.contacts.avatars.FallbackPhoto80dp; import org.thoughtcrime.securesms.groups.ui.GroupMemberListView; +import org.thoughtcrime.securesms.keyvalue.SignalStore; import org.thoughtcrime.securesms.mediaoverview.MediaOverviewActivity; import org.thoughtcrime.securesms.mms.GlideApp; import org.thoughtcrime.securesms.notifications.NotificationChannels; @@ -68,6 +69,7 @@ public class ManageRecipientFragment extends LoggingFragment { private Toolbar toolbar; private TextView title; private TextView subtitle; + private TextView internalDetails; private View contactRow; private TextView contactText; private ImageView contactIcon; @@ -125,6 +127,7 @@ public class ManageRecipientFragment extends LoggingFragment { contactIcon = view.findViewById(R.id.recipient_contact_icon); title = view.findViewById(R.id.name); subtitle = view.findViewById(R.id.username_number); + internalDetails = view.findViewById(R.id.recipient_internal_details); sharedGroupList = view.findViewById(R.id.shared_group_list); groupsInCommonCount = view.findViewById(R.id.groups_in_common_count); threadPhotoRailView = view.findViewById(R.id.recent_photos); @@ -214,6 +217,13 @@ public class ManageRecipientFragment extends LoggingFragment { viewModel.getMuteState().observe(getViewLifecycleOwner(), this::presentMuteState); viewModel.getCanAddToAGroup().observe(getViewLifecycleOwner(), canAdd -> addToAGroup.setVisibility(canAdd ? View.VISIBLE : View.GONE)); + if (SignalStore.internalValues().recipientDetails()) { + viewModel.getInternalDetails().observe(getViewLifecycleOwner(), internalDetails::setText); + internalDetails.setVisibility(View.VISIBLE); + } else { + internalDetails.setVisibility(View.GONE); + } + disappearingMessagesRow.setOnClickListener(v -> viewModel.handleExpirationSelection(requireContext())); block.setOnClickListener(v -> viewModel.onBlockClicked(requireActivity())); unblock.setOnClickListener(v -> viewModel.onUnblockClicked(requireActivity())); diff --git a/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/managerecipient/ManageRecipientViewModel.java b/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/managerecipient/ManageRecipientViewModel.java index 07cc4470d..40ac82d6b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/managerecipient/ManageRecipientViewModel.java +++ b/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/managerecipient/ManageRecipientViewModel.java @@ -27,17 +27,21 @@ import org.thoughtcrime.securesms.database.loaders.ThreadMediaLoader; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.groups.ui.GroupMemberEntry; import org.thoughtcrime.securesms.groups.ui.addtogroup.AddToGroupsActivity; +import org.thoughtcrime.securesms.keyvalue.SignalStore; import org.thoughtcrime.securesms.notifications.NotificationChannels; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.RecipientId; import org.thoughtcrime.securesms.recipients.RecipientUtil; +import org.thoughtcrime.securesms.util.Base64; import org.thoughtcrime.securesms.util.CommunicationActions; import org.thoughtcrime.securesms.util.DefaultValueLiveData; import org.thoughtcrime.securesms.util.ExpirationUtil; +import org.thoughtcrime.securesms.util.Hex; import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.livedata.LiveDataUtil; import java.util.List; +import java.util.UUID; public final class ManageRecipientViewModel extends ViewModel { @@ -47,14 +51,15 @@ public final class ManageRecipientViewModel extends ViewModel { private final ManageRecipientRepository manageRecipientRepository; private final LiveData title; private final LiveData subtitle; + private final LiveData internalDetails; private final LiveData disappearingMessageTimer; private final MutableLiveData identity; private final LiveData recipient; - private final MutableLiveData mediaCursor = new MutableLiveData<>(null); + private final MutableLiveData mediaCursor; private final LiveData muteState; private final LiveData hasCustomNotifications; private final LiveData canCollapseMemberList; - private final DefaultValueLiveData groupListCollapseState = new DefaultValueLiveData<>(CollapseState.COLLAPSED); + private final DefaultValueLiveData groupListCollapseState; private final LiveData canBlock; private final LiveData> visibleSharedGroups; private final LiveData sharedGroupsCountSummary; @@ -63,14 +68,20 @@ public final class ManageRecipientViewModel extends ViewModel { private ManageRecipientViewModel(@NonNull Context context, @NonNull ManageRecipientRepository manageRecipientRepository) { this.context = context; this.manageRecipientRepository = manageRecipientRepository; + this.recipient = Recipient.live(manageRecipientRepository.getRecipientId()).getLiveData(); + this.title = Transformations.map(recipient, r -> getDisplayTitle(r, context) ); + this.subtitle = Transformations.map(recipient, r -> getDisplaySubtitle(r, context)); + this.identity = new MutableLiveData<>(); + this.mediaCursor = new MutableLiveData<>(null); + this.groupListCollapseState = new DefaultValueLiveData<>(CollapseState.COLLAPSED); + this.disappearingMessageTimer = Transformations.map(this.recipient, r -> ExpirationUtil.getExpirationDisplayValue(context, r.getExpireMessages())); + this.muteState = Transformations.map(this.recipient, r -> new MuteState(r.getMuteUntil(), r.isMuted())); + this.hasCustomNotifications = Transformations.map(this.recipient, r -> r.getNotificationChannel() != null || !NotificationChannels.supported()); + this.canBlock = Transformations.map(this.recipient, r -> !r.isBlocked()); + this.internalDetails = Transformations.map(this.recipient, this::populateInternalDetails); manageRecipientRepository.getThreadId(this::onThreadIdLoaded); - this.recipient = Recipient.live(manageRecipientRepository.getRecipientId()).getLiveData(); - this.title = Transformations.map(recipient, r -> getDisplayTitle(r, context)); - this.subtitle = Transformations.map(recipient, r -> getDisplaySubtitle(r, context)); - this.identity = new MutableLiveData<>(); - LiveData> allSharedGroups = LiveDataUtil.mapAsync(this.recipient, r -> manageRecipientRepository.getSharedGroups(r.getId())); this.sharedGroupsCountSummary = Transformations.map(allSharedGroups, list -> { @@ -87,10 +98,6 @@ public final class ManageRecipientViewModel extends ViewModel { ManageRecipientViewModel::filterSharedGroupList), recipients -> Stream.of(recipients).map(r -> new GroupMemberEntry.FullMember(r, false)).toList()); - this.disappearingMessageTimer = Transformations.map(this.recipient, r -> ExpirationUtil.getExpirationDisplayValue(context, r.getExpireMessages())); - this.muteState = Transformations.map(this.recipient, r -> new MuteState(r.getMuteUntil(), r.isMuted())); - this.hasCustomNotifications = Transformations.map(this.recipient, r -> r.getNotificationChannel() != null || !NotificationChannels.supported()); - this.canBlock = Transformations.map(this.recipient, r -> !r.isBlocked()); boolean isSelf = manageRecipientRepository.getRecipientId().equals(Recipient.self().getId()); if (!isSelf) { @@ -99,8 +106,9 @@ public final class ManageRecipientViewModel extends ViewModel { MutableLiveData localGroupCount = new MutableLiveData<>(0); - canAddToAGroup = LiveDataUtil.combineLatest(recipient, localGroupCount, - (r, count) -> count > 0 && r.isRegistered() && !r.isGroup() && !r.isLocalNumber()); + this.canAddToAGroup = LiveDataUtil.combineLatest(recipient, + localGroupCount, + (r, count) -> count > 0 && r.isRegistered() && !r.isGroup() && !r.isLocalNumber()); manageRecipientRepository.getActiveGroupCount(localGroupCount::postValue); } @@ -136,6 +144,10 @@ public final class ManageRecipientViewModel extends ViewModel { return subtitle; } + LiveData getInternalDetails() { + return internalDetails; + } + LiveData getRecipient() { return recipient; } @@ -265,6 +277,27 @@ public final class ManageRecipientViewModel extends ViewModel { manageRecipientRepository.refreshRecipient(); } + private @NonNull String populateInternalDetails(@NonNull Recipient recipient) { + if (!SignalStore.internalValues().recipientDetails()) { + return ""; + } + + String profileKeyBase64 = recipient.getProfileKey() != null ? Base64.encodeBytes(recipient.getProfileKey()) : "None"; + String profileKeyHex = recipient.getProfileKey() != null ? Hex.toStringCondensed(recipient.getProfileKey()) : "None"; + return String.format("-- Profile Name --\n%s\n\n" + + "-- Profile Sharing --\n%s\n\n" + + "-- Profile Key (Base64) --\n%s\n\n" + + "-- Profile Key (Hex) --\n%s\n\n" + + "-- UUID --\n%s\n\n" + + "-- RecipientId --\n%s", + recipient.getProfileName().toString(), + recipient.isProfileSharing(), + profileKeyBase64, + profileKeyHex, + recipient.getUuid().transform(UUID::toString).or("None"), + recipient.getId().serialize()); + } + static final class MediaCursor { private final long threadId; @NonNull private final CursorFactory mediaCursorFactory; diff --git a/app/src/main/res/layout/recipient_manage_fragment.xml b/app/src/main/res/layout/recipient_manage_fragment.xml index dce36d9ad..3a6773c4f 100644 --- a/app/src/main/res/layout/recipient_manage_fragment.xml +++ b/app/src/main/res/layout/recipient_manage_fragment.xml @@ -55,6 +55,18 @@ android:textColor="?title_text_color_secondary" tools:text="\@spidergwen +1 555-654-6657" /> + + Creates a new versioned profile, and triggers an update of any GV2 group you belong to. Refresh remote values Forces a refresh of remote values locally instead of waiting for the elapsed time + Display + User Details + See more information about a user when viewing conversation settings. Storage service Overwrite remote data Forces remote storage to match the local device state. diff --git a/app/src/main/res/xml/preferences_internal.xml b/app/src/main/res/xml/preferences_internal.xml index 5b61ce91d..705dbb784 100644 --- a/app/src/main/res/xml/preferences_internal.xml +++ b/app/src/main/res/xml/preferences_internal.xml @@ -22,6 +22,18 @@ + + + + + +