Add 'Add to a group' button to bottom sheet.

fork-5.53.8
Alan Evans 2020-06-09 12:09:59 -03:00 zatwierdzone przez GitHub
rodzic 7e934eff5d
commit e1bb773d85
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
17 zmienionych plików z 505 dodań i 37 usunięć

Wyświetl plik

@ -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" />

Wyświetl plik

@ -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);
}

Wyświetl plik

@ -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 {

Wyświetl plik

@ -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());
}

Wyświetl plik

@ -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) {

Wyświetl plik

@ -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) {

Wyświetl plik

@ -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;
}

Wyświetl plik

@ -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);
}
});
}
}

Wyświetl plik

@ -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)));
}
}
}

Wyświetl plik

@ -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);
}
}

Wyświetl plik

@ -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);

Wyświetl plik

@ -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);
}

Wyświetl plik

@ -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());

Wyświetl plik

@ -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>

Wyświetl plik

@ -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"

Wyświetl plik

@ -484,6 +484,7 @@
<declare-styleable name="ContactFilterToolbar">
<attr name="searchTextStyle" format="reference" />
<attr name="showDialpad" format="boolean" />
</declare-styleable>
<declare-styleable name="SquareImageView">

Wyświetl plik

@ -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>