mirror of
https://github.com/oxen-io/session-android.git
synced 2025-12-31 12:06:12 +00:00
Add 'Add to a group' button to bottom sheet.
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
package org.thoughtcrime.securesms.groups.ui.addtogroup;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
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.GroupInsufficientRightsException;
|
||||
import org.thoughtcrime.securesms.groups.GroupManager;
|
||||
import org.thoughtcrime.securesms.groups.GroupNotAMemberException;
|
||||
import org.thoughtcrime.securesms.groups.MembershipNotSuitableForV2Exception;
|
||||
import org.thoughtcrime.securesms.groups.ui.GroupChangeErrorCallback;
|
||||
import org.thoughtcrime.securesms.groups.ui.GroupChangeFailureReason;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
import org.thoughtcrime.securesms.util.concurrent.SignalExecutors;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
|
||||
final class AddToGroupRepository {
|
||||
|
||||
private static final String TAG = Log.tag(AddToGroupRepository.class);
|
||||
|
||||
private final Context context;
|
||||
|
||||
AddToGroupRepository() {
|
||||
this.context = ApplicationDependencies.getApplication();
|
||||
}
|
||||
|
||||
public void add(@NonNull RecipientId recipientId,
|
||||
@NonNull Recipient groupRecipient,
|
||||
@NonNull GroupChangeErrorCallback error,
|
||||
@NonNull Runnable success)
|
||||
{
|
||||
SignalExecutors.UNBOUNDED.execute(() -> {
|
||||
try {
|
||||
GroupId.Push pushGroupId = groupRecipient.requireGroupId().requirePush();
|
||||
|
||||
GroupManager.addMembers(context, pushGroupId, Collections.singletonList(recipientId));
|
||||
|
||||
success.run();
|
||||
} catch (GroupInsufficientRightsException | GroupNotAMemberException e) {
|
||||
Log.w(TAG, e);
|
||||
error.onError(GroupChangeFailureReason.NO_RIGHTS);
|
||||
} catch (GroupChangeFailedException | GroupChangeBusyException | IOException e) {
|
||||
Log.w(TAG, e);
|
||||
error.onError(GroupChangeFailureReason.OTHER);
|
||||
} catch (MembershipNotSuitableForV2Exception e) {
|
||||
Log.w(TAG, e);
|
||||
error.onError(GroupChangeFailureReason.NOT_CAPABLE);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
package org.thoughtcrime.securesms.groups.ui.addtogroup;
|
||||
|
||||
import android.app.Application;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.lifecycle.ViewModel;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.groups.ui.GroupErrors;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
import org.thoughtcrime.securesms.util.SingleLiveEvent;
|
||||
import org.thoughtcrime.securesms.util.concurrent.SignalExecutors;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public final class AddToGroupViewModel extends ViewModel {
|
||||
|
||||
private final Application context;
|
||||
private final AddToGroupRepository repository;
|
||||
private final RecipientId recipientId;
|
||||
private final SingleLiveEvent<Event> events = new SingleLiveEvent<>();
|
||||
|
||||
private AddToGroupViewModel(@NonNull RecipientId recipientId) {
|
||||
this.context = ApplicationDependencies.getApplication();
|
||||
this.recipientId = recipientId;
|
||||
this.repository = new AddToGroupRepository();
|
||||
}
|
||||
|
||||
public SingleLiveEvent<Event> getEvents() {
|
||||
return events;
|
||||
}
|
||||
|
||||
void onContinueWithSelection(@NonNull List<RecipientId> groupRecipientIds) {
|
||||
if (groupRecipientIds.isEmpty()) {
|
||||
events.postValue(new Event.CloseEvent());
|
||||
} else if (groupRecipientIds.size() == 1) {
|
||||
SignalExecutors.BOUNDED.execute(() -> {
|
||||
Recipient groupRecipient = Recipient.resolved(groupRecipientIds.get(0));
|
||||
String recipientName = Recipient.resolved(recipientId).getDisplayName(context);
|
||||
String groupName = groupRecipient.getDisplayName(context);
|
||||
|
||||
events.postValue(new Event.AddToSingleGroupConfirmationEvent(context.getResources().getString(R.string.AddToGroupActivity_add_member),
|
||||
context.getResources().getString(R.string.AddToGroupActivity_add_s_to_s, recipientName, groupName),
|
||||
groupRecipient, recipientName, groupName));
|
||||
});
|
||||
} else {
|
||||
throw new AssertionError("Does not support multi-select");
|
||||
}
|
||||
}
|
||||
|
||||
void onAddToGroupsConfirmed(@NonNull Event.AddToSingleGroupConfirmationEvent event) {
|
||||
repository.add(recipientId,
|
||||
event.groupRecipient,
|
||||
error -> events.postValue(new Event.ToastEvent(context.getResources().getString(GroupErrors.getUserDisplayMessage(error)))),
|
||||
() -> {
|
||||
events.postValue(new Event.ToastEvent(context.getResources().getString(R.string.AddToGroupActivity_s_added_to_s, event.recipientName, event.groupName)));
|
||||
events.postValue(new Event.CloseEvent());
|
||||
});
|
||||
}
|
||||
|
||||
static abstract class Event {
|
||||
|
||||
static class CloseEvent extends Event {
|
||||
}
|
||||
|
||||
static class ToastEvent extends Event {
|
||||
private final String message;
|
||||
|
||||
ToastEvent(@NonNull String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
}
|
||||
|
||||
static class AddToSingleGroupConfirmationEvent extends Event {
|
||||
private final String title;
|
||||
private final String message;
|
||||
private final Recipient groupRecipient;
|
||||
private final String recipientName;
|
||||
private final String groupName;
|
||||
|
||||
AddToSingleGroupConfirmationEvent(@NonNull String title,
|
||||
@NonNull String message,
|
||||
@NonNull Recipient groupRecipient,
|
||||
@NonNull String recipientName,
|
||||
@NonNull String groupName)
|
||||
{
|
||||
this.title = title;
|
||||
this.message = message;
|
||||
this.groupRecipient = groupRecipient;
|
||||
this.recipientName = recipientName;
|
||||
this.groupName = groupName;
|
||||
}
|
||||
|
||||
String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
String getMessage() {
|
||||
return message;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class Factory implements ViewModelProvider.Factory {
|
||||
|
||||
private final RecipientId recipientId;
|
||||
|
||||
public Factory(@NonNull RecipientId recipientId) {
|
||||
this.recipientId = recipientId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
|
||||
return Objects.requireNonNull(modelClass.cast(new AddToGroupViewModel(recipientId)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
package org.thoughtcrime.securesms.groups.ui.addtogroup;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.lifecycle.ViewModelProviders;
|
||||
|
||||
import com.annimon.stream.Stream;
|
||||
|
||||
import org.thoughtcrime.securesms.ContactSelectionActivity;
|
||||
import org.thoughtcrime.securesms.ContactSelectionListFragment;
|
||||
import org.thoughtcrime.securesms.GroupCreateActivity;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.contacts.ContactsCursorLoader;
|
||||
import org.thoughtcrime.securesms.groups.ui.addtogroup.AddToGroupViewModel.Event;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
import org.thoughtcrime.securesms.util.FeatureFlags;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Group selection activity, will add a single member to selected groups.
|
||||
*/
|
||||
public final class AddToGroupsActivity extends ContactSelectionActivity {
|
||||
|
||||
private static final int MINIMUM_GROUP_SELECT_SIZE = 1;
|
||||
|
||||
private static final String EXTRA_RECIPIENT_ID = "RECIPIENT_ID";
|
||||
|
||||
private View next;
|
||||
private AddToGroupViewModel viewModel;
|
||||
|
||||
public static Intent newIntent(@NonNull Context context,
|
||||
@NonNull RecipientId recipientId,
|
||||
@NonNull List<RecipientId> currentGroupsMemberOf)
|
||||
{
|
||||
if (!FeatureFlags.newGroupUI()) {
|
||||
return new Intent(context, GroupCreateActivity.class);
|
||||
}
|
||||
|
||||
Intent intent = new Intent(context, AddToGroupsActivity.class);
|
||||
|
||||
intent.putExtra(ContactSelectionListFragment.MULTI_SELECT, false);
|
||||
intent.putExtra(ContactSelectionListFragment.REFRESHABLE, false);
|
||||
intent.putExtra(ContactSelectionActivity.EXTRA_LAYOUT_RES_ID, R.layout.add_to_group_activity);
|
||||
intent.putExtra(EXTRA_RECIPIENT_ID, recipientId);
|
||||
|
||||
intent.putExtra(ContactSelectionListFragment.DISPLAY_MODE, ContactsCursorLoader.DisplayMode.FLAG_ACTIVE_GROUPS);
|
||||
intent.putExtra(ContactSelectionListFragment.TOTAL_CAPACITY, ContactSelectionListFragment.NO_LIMIT);
|
||||
|
||||
intent.putParcelableArrayListExtra(ContactSelectionListFragment.CURRENT_SELECTION, new ArrayList<>(currentGroupsMemberOf));
|
||||
|
||||
return intent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle bundle, boolean ready) {
|
||||
super.onCreate(bundle, ready);
|
||||
|
||||
Objects.requireNonNull(getSupportActionBar()).setDisplayHomeAsUpEnabled(true);
|
||||
|
||||
next = findViewById(R.id.next);
|
||||
|
||||
getToolbar().setHint(contactsFragment.isMulti() ? R.string.AddToGroupActivity_add_to_groups : R.string.AddToGroupActivity_add_to_group);
|
||||
|
||||
next.setVisibility(contactsFragment.isMulti() ? View.VISIBLE : View.GONE);
|
||||
|
||||
disableNext();
|
||||
next.setOnClickListener(v -> handleNextPressed());
|
||||
|
||||
AddToGroupViewModel.Factory factory = new AddToGroupViewModel.Factory(getRecipientId());
|
||||
viewModel = ViewModelProviders.of(this, factory)
|
||||
.get(AddToGroupViewModel.class);
|
||||
|
||||
|
||||
viewModel.getEvents().observe(this, event -> {
|
||||
if (event instanceof Event.CloseEvent) {
|
||||
finish();
|
||||
} else if (event instanceof Event.ToastEvent) {
|
||||
Toast.makeText(this, ((Event.ToastEvent) event).getMessage(), Toast.LENGTH_SHORT).show();
|
||||
} else if (event instanceof Event.AddToSingleGroupConfirmationEvent) {
|
||||
Event.AddToSingleGroupConfirmationEvent addEvent = (Event.AddToSingleGroupConfirmationEvent) event;
|
||||
new AlertDialog.Builder(this)
|
||||
.setTitle(addEvent.getTitle())
|
||||
.setMessage(addEvent.getMessage())
|
||||
.setPositiveButton(android.R.string.ok, (dialog, which) -> viewModel.onAddToGroupsConfirmed(addEvent))
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.show();
|
||||
} else {
|
||||
throw new AssertionError();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private @NonNull RecipientId getRecipientId() {
|
||||
return getIntent().getParcelableExtra(EXTRA_RECIPIENT_ID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
if (item.getItemId() == android.R.id.home) {
|
||||
finish();
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onContactSelected(Optional<RecipientId> recipientId, String number) {
|
||||
if (contactsFragment.isMulti()) {
|
||||
if (contactsFragment.hasQueryFilter()) {
|
||||
getToolbar().clear();
|
||||
}
|
||||
|
||||
if (contactsFragment.getSelectedContactsCount() >= MINIMUM_GROUP_SELECT_SIZE) {
|
||||
enableNext();
|
||||
}
|
||||
} else {
|
||||
if (recipientId.isPresent()) {
|
||||
viewModel.onContinueWithSelection(Collections.singletonList(recipientId.get()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onContactDeselected(Optional<RecipientId> recipientId, String number) {
|
||||
if (contactsFragment.hasQueryFilter()) {
|
||||
getToolbar().clear();
|
||||
}
|
||||
|
||||
if (contactsFragment.getSelectedContactsCount() < MINIMUM_GROUP_SELECT_SIZE) {
|
||||
disableNext();
|
||||
}
|
||||
}
|
||||
|
||||
private void enableNext() {
|
||||
next.setEnabled(true);
|
||||
next.animate().alpha(1f);
|
||||
}
|
||||
|
||||
private void disableNext() {
|
||||
next.setEnabled(false);
|
||||
next.animate().alpha(0.5f);
|
||||
}
|
||||
|
||||
private void handleNextPressed() {
|
||||
List<RecipientId> groupsRecipientIds = Stream.of(contactsFragment.getSelectedContacts())
|
||||
.map(selectedContact -> selectedContact.getOrCreateRecipientId(this))
|
||||
.toList();
|
||||
|
||||
viewModel.onContinueWithSelection(groupsRecipientIds);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user