Add 'Add to a group' button to bottom sheet.
This commit is contained in:
parent
7e934eff5d
commit
e1bb773d85
17 changed files with 505 additions and 37 deletions
|
@ -492,6 +492,9 @@
|
|||
<activity android:name=".groups.ui.creategroup.CreateGroupActivity"
|
||||
android:theme="@style/TextSecure.LightNoActionBar" />
|
||||
|
||||
<activity android:name=".groups.ui.addtogroup.AddToGroupsActivity"
|
||||
android:theme="@style/TextSecure.LightNoActionBar" />
|
||||
|
||||
<activity android:name=".groups.ui.addmembers.AddMembersActivity"
|
||||
android:theme="@style/TextSecure.LightNoActionBar" />
|
||||
|
||||
|
|
|
@ -255,7 +255,7 @@ public final class ContactSelectionListFragment extends Fragment
|
|||
: Collections.unmodifiableSet(Stream.of(currentSelection).collect(Collectors.toSet()));
|
||||
}
|
||||
|
||||
private boolean isMulti() {
|
||||
public boolean isMulti() {
|
||||
return requireActivity().getIntent().getBooleanExtra(MULTI_SELECT, false);
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ import android.util.AttributeSet;
|
|||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.widget.AppCompatImageView;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
||||
|
||||
|
@ -24,6 +25,7 @@ import org.thoughtcrime.securesms.groups.ui.managegroup.ManageGroupActivity;
|
|||
import org.thoughtcrime.securesms.mms.GlideApp;
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.ui.bottomsheet.RecipientBottomSheetDialogFragment;
|
||||
import org.thoughtcrime.securesms.util.AvatarUtil;
|
||||
import org.thoughtcrime.securesms.util.FeatureFlags;
|
||||
import org.thoughtcrime.securesms.util.ThemeUtil;
|
||||
|
@ -162,11 +164,17 @@ public final class AvatarImageView extends AppCompatImageView {
|
|||
private void setAvatarClickHandler(@NonNull final Recipient recipient, boolean quickContactEnabled) {
|
||||
if (quickContactEnabled) {
|
||||
super.setOnClickListener(v -> {
|
||||
Context context = getContext();
|
||||
if (FeatureFlags.newGroupUI() && recipient.isPushGroup()) {
|
||||
getContext().startActivity(ManageGroupActivity.newIntent(getContext(), recipient.requireGroupId().requirePush()),
|
||||
ManageGroupActivity.createTransitionBundle(getContext(), this));
|
||||
context.startActivity(ManageGroupActivity.newIntent(context, recipient.requireGroupId().requirePush()),
|
||||
ManageGroupActivity.createTransitionBundle(context, this));
|
||||
} else {
|
||||
getContext().startActivity(RecipientPreferenceActivity.getLaunchIntent(getContext(), recipient.getId()));
|
||||
if (context instanceof FragmentActivity) {
|
||||
RecipientBottomSheetDialogFragment.create(recipient.getId(), null)
|
||||
.show(((FragmentActivity) context).getSupportFragmentManager(), "BOTTOM");
|
||||
} else {
|
||||
context.startActivity(RecipientPreferenceActivity.getLaunchIntent(context, recipient.getId()));
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
|
|
|
@ -3,11 +3,6 @@ package org.thoughtcrime.securesms.components;
|
|||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Rect;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.core.widget.TextViewCompat;
|
||||
|
||||
import android.text.Editable;
|
||||
import android.text.InputType;
|
||||
import android.text.TextWatcher;
|
||||
|
@ -18,20 +13,23 @@ import android.widget.EditText;
|
|||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.core.widget.TextViewCompat;
|
||||
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.util.ServiceUtil;
|
||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||
import org.thoughtcrime.securesms.util.views.DarkOverflowToolbar;
|
||||
|
||||
public class ContactFilterToolbar extends DarkOverflowToolbar {
|
||||
public final class ContactFilterToolbar extends DarkOverflowToolbar {
|
||||
private OnFilterChangedListener listener;
|
||||
|
||||
private EditText searchText;
|
||||
private AnimatingToggle toggle;
|
||||
private ImageView keyboardToggle;
|
||||
private ImageView dialpadToggle;
|
||||
private ImageView clearToggle;
|
||||
private LinearLayout toggleContainer;
|
||||
private final EditText searchText;
|
||||
private final AnimatingToggle toggle;
|
||||
private final ImageView keyboardToggle;
|
||||
private final ImageView dialpadToggle;
|
||||
private final ImageView clearToggle;
|
||||
private final LinearLayout toggleContainer;
|
||||
|
||||
public ContactFilterToolbar(Context context) {
|
||||
this(context, null);
|
||||
|
@ -45,12 +43,12 @@ public class ContactFilterToolbar extends DarkOverflowToolbar {
|
|||
super(context, attrs, defStyleAttr);
|
||||
inflate(context, R.layout.contact_filter_toolbar, this);
|
||||
|
||||
this.searchText = ViewUtil.findById(this, R.id.search_view);
|
||||
this.toggle = ViewUtil.findById(this, R.id.button_toggle);
|
||||
this.keyboardToggle = ViewUtil.findById(this, R.id.search_keyboard);
|
||||
this.dialpadToggle = ViewUtil.findById(this, R.id.search_dialpad);
|
||||
this.clearToggle = ViewUtil.findById(this, R.id.search_clear);
|
||||
this.toggleContainer = ViewUtil.findById(this, R.id.toggle_container);
|
||||
this.searchText = findViewById(R.id.search_view);
|
||||
this.toggle = findViewById(R.id.button_toggle);
|
||||
this.keyboardToggle = findViewById(R.id.search_keyboard);
|
||||
this.dialpadToggle = findViewById(R.id.search_dialpad);
|
||||
this.clearToggle = findViewById(R.id.search_clear);
|
||||
this.toggleContainer = findViewById(R.id.toggle_container);
|
||||
|
||||
this.keyboardToggle.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
|
@ -103,11 +101,11 @@ public class ContactFilterToolbar extends DarkOverflowToolbar {
|
|||
setLogo(null);
|
||||
setContentInsetStartWithNavigation(0);
|
||||
expandTapArea(toggleContainer, dialpadToggle);
|
||||
styleSearchText(searchText, context, attrs, defStyleAttr);
|
||||
applyAttributes(searchText, context, attrs, defStyleAttr);
|
||||
searchText.requestFocus();
|
||||
}
|
||||
|
||||
private void styleSearchText(@NonNull EditText searchText,
|
||||
private void applyAttributes(@NonNull EditText searchText,
|
||||
@NonNull Context context,
|
||||
@NonNull AttributeSet attrs,
|
||||
int defStyle)
|
||||
|
@ -121,6 +119,9 @@ public class ContactFilterToolbar extends DarkOverflowToolbar {
|
|||
if (styleResource != -1) {
|
||||
TextViewCompat.setTextAppearance(searchText, styleResource);
|
||||
}
|
||||
if (!attributes.getBoolean(R.styleable.ContactFilterToolbar_showDialpad, true)) {
|
||||
dialpadToggle.setVisibility(GONE);
|
||||
}
|
||||
attributes.recycle();
|
||||
}
|
||||
|
||||
|
@ -133,6 +134,10 @@ public class ContactFilterToolbar extends DarkOverflowToolbar {
|
|||
this.listener = listener;
|
||||
}
|
||||
|
||||
public void setHint(@StringRes int hint) {
|
||||
searchText.setHint(hint);
|
||||
}
|
||||
|
||||
private void notifyListener() {
|
||||
if (listener != null) listener.onFilterChanged(searchText.getText().toString());
|
||||
}
|
||||
|
|
|
@ -101,7 +101,7 @@ public class ContactSelectionListAdapter extends CursorRecyclerViewAdapter<ViewH
|
|||
super(itemView);
|
||||
}
|
||||
|
||||
public abstract void bind(@NonNull GlideRequests glideRequests, @Nullable RecipientId recipientId, int type, String name, String number, String label, int color, boolean multiSelect);
|
||||
public abstract void bind(@NonNull GlideRequests glideRequests, @Nullable RecipientId recipientId, int type, String name, String number, String label, int color, boolean checkboxVisible);
|
||||
public abstract void unbind(@NonNull GlideRequests glideRequests);
|
||||
public abstract void setChecked(boolean checked);
|
||||
public abstract void setEnabled(boolean enabled);
|
||||
|
@ -121,8 +121,8 @@ public class ContactSelectionListAdapter extends CursorRecyclerViewAdapter<ViewH
|
|||
return (ContactSelectionListItem) itemView;
|
||||
}
|
||||
|
||||
public void bind(@NonNull GlideRequests glideRequests, @Nullable RecipientId recipientId, int type, String name, String number, String label, int color, boolean multiSelect) {
|
||||
getView().set(glideRequests, recipientId, type, name, number, label, color, multiSelect);
|
||||
public void bind(@NonNull GlideRequests glideRequests, @Nullable RecipientId recipientId, int type, String name, String number, String label, int color, boolean checkBoxVisible) {
|
||||
getView().set(glideRequests, recipientId, type, name, number, label, color, checkBoxVisible);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -151,7 +151,7 @@ public class ContactSelectionListAdapter extends CursorRecyclerViewAdapter<ViewH
|
|||
}
|
||||
|
||||
@Override
|
||||
public void bind(@NonNull GlideRequests glideRequests, @Nullable RecipientId recipientId, int type, String name, String number, String label, int color, boolean multiSelect) {
|
||||
public void bind(@NonNull GlideRequests glideRequests, @Nullable RecipientId recipientId, int type, String name, String number, String label, int color, boolean checkboxVisible) {
|
||||
this.label.setText(name);
|
||||
}
|
||||
|
||||
|
@ -222,11 +222,13 @@ public class ContactSelectionListAdapter extends CursorRecyclerViewAdapter<ViewH
|
|||
int color = (contactType == ContactRepository.PUSH_TYPE) ? drawables.getColor(0, 0xa0000000) :
|
||||
drawables.getColor(1, 0xff000000);
|
||||
|
||||
boolean currentContact = currentContacts.contains(id);
|
||||
|
||||
viewHolder.unbind(glideRequests);
|
||||
viewHolder.bind(glideRequests, id, contactType, name, number, labelText, color, multiSelect);
|
||||
viewHolder.bind(glideRequests, id, contactType, name, number, labelText, color, multiSelect || currentContact);
|
||||
viewHolder.setEnabled(true);
|
||||
|
||||
if (currentContacts.contains(id)) {
|
||||
if (currentContact) {
|
||||
viewHolder.setChecked(true);
|
||||
viewHolder.setEnabled(false);
|
||||
} else if (numberType == ContactRepository.NEW_USERNAME_TYPE) {
|
||||
|
|
|
@ -67,7 +67,7 @@ public class ContactSelectionListItem extends LinearLayout implements RecipientF
|
|||
String number,
|
||||
String label,
|
||||
int color,
|
||||
boolean multiSelect)
|
||||
boolean checkboxVisible)
|
||||
{
|
||||
this.glideRequests = glideRequests;
|
||||
this.number = number;
|
||||
|
@ -90,8 +90,7 @@ public class ContactSelectionListItem extends LinearLayout implements RecipientF
|
|||
|
||||
setText(recipientSnapshot, type, name, number, label);
|
||||
|
||||
if (multiSelect) this.checkBox.setVisibility(View.VISIBLE);
|
||||
else this.checkBox.setVisibility(View.GONE);
|
||||
this.checkBox.setVisibility(checkboxVisible ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
|
||||
public void setChecked(boolean selected) {
|
||||
|
|
|
@ -115,8 +115,12 @@ public class ContactsCursorLoader extends CursorLoader {
|
|||
private List<Cursor> getUnfilteredResults() {
|
||||
ArrayList<Cursor> cursorList = new ArrayList<>();
|
||||
|
||||
addRecentsSection(cursorList);
|
||||
addContactsSection(cursorList);
|
||||
if (groupsOnly(mode)) {
|
||||
addGroupsSection(cursorList);
|
||||
} else {
|
||||
addRecentsSection(cursorList);
|
||||
addContactsSection(cursorList);
|
||||
}
|
||||
|
||||
return cursorList;
|
||||
}
|
||||
|
@ -376,6 +380,10 @@ public class ContactsCursorLoader extends CursorLoader {
|
|||
return flagSet(mode, DisplayMode.FLAG_ACTIVE_GROUPS);
|
||||
}
|
||||
|
||||
private static boolean groupsOnly(int mode) {
|
||||
return mode == DisplayMode.FLAG_ACTIVE_GROUPS;
|
||||
}
|
||||
|
||||
private static boolean flagSet(int mode, int flag) {
|
||||
return (mode & flag) > 0;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -50,6 +50,7 @@ public final class RecipientBottomSheetDialogFragment extends BottomSheetDialogF
|
|||
private Button blockButton;
|
||||
private Button unblockButton;
|
||||
private Button addContactButton;
|
||||
private Button addToGroupButton;
|
||||
private Button viewSafetyNumberButton;
|
||||
private Button makeGroupAdminButton;
|
||||
private Button removeAdminButton;
|
||||
|
@ -93,6 +94,7 @@ public final class RecipientBottomSheetDialogFragment extends BottomSheetDialogF
|
|||
blockButton = view.findViewById(R.id.rbs_block_button);
|
||||
unblockButton = view.findViewById(R.id.rbs_unblock_button);
|
||||
addContactButton = view.findViewById(R.id.rbs_add_contact_button);
|
||||
addToGroupButton = view.findViewById(R.id.rbs_add_to_group_button);
|
||||
viewSafetyNumberButton = view.findViewById(R.id.rbs_view_safety_number_button);
|
||||
makeGroupAdminButton = view.findViewById(R.id.rbs_make_group_admin_button);
|
||||
removeAdminButton = view.findViewById(R.id.rbs_remove_group_admin_button);
|
||||
|
@ -188,6 +190,11 @@ public final class RecipientBottomSheetDialogFragment extends BottomSheetDialogF
|
|||
|
||||
removeFromGroupButton.setOnClickListener(view -> viewModel.onRemoveFromGroupClicked(requireActivity(), this::dismiss));
|
||||
|
||||
addToGroupButton.setOnClickListener(view -> {
|
||||
dismiss();
|
||||
viewModel.onAddToGroupButton(requireActivity());
|
||||
});
|
||||
|
||||
viewModel.getAdminActionBusy().observe(getViewLifecycleOwner(), busy -> {
|
||||
adminActionBusy.setVisibility(busy ? View.VISIBLE : View.GONE);
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ import androidx.core.util.Consumer;
|
|||
|
||||
import org.thoughtcrime.securesms.contacts.sync.DirectoryHelper;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.GroupDatabase;
|
||||
import org.thoughtcrime.securesms.database.IdentityDatabase;
|
||||
import org.thoughtcrime.securesms.groups.GroupChangeBusyException;
|
||||
import org.thoughtcrime.securesms.groups.GroupChangeFailedException;
|
||||
|
@ -24,6 +25,8 @@ import org.thoughtcrime.securesms.util.concurrent.SignalExecutors;
|
|||
import org.thoughtcrime.securesms.util.concurrent.SimpleTask;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
final class RecipientDialogRepository {
|
||||
|
@ -117,6 +120,22 @@ final class RecipientDialogRepository {
|
|||
onComplete::accept);
|
||||
}
|
||||
|
||||
void getGroupMembership(@NonNull Consumer<List<RecipientId>> onComplete) {
|
||||
SimpleTask.run(SignalExecutors.UNBOUNDED,
|
||||
() -> {
|
||||
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
|
||||
List<GroupDatabase.GroupRecord> groupRecords = groupDatabase.getPushGroupsContainingMember(recipientId);
|
||||
ArrayList<RecipientId> groupRecipients = new ArrayList<>(groupRecords.size());
|
||||
|
||||
for (GroupDatabase.GroupRecord groupRecord : groupRecords) {
|
||||
groupRecipients.add(groupRecord.getRecipientId());
|
||||
}
|
||||
|
||||
return groupRecipients;
|
||||
},
|
||||
onComplete::accept);
|
||||
}
|
||||
|
||||
interface IdentityCallback {
|
||||
void remoteIdentity(@Nullable IdentityDatabase.IdentityRecord identityRecord);
|
||||
}
|
||||
|
|
|
@ -11,7 +11,6 @@ import androidx.appcompat.app.AlertDialog;
|
|||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
import androidx.lifecycle.Transformations;
|
||||
import androidx.lifecycle.ViewModel;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
|
||||
|
@ -24,6 +23,7 @@ import org.thoughtcrime.securesms.groups.GroupId;
|
|||
import org.thoughtcrime.securesms.groups.LiveGroup;
|
||||
import org.thoughtcrime.securesms.groups.ui.GroupChangeFailureReason;
|
||||
import org.thoughtcrime.securesms.groups.ui.GroupErrors;
|
||||
import org.thoughtcrime.securesms.groups.ui.addtogroup.AddToGroupsActivity;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientUtil;
|
||||
|
@ -171,6 +171,10 @@ final class RecipientDialogViewModel extends ViewModel {
|
|||
recipientDialogRepository.refreshRecipient();
|
||||
}
|
||||
|
||||
void onAddToGroupButton(@NonNull Activity activity) {
|
||||
recipientDialogRepository.getGroupMembership(existingGroups -> activity.startActivity(AddToGroupsActivity.newIntent(activity, recipientDialogRepository.getRecipientId(), existingGroups)));
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
private void showErrorToast(@NonNull GroupChangeFailureReason e) {
|
||||
Util.runOnMain(() -> Toast.makeText(context, GroupErrors.getUserDisplayMessage(e), Toast.LENGTH_LONG).show());
|
||||
|
|
46
app/src/main/res/layout/add_to_group_activity.xml
Normal file
46
app/src/main/res/layout/add_to_group_activity.xml
Normal file
|
@ -0,0 +1,46 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_gravity="center"
|
||||
android:orientation="vertical">
|
||||
|
||||
<org.thoughtcrime.securesms.components.ContactFilterToolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:elevation="4dp"
|
||||
android:minHeight="?attr/actionBarSize"
|
||||
android:theme="?attr/actionBarStyle"
|
||||
app:contentInsetStartWithNavigation="0dp"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:navigationIcon="@drawable/ic_arrow_left_24"
|
||||
app:showDialpad="false" />
|
||||
|
||||
<fragment
|
||||
android:id="@+id/contact_selection_list_fragment"
|
||||
android:name="org.thoughtcrime.securesms.ContactSelectionListFragment"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/toolbar" />
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/next"
|
||||
android:layout_width="56dp"
|
||||
android:layout_height="56dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:tint="@color/core_white"
|
||||
android:visibility="gone"
|
||||
app:backgroundTint="@color/core_ultramarine"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:srcCompat="@drawable/ic_arrow_end_24"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -119,6 +119,16 @@
|
|||
app:drawableStartCompat="?attr/recipient_add_contact_icon"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/rbs_add_to_group_button"
|
||||
style="@style/Widget.Signal.Button.TextButton.Drawable"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
android:paddingStart="20dp"
|
||||
android:paddingEnd="20dp"
|
||||
android:text="@string/RecipientBottomSheet_add_to_a_group"
|
||||
app:drawableStartCompat="?attr/recipient_make_admin_icon" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/rbs_view_safety_number_button"
|
||||
style="@style/Widget.Signal.Button.TextButton.Drawable"
|
||||
|
|
|
@ -484,6 +484,7 @@
|
|||
|
||||
<declare-styleable name="ContactFilterToolbar">
|
||||
<attr name="searchTextStyle" format="reference" />
|
||||
<attr name="showDialpad" format="boolean" />
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="SquareImageView">
|
||||
|
|
|
@ -449,6 +449,13 @@
|
|||
<string name="GroupCreateActivity_youre_already_in_the_group">You\'re already in the group.</string>
|
||||
<string name="GroupCreateActivity_remove_member_description">Remove member</string>
|
||||
|
||||
<!-- AddToGroupActivity -->
|
||||
<string name="AddToGroupActivity_add_member">Add member?</string>
|
||||
<string name="AddToGroupActivity_add_s_to_s">Add \"%1$s\" to \"%2$s\"?</string>
|
||||
<string name="AddToGroupActivity_s_added_to_s">\"%1$s\" added to \"%2$s\".</string>
|
||||
<string name="AddToGroupActivity_add_to_group">Add to group</string>
|
||||
<string name="AddToGroupActivity_add_to_groups">Add to groups</string>
|
||||
|
||||
<!-- GroupShareProfileView -->
|
||||
<string name="GroupShareProfileView_share_your_profile_name_and_photo_with_this_group">Share your profile name and photo with this group?</string>
|
||||
<string name="GroupShareProfileView_do_you_want_to_make_your_profile_name_and_photo_visible_to_all_current_and_future_members_of_this_group">Do you want to make your profile name and photo visible to all current and future members of this group?</string>
|
||||
|
@ -2303,6 +2310,7 @@
|
|||
<string name="RecipientBottomSheet_block">Block</string>
|
||||
<string name="RecipientBottomSheet_unblock">Unblock</string>
|
||||
<string name="RecipientBottomSheet_add_to_contacts">Add to contacts</string>
|
||||
<string name="RecipientBottomSheet_add_to_a_group">Add to a group</string>
|
||||
<string name="RecipientBottomSheet_view_safety_number">View safety number</string>
|
||||
<string name="RecipientBottomSheet_make_group_admin">Make group admin</string>
|
||||
<string name="RecipientBottomSheet_remove_as_admin">Remove as admin</string>
|
||||
|
|
Loading…
Add table
Reference in a new issue