mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-25 11:20:47 +01:00
Add support for manual initiation of GV1->GV2 migrations.
This commit is contained in:
@@ -116,6 +116,7 @@ import org.thoughtcrime.securesms.components.identity.UnverifiedBannerView;
|
||||
import org.thoughtcrime.securesms.components.location.SignalPlace;
|
||||
import org.thoughtcrime.securesms.components.mention.MentionAnnotation;
|
||||
import org.thoughtcrime.securesms.components.reminder.ExpiredBuildReminder;
|
||||
import org.thoughtcrime.securesms.components.reminder.GroupsV1MigrationInitiationReminder;
|
||||
import org.thoughtcrime.securesms.components.reminder.GroupsV1MigrationSuggestionsReminder;
|
||||
import org.thoughtcrime.securesms.components.reminder.PendingGroupJoinRequestsReminder;
|
||||
import org.thoughtcrime.securesms.components.reminder.Reminder;
|
||||
@@ -167,6 +168,7 @@ import org.thoughtcrime.securesms.groups.ui.GroupErrors;
|
||||
import org.thoughtcrime.securesms.groups.ui.LeaveGroupDialog;
|
||||
import org.thoughtcrime.securesms.groups.ui.invitesandrequests.ManagePendingAndRequestingMembersActivity;
|
||||
import org.thoughtcrime.securesms.groups.ui.managegroup.ManageGroupActivity;
|
||||
import org.thoughtcrime.securesms.groups.ui.migration.GroupsV1MigrationInitiationBottomSheetDialogFragment;
|
||||
import org.thoughtcrime.securesms.groups.ui.migration.GroupsV1MigrationSuggestionsDialog;
|
||||
import org.thoughtcrime.securesms.insights.InsightsLauncher;
|
||||
import org.thoughtcrime.securesms.invites.InviteReminderModel;
|
||||
@@ -455,7 +457,7 @@ public class ConversationActivity extends PassphraseRequiredActivity
|
||||
initializeMentionsViewModel();
|
||||
initializeEnabledCheck();
|
||||
initializePendingRequestsBanner();
|
||||
initializeGroupV1MigrationSuggestionsBanner();
|
||||
initializeGroupV1MigrationsBanners();
|
||||
initializeSecurity(recipient.get().isRegistered(), isDefaultSms).addListener(new AssertedSuccessListener<Boolean>() {
|
||||
@Override
|
||||
public void onSuccess(Boolean result) {
|
||||
@@ -1550,11 +1552,14 @@ public class ConversationActivity extends PassphraseRequiredActivity
|
||||
.observe(this, actionablePendingGroupRequests -> updateReminders());
|
||||
}
|
||||
|
||||
private void initializeGroupV1MigrationSuggestionsBanner() {
|
||||
private void initializeGroupV1MigrationsBanners() {
|
||||
groupViewModel.getGroupV1MigrationSuggestions()
|
||||
.observe(this, s -> updateReminders());
|
||||
groupViewModel.getShowGroupsV1MigrationBanner()
|
||||
.observe(this, b -> updateReminders());
|
||||
}
|
||||
|
||||
|
||||
private ListenableFuture<Boolean> initializeDraftFromDatabase() {
|
||||
SettableFuture<Boolean> future = new SettableFuture<>();
|
||||
|
||||
@@ -1711,6 +1716,7 @@ public class ConversationActivity extends PassphraseRequiredActivity
|
||||
Optional<Reminder> inviteReminder = inviteReminderModel.getReminder();
|
||||
Integer actionableRequestingMembers = groupViewModel.getActionableRequestingMembers().getValue();
|
||||
List<RecipientId> gv1MigrationSuggestions = groupViewModel.getGroupV1MigrationSuggestions().getValue();
|
||||
Boolean gv1MigrationBanner = groupViewModel.getShowGroupsV1MigrationBanner().getValue();
|
||||
|
||||
if (UnauthorizedReminder.isEligible(this)) {
|
||||
reminderView.get().showReminder(new UnauthorizedReminder(this));
|
||||
@@ -1735,6 +1741,15 @@ public class ConversationActivity extends PassphraseRequiredActivity
|
||||
startActivity(ManagePendingAndRequestingMembersActivity.newIntent(this, getRecipient().getGroupId().get().requireV2()));
|
||||
}
|
||||
});
|
||||
} else if (gv1MigrationBanner == Boolean.TRUE && recipient.get().isPushV1Group()) {
|
||||
reminderView.get().showReminder(new GroupsV1MigrationInitiationReminder(this));
|
||||
reminderView.get().setOnActionClickListener(actionId -> {
|
||||
if (actionId == R.id.reminder_action_gv1_initiation_update_group) {
|
||||
GroupsV1MigrationInitiationBottomSheetDialogFragment.showForInitiation(getSupportFragmentManager(), recipient.getId());
|
||||
} else if (actionId == R.id.reminder_action_gv1_initiation_not_now) {
|
||||
groupViewModel.onMigrationInitiationReminderBannerDismissed(recipient.getId());
|
||||
}
|
||||
});
|
||||
} else if (gv1MigrationSuggestions != null && gv1MigrationSuggestions.size() > 0 && recipient.get().isPushV2Group()) {
|
||||
reminderView.get().showReminder(new GroupsV1MigrationSuggestionsReminder(this, gv1MigrationSuggestions));
|
||||
reminderView.get().setOnActionClickListener(actionId -> {
|
||||
|
||||
@@ -91,7 +91,7 @@ import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||
import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.groups.GroupId;
|
||||
import org.thoughtcrime.securesms.groups.ui.migration.GroupsV1MigrationBottomSheetDialogFragment;
|
||||
import org.thoughtcrime.securesms.groups.ui.migration.GroupsV1MigrationInfoBottomSheetDialogFragment;
|
||||
import org.thoughtcrime.securesms.jobs.DirectoryRefreshJob;
|
||||
import org.thoughtcrime.securesms.jobs.MultiDeviceViewOnceOpenJob;
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||
@@ -1427,7 +1427,7 @@ public class ConversationFragment extends LoggingFragment {
|
||||
|
||||
@Override
|
||||
public void onGroupMigrationLearnMoreClicked(@NonNull List<RecipientId> pendingRecipients) {
|
||||
GroupsV1MigrationBottomSheetDialogFragment.showForLearnMore(requireFragmentManager(), pendingRecipients);
|
||||
GroupsV1MigrationInfoBottomSheetDialogFragment.showForLearnMore(requireFragmentManager(), pendingRecipients);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.thoughtcrime.securesms.conversation;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
@@ -16,13 +17,13 @@ import com.annimon.stream.Stream;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.GroupDatabase;
|
||||
import org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord;
|
||||
import org.thoughtcrime.securesms.database.RecipientDatabase;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.groups.GroupChangeBusyException;
|
||||
import org.thoughtcrime.securesms.groups.GroupChangeFailedException;
|
||||
import org.thoughtcrime.securesms.groups.GroupId;
|
||||
import org.thoughtcrime.securesms.groups.GroupManager;
|
||||
import org.thoughtcrime.securesms.groups.ui.GroupChangeFailureReason;
|
||||
import org.thoughtcrime.securesms.groups.GroupsV1MigrationUtil;
|
||||
import org.thoughtcrime.securesms.profiles.spoofing.ReviewRecipient;
|
||||
import org.thoughtcrime.securesms.profiles.spoofing.ReviewUtil;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
@@ -37,15 +38,19 @@ import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
final class ConversationGroupViewModel extends ViewModel {
|
||||
|
||||
private static final long GV1_MIGRATION_REMINDER_INTERVAL = TimeUnit.DAYS.toMillis(1);
|
||||
|
||||
private final MutableLiveData<Recipient> liveRecipient;
|
||||
private final LiveData<GroupActiveState> groupActiveState;
|
||||
private final LiveData<GroupDatabase.MemberLevel> selfMembershipLevel;
|
||||
private final LiveData<Integer> actionableRequestingMembers;
|
||||
private final LiveData<ReviewState> reviewState;
|
||||
private final LiveData<List<RecipientId>> gv1MigrationSuggestions;
|
||||
private final LiveData<Boolean> gv1MigrationReminder;
|
||||
|
||||
private ConversationGroupViewModel() {
|
||||
this.liveRecipient = new MutableLiveData<>();
|
||||
@@ -65,6 +70,7 @@ final class ConversationGroupViewModel extends ViewModel {
|
||||
this.selfMembershipLevel = Transformations.distinctUntilChanged(Transformations.map(groupRecord, ConversationGroupViewModel::mapToSelfMembershipLevel));
|
||||
this.actionableRequestingMembers = Transformations.distinctUntilChanged(Transformations.map(groupRecord, ConversationGroupViewModel::mapToActionableRequestingMemberCount));
|
||||
this.gv1MigrationSuggestions = Transformations.distinctUntilChanged(LiveDataUtil.mapAsync(groupRecord, ConversationGroupViewModel::mapToGroupV1MigrationSuggestions));
|
||||
this.gv1MigrationReminder = Transformations.distinctUntilChanged(LiveDataUtil.mapAsync(groupRecord, ConversationGroupViewModel::mapToGroupV1MigrationReminder));
|
||||
this.reviewState = LiveDataUtil.combineLatest(groupRecord,
|
||||
duplicates,
|
||||
(record, dups) -> dups.isEmpty()
|
||||
@@ -86,6 +92,13 @@ final class ConversationGroupViewModel extends ViewModel {
|
||||
});
|
||||
}
|
||||
|
||||
void onMigrationInitiationReminderBannerDismissed(@NonNull RecipientId recipientId) {
|
||||
SignalExecutors.BOUNDED.execute(() -> {
|
||||
DatabaseFactory.getRecipientDatabase(ApplicationDependencies.getApplication()).markGroupsV1MigrationReminderSeen(recipientId, System.currentTimeMillis());
|
||||
liveRecipient.postValue(liveRecipient.getValue());
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* The number of pending group join requests that can be actioned by this client.
|
||||
*/
|
||||
@@ -109,6 +122,10 @@ final class ConversationGroupViewModel extends ViewModel {
|
||||
return gv1MigrationSuggestions;
|
||||
}
|
||||
|
||||
@NonNull LiveData<Boolean> getShowGroupsV1MigrationBanner() {
|
||||
return gv1MigrationReminder;
|
||||
}
|
||||
|
||||
private static @Nullable GroupRecord getGroupRecordForRecipient(@Nullable Recipient recipient) {
|
||||
if (recipient != null && recipient.isGroup()) {
|
||||
Application context = ApplicationDependencies.getApplication();
|
||||
@@ -156,15 +173,30 @@ final class ConversationGroupViewModel extends ViewModel {
|
||||
Set<RecipientId> difference = SetUtil.difference(record.getFormerV1Members(), record.getMembers());
|
||||
|
||||
return Stream.of(Recipient.resolvedList(difference))
|
||||
.filter(r -> r.hasUuid() &&
|
||||
r.getGroupsV1MigrationCapability() == Recipient.Capability.SUPPORTED &&
|
||||
r.getGroupsV2Capability() == Recipient.Capability.SUPPORTED &&
|
||||
r.getProfileKey() != null &&
|
||||
r.getRegistered() == RecipientDatabase.RegisteredState.REGISTERED)
|
||||
.filter(GroupsV1MigrationUtil::isAutoMigratable)
|
||||
.map(Recipient::getId)
|
||||
.toList();
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
private static boolean mapToGroupV1MigrationReminder(@Nullable GroupRecord record) {
|
||||
if (record == null || !record.isV1Group() || !record.isActive() || !FeatureFlags.groupsV1ManualMigration()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean canAutoMigrate = Stream.of(Recipient.resolvedList(record.getMembers()))
|
||||
.allMatch(GroupsV1MigrationUtil::isAutoMigratable);
|
||||
|
||||
if (canAutoMigrate) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Context context = ApplicationDependencies.getApplication();
|
||||
long lastReminderTime = DatabaseFactory.getRecipientDatabase(context).getGroupsV1MigrationReminderLastSeen(record.getRecipientId());
|
||||
|
||||
return System.currentTimeMillis() - lastReminderTime > GV1_MIGRATION_REMINDER_INTERVAL;
|
||||
}
|
||||
|
||||
public static void onCancelJoinRequest(@NonNull Recipient recipient,
|
||||
@NonNull AsynchronousCallback.WorkerThread<Void, GroupChangeFailureReason> callback)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user