Improve contact blocking UX via settings.
This commit is contained in:
parent
d6a230a235
commit
e9c7b120a0
18 changed files with 755 additions and 223 deletions
|
@ -443,7 +443,7 @@
|
||||||
android:theme="@style/TextSecure.FullScreenMedia"
|
android:theme="@style/TextSecure.FullScreenMedia"
|
||||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
||||||
|
|
||||||
<activity android:name=".BlockedContactsActivity"
|
<activity android:name=".blocked.BlockedUsersActivity"
|
||||||
android:theme="@style/TextSecure.LightTheme"
|
android:theme="@style/TextSecure.LightTheme"
|
||||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
||||||
|
|
||||||
|
|
|
@ -1,139 +0,0 @@
|
||||||
package org.thoughtcrime.securesms;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.database.Cursor;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.AdapterView;
|
|
||||||
import android.widget.ListView;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import androidx.cursoradapter.widget.CursorAdapter;
|
|
||||||
import androidx.fragment.app.ListFragment;
|
|
||||||
import androidx.loader.app.LoaderManager;
|
|
||||||
import androidx.loader.content.Loader;
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.database.RecipientDatabase;
|
|
||||||
import org.thoughtcrime.securesms.database.loaders.BlockedContactsLoader;
|
|
||||||
import org.thoughtcrime.securesms.mms.GlideApp;
|
|
||||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
|
||||||
import org.thoughtcrime.securesms.preferences.BlockedContactListItem;
|
|
||||||
import org.thoughtcrime.securesms.recipients.LiveRecipient;
|
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientUtil;
|
|
||||||
import org.thoughtcrime.securesms.util.DynamicTheme;
|
|
||||||
|
|
||||||
public class BlockedContactsActivity extends PassphraseRequiredActivity {
|
|
||||||
|
|
||||||
private final DynamicTheme dynamicTheme = new DynamicTheme();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPreCreate() {
|
|
||||||
dynamicTheme.onCreate(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate(Bundle bundle, boolean ready) {
|
|
||||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
|
||||||
getSupportActionBar().setTitle(R.string.BlockedContactsActivity_blocked_contacts);
|
|
||||||
initFragment(android.R.id.content, new BlockedContactsFragment());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onResume() {
|
|
||||||
super.onResume();
|
|
||||||
dynamicTheme.onResume(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onSupportNavigateUp() {
|
|
||||||
onBackPressed();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class BlockedContactsFragment
|
|
||||||
extends ListFragment
|
|
||||||
implements LoaderManager.LoaderCallbacks<Cursor>, ListView.OnItemClickListener
|
|
||||||
{
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
|
|
||||||
return inflater.inflate(R.layout.blocked_contacts_fragment, container, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate(Bundle bundle) {
|
|
||||||
super.onCreate(bundle);
|
|
||||||
setListAdapter(new BlockedContactAdapter(requireActivity(), GlideApp.with(this), null));
|
|
||||||
LoaderManager.getInstance(this).initLoader(0, null, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onStart() {
|
|
||||||
super.onStart();
|
|
||||||
LoaderManager.getInstance(this).restartLoader(0, null, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onActivityCreated(Bundle bundle) {
|
|
||||||
super.onActivityCreated(bundle);
|
|
||||||
getListView().setOnItemClickListener(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull Loader<Cursor> onCreateLoader(int id, Bundle args) {
|
|
||||||
return new BlockedContactsLoader(getActivity());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onLoadFinished(@NonNull Loader<Cursor> loader, Cursor data) {
|
|
||||||
if (getListAdapter() != null) {
|
|
||||||
((CursorAdapter) getListAdapter()).changeCursor(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onLoaderReset(@NonNull Loader<Cursor> loader) {
|
|
||||||
if (getListAdapter() != null) {
|
|
||||||
((CursorAdapter) getListAdapter()).changeCursor(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
|
||||||
Recipient recipient = ((BlockedContactListItem)view).getRecipient();
|
|
||||||
BlockUnblockDialog.showUnblockFor(requireContext(), getLifecycle(), recipient, () -> {
|
|
||||||
RecipientUtil.unblock(requireContext(), recipient);
|
|
||||||
LoaderManager.getInstance(this).restartLoader(0, null, this);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class BlockedContactAdapter extends CursorAdapter {
|
|
||||||
|
|
||||||
private final GlideRequests glideRequests;
|
|
||||||
|
|
||||||
BlockedContactAdapter(@NonNull Context context, @NonNull GlideRequests glideRequests, @Nullable Cursor c) {
|
|
||||||
super(context, c);
|
|
||||||
this.glideRequests = glideRequests;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public View newView(Context context, Cursor cursor, ViewGroup parent) {
|
|
||||||
return LayoutInflater.from(context)
|
|
||||||
.inflate(R.layout.blocked_contact_list_item, parent, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void bindView(View view, Context context, Cursor cursor) {
|
|
||||||
RecipientId recipientId = RecipientId.from(cursor.getLong(cursor.getColumnIndexOrThrow(RecipientDatabase.ID)));
|
|
||||||
LiveRecipient recipient = Recipient.live(recipientId);
|
|
||||||
|
|
||||||
((BlockedContactListItem) view).set(glideRequests, recipient);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,167 @@
|
||||||
|
package org.thoughtcrime.securesms.blocked;
|
||||||
|
|
||||||
|
import android.app.AlertDialog;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.ViewSwitcher;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.StringRes;
|
||||||
|
import androidx.appcompat.widget.Toolbar;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.lifecycle.ViewModelProviders;
|
||||||
|
|
||||||
|
import com.google.android.material.snackbar.Snackbar;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.ContactSelectionListFragment;
|
||||||
|
import org.thoughtcrime.securesms.PassphraseRequiredActivity;
|
||||||
|
import org.thoughtcrime.securesms.R;
|
||||||
|
import org.thoughtcrime.securesms.components.ContactFilterToolbar;
|
||||||
|
import org.thoughtcrime.securesms.contacts.ContactsCursorLoader;
|
||||||
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
|
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme;
|
||||||
|
import org.thoughtcrime.securesms.util.DynamicTheme;
|
||||||
|
import org.whispersystems.libsignal.util.guava.Optional;
|
||||||
|
|
||||||
|
public class BlockedUsersActivity extends PassphraseRequiredActivity implements BlockedUsersFragment.Listener, ContactSelectionListFragment.OnContactSelectedListener {
|
||||||
|
|
||||||
|
private static final String CONTACT_SELECTION_FRAGMENT = "Contact.Selection.Fragment";
|
||||||
|
|
||||||
|
private final DynamicTheme dynamicTheme = new DynamicNoActionBarTheme();
|
||||||
|
|
||||||
|
private BlockedUsersViewModel viewModel;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState, boolean ready) {
|
||||||
|
super.onCreate(savedInstanceState, ready);
|
||||||
|
|
||||||
|
dynamicTheme.onCreate(this);
|
||||||
|
|
||||||
|
setContentView(R.layout.blocked_users_activity);
|
||||||
|
|
||||||
|
BlockedUsersRepository repository = new BlockedUsersRepository(this);
|
||||||
|
BlockedUsersViewModel.Factory factory = new BlockedUsersViewModel.Factory(repository);
|
||||||
|
|
||||||
|
viewModel = ViewModelProviders.of(this, factory).get(BlockedUsersViewModel.class);
|
||||||
|
|
||||||
|
ViewSwitcher viewSwitcher = findViewById(R.id.toolbar_switcher);
|
||||||
|
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||||
|
ContactFilterToolbar contactFilterToolbar = findViewById(R.id.filter_toolbar);
|
||||||
|
View container = findViewById(R.id.fragment_container);
|
||||||
|
|
||||||
|
toolbar.setNavigationOnClickListener(unused -> onBackPressed());
|
||||||
|
contactFilterToolbar.setNavigationOnClickListener(unused -> onBackPressed());
|
||||||
|
contactFilterToolbar.setOnFilterChangedListener(query -> {
|
||||||
|
Fragment fragment = getSupportFragmentManager().findFragmentByTag(CONTACT_SELECTION_FRAGMENT);
|
||||||
|
if (fragment != null) {
|
||||||
|
((ContactSelectionListFragment) fragment).setQueryFilter(query);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
contactFilterToolbar.setHint(R.string.BlockedUsersActivity__add_blocked_user);
|
||||||
|
|
||||||
|
//noinspection CodeBlock2Expr
|
||||||
|
getSupportFragmentManager().addOnBackStackChangedListener(() -> {
|
||||||
|
viewSwitcher.setDisplayedChild(getSupportFragmentManager().getBackStackEntryCount());
|
||||||
|
});
|
||||||
|
|
||||||
|
getSupportFragmentManager().beginTransaction()
|
||||||
|
.add(R.id.fragment_container, new BlockedUsersFragment())
|
||||||
|
.commit();
|
||||||
|
|
||||||
|
viewModel.getEvents().observe(this, event -> handleEvent(container, event));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
|
||||||
|
dynamicTheme.onResume(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onBeforeContactSelected(Optional<RecipientId> recipientId, String number) {
|
||||||
|
final String displayName = recipientId.transform(id -> Recipient.resolved(id).getDisplayName(this)).or(number);
|
||||||
|
|
||||||
|
AlertDialog confirmationDialog = new AlertDialog.Builder(BlockedUsersActivity.this)
|
||||||
|
.setTitle(R.string.BlockedUsersActivity__block_user)
|
||||||
|
.setMessage(getString(R.string.BlockedUserActivity__s_will_not_be_able_to, displayName))
|
||||||
|
.setPositiveButton(R.string.BlockedUsersActivity__block, (dialog, which) -> {
|
||||||
|
if (recipientId.isPresent()) {
|
||||||
|
viewModel.block(recipientId.get());
|
||||||
|
} else {
|
||||||
|
viewModel.createAndBlock(number);
|
||||||
|
}
|
||||||
|
dialog.dismiss();
|
||||||
|
onBackPressed();
|
||||||
|
})
|
||||||
|
.setNegativeButton(android.R.string.cancel, (dialog, which) -> dialog.dismiss())
|
||||||
|
.setCancelable(true)
|
||||||
|
.create();
|
||||||
|
|
||||||
|
confirmationDialog.setOnShowListener(dialog -> {
|
||||||
|
confirmationDialog.getButton(AlertDialog.BUTTON_POSITIVE).setTextColor(Color.RED);
|
||||||
|
});
|
||||||
|
|
||||||
|
confirmationDialog.show();
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onContactDeselected(Optional<RecipientId> recipientId, String number) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleAddUserToBlockedList() {
|
||||||
|
ContactSelectionListFragment fragment = new ContactSelectionListFragment();
|
||||||
|
Intent intent = getIntent();
|
||||||
|
|
||||||
|
fragment.setOnContactSelectedListener(this);
|
||||||
|
|
||||||
|
intent.putExtra(ContactSelectionListFragment.REFRESHABLE, false);
|
||||||
|
intent.putExtra(ContactSelectionListFragment.SELECTION_LIMITS, 1);
|
||||||
|
intent.putExtra(ContactSelectionListFragment.HIDE_COUNT, true);
|
||||||
|
intent.putExtra(ContactSelectionListFragment.DISPLAY_MODE,
|
||||||
|
ContactsCursorLoader.DisplayMode.FLAG_PUSH |
|
||||||
|
ContactsCursorLoader.DisplayMode.FLAG_SMS |
|
||||||
|
ContactsCursorLoader.DisplayMode.FLAG_ACTIVE_GROUPS |
|
||||||
|
ContactsCursorLoader.DisplayMode.FLAG_INACTIVE_GROUPS |
|
||||||
|
ContactsCursorLoader.DisplayMode.FLAG_BLOCK);
|
||||||
|
|
||||||
|
getSupportFragmentManager().beginTransaction()
|
||||||
|
.replace(R.id.fragment_container, fragment, CONTACT_SELECTION_FRAGMENT)
|
||||||
|
.addToBackStack(null)
|
||||||
|
.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleEvent(@NonNull View view, @NonNull BlockedUsersViewModel.Event event) {
|
||||||
|
final String displayName;
|
||||||
|
|
||||||
|
if (event.getRecipient() == null) {
|
||||||
|
displayName = event.getNumber();
|
||||||
|
} else {
|
||||||
|
displayName = event.getRecipient().getDisplayName(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
final @StringRes int messageResId;
|
||||||
|
switch (event.getEventType()) {
|
||||||
|
case BLOCK_SUCCEEDED:
|
||||||
|
messageResId = R.string.BlockedUsersActivity__s_has_been_blocked;
|
||||||
|
break;
|
||||||
|
case BLOCK_FAILED:
|
||||||
|
messageResId = R.string.BlockedUsersActivity__failed_to_block_s;
|
||||||
|
break;
|
||||||
|
case UNBLOCK_SUCCEEDED:
|
||||||
|
messageResId = R.string.BlockedUsersActivity__s_has_been_unblocked;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("Unsupported event type " + event);
|
||||||
|
}
|
||||||
|
|
||||||
|
Snackbar.make(view, getString(messageResId, displayName), Snackbar.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,96 @@
|
||||||
|
package org.thoughtcrime.securesms.blocked;
|
||||||
|
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.core.util.Consumer;
|
||||||
|
import androidx.recyclerview.widget.DiffUtil;
|
||||||
|
import androidx.recyclerview.widget.ListAdapter;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.R;
|
||||||
|
import org.thoughtcrime.securesms.components.AvatarImageView;
|
||||||
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
final class BlockedUsersAdapter extends ListAdapter<Recipient, BlockedUsersAdapter.ViewHolder> {
|
||||||
|
|
||||||
|
private final RecipientClickedListener recipientClickedListener;
|
||||||
|
|
||||||
|
BlockedUsersAdapter(@NonNull RecipientClickedListener recipientClickedListener) {
|
||||||
|
super(new RecipientDiffCallback());
|
||||||
|
|
||||||
|
this.recipientClickedListener = recipientClickedListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NonNull ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||||
|
return new ViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.blocked_users_adapter_item, parent, false),
|
||||||
|
position -> recipientClickedListener.onRecipientClicked(Objects.requireNonNull(getItem(position))));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
||||||
|
holder.bind(Objects.requireNonNull(getItem(position)));
|
||||||
|
}
|
||||||
|
|
||||||
|
static final class ViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
|
||||||
|
private final AvatarImageView avatar;
|
||||||
|
private final TextView displayName;
|
||||||
|
private final TextView numberOrUsername;
|
||||||
|
|
||||||
|
public ViewHolder(@NonNull View itemView, Consumer<Integer> clickConsumer) {
|
||||||
|
super(itemView);
|
||||||
|
|
||||||
|
this.avatar = itemView.findViewById(R.id.avatar);
|
||||||
|
this.displayName = itemView.findViewById(R.id.display_name);
|
||||||
|
this.numberOrUsername = itemView.findViewById(R.id.number_or_username);
|
||||||
|
|
||||||
|
itemView.setOnClickListener(unused -> {
|
||||||
|
if (getAdapterPosition() != RecyclerView.NO_POSITION) {
|
||||||
|
clickConsumer.accept(getAdapterPosition());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void bind(@NonNull Recipient recipient) {
|
||||||
|
avatar.setAvatar(recipient);
|
||||||
|
displayName.setText(recipient.getDisplayName(itemView.getContext()));
|
||||||
|
|
||||||
|
if (recipient.hasAUserSetDisplayName(itemView.getContext())) {
|
||||||
|
String identifier = recipient.getE164().or(recipient.getUsername()).orNull();
|
||||||
|
|
||||||
|
if (identifier != null) {
|
||||||
|
numberOrUsername.setText(identifier);
|
||||||
|
numberOrUsername.setVisibility(View.VISIBLE);
|
||||||
|
} else {
|
||||||
|
numberOrUsername.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
numberOrUsername.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class RecipientDiffCallback extends DiffUtil.ItemCallback<Recipient> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean areItemsTheSame(@NonNull Recipient oldItem, @NonNull Recipient newItem) {
|
||||||
|
return oldItem.equals(newItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean areContentsTheSame(@NonNull Recipient oldItem, @NonNull Recipient newItem) {
|
||||||
|
return oldItem.equals(newItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RecipientClickedListener {
|
||||||
|
void onRecipientClicked(@NonNull Recipient recipient);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,100 @@
|
||||||
|
package org.thoughtcrime.securesms.blocked;
|
||||||
|
|
||||||
|
import android.app.AlertDialog;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.lifecycle.ViewModelProviders;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.R;
|
||||||
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
|
|
||||||
|
public class BlockedUsersFragment extends Fragment {
|
||||||
|
|
||||||
|
private BlockedUsersViewModel viewModel;
|
||||||
|
private Listener listener;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAttach(@NonNull Context context) {
|
||||||
|
super.onAttach(context);
|
||||||
|
|
||||||
|
if (context instanceof Listener) {
|
||||||
|
listener = (Listener) context;
|
||||||
|
} else {
|
||||||
|
throw new ClassCastException("Expected context to implement Listener");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDetach() {
|
||||||
|
super.onDetach();
|
||||||
|
|
||||||
|
listener = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||||
|
return inflater.inflate(R.layout.blocked_users_fragment, container, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||||
|
View addUser = view.findViewById(R.id.add_blocked_user_touch_target);
|
||||||
|
RecyclerView recycler = view.findViewById(R.id.blocked_users_recycler);
|
||||||
|
View empty = view.findViewById(R.id.no_blocked_users);
|
||||||
|
BlockedUsersAdapter adapter = new BlockedUsersAdapter(this::handleRecipientClicked);
|
||||||
|
|
||||||
|
recycler.setAdapter(adapter);
|
||||||
|
|
||||||
|
addUser.setOnClickListener(unused -> {
|
||||||
|
if (listener != null) {
|
||||||
|
listener.handleAddUserToBlockedList();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
viewModel = ViewModelProviders.of(requireActivity()).get(BlockedUsersViewModel.class);
|
||||||
|
viewModel.getRecipients().observe(getViewLifecycleOwner(), list -> {
|
||||||
|
if (list.isEmpty()) {
|
||||||
|
empty.setVisibility(View.VISIBLE);
|
||||||
|
} else {
|
||||||
|
empty.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
adapter.submitList(list);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleRecipientClicked(@NonNull Recipient recipient) {
|
||||||
|
AlertDialog confirmationDialog = new AlertDialog.Builder(requireContext())
|
||||||
|
.setTitle(R.string.BlockedUsersActivity__unblock_user)
|
||||||
|
.setMessage(getString(R.string.BlockedUsersActivity__do_you_want_to_unblock_s, recipient.getDisplayName(requireContext())))
|
||||||
|
.setPositiveButton(R.string.BlockedUsersActivity__unblock, (dialog, which) -> {
|
||||||
|
viewModel.unblock(recipient.getId());
|
||||||
|
dialog.dismiss();
|
||||||
|
})
|
||||||
|
.setNegativeButton(android.R.string.cancel, (dialog, which) -> {
|
||||||
|
dialog.dismiss();
|
||||||
|
})
|
||||||
|
.setCancelable(true)
|
||||||
|
.create();
|
||||||
|
|
||||||
|
confirmationDialog.setOnShowListener(dialog -> {
|
||||||
|
confirmationDialog.getButton(DialogInterface.BUTTON_POSITIVE).setTextColor(Color.RED);
|
||||||
|
});
|
||||||
|
|
||||||
|
confirmationDialog.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Listener {
|
||||||
|
void handleAddUserToBlockedList();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
package org.thoughtcrime.securesms.blocked;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.core.util.Consumer;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
|
import org.thoughtcrime.securesms.database.RecipientDatabase;
|
||||||
|
import org.thoughtcrime.securesms.groups.GroupChangeBusyException;
|
||||||
|
import org.thoughtcrime.securesms.groups.GroupChangeFailedException;
|
||||||
|
import org.thoughtcrime.securesms.logging.Log;
|
||||||
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
|
import org.thoughtcrime.securesms.recipients.RecipientUtil;
|
||||||
|
import org.thoughtcrime.securesms.util.concurrent.SignalExecutors;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
class BlockedUsersRepository {
|
||||||
|
|
||||||
|
private static final String TAG = Log.tag(BlockedUsersRepository.class);
|
||||||
|
|
||||||
|
private final Context context;
|
||||||
|
|
||||||
|
BlockedUsersRepository(@NonNull Context context) {
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
void getBlocked(@NonNull Consumer<List<Recipient>> blockedUsers) {
|
||||||
|
SignalExecutors.BOUNDED.execute(() -> {
|
||||||
|
RecipientDatabase db = DatabaseFactory.getRecipientDatabase(context);
|
||||||
|
try (RecipientDatabase.RecipientReader reader = db.readerForBlocked(db.getBlocked())) {
|
||||||
|
int count = reader.getCount();
|
||||||
|
if (count == 0) {
|
||||||
|
blockedUsers.accept(Collections.emptyList());
|
||||||
|
} else {
|
||||||
|
List<Recipient> recipients = new ArrayList<>();
|
||||||
|
while (reader.getNext() != null) {
|
||||||
|
recipients.add(reader.getCurrent());
|
||||||
|
}
|
||||||
|
blockedUsers.accept(recipients);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void block(@NonNull RecipientId recipientId, @NonNull Runnable success, @NonNull Runnable failure) {
|
||||||
|
SignalExecutors.BOUNDED.execute(() -> {
|
||||||
|
try {
|
||||||
|
RecipientUtil.block(context, Recipient.resolved(recipientId));
|
||||||
|
success.run();
|
||||||
|
} catch (IOException | GroupChangeFailedException | GroupChangeBusyException e) {
|
||||||
|
Log.w(TAG, "block: failed to block recipient: ", e);
|
||||||
|
failure.run();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void createAndBlock(@NonNull String number, @NonNull Runnable success) {
|
||||||
|
SignalExecutors.BOUNDED.execute(() -> {
|
||||||
|
RecipientUtil.blockNonGroup(context, Recipient.external(context, number));
|
||||||
|
success.run();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void unblock(@NonNull RecipientId recipientId, @NonNull Runnable success) {
|
||||||
|
SignalExecutors.BOUNDED.execute(() -> {
|
||||||
|
RecipientUtil.unblock(context, Recipient.resolved(recipientId));
|
||||||
|
success.run();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,115 @@
|
||||||
|
package org.thoughtcrime.securesms.blocked;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.lifecycle.LiveData;
|
||||||
|
import androidx.lifecycle.MutableLiveData;
|
||||||
|
import androidx.lifecycle.ViewModel;
|
||||||
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
|
import org.thoughtcrime.securesms.util.SingleLiveEvent;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public class BlockedUsersViewModel extends ViewModel {
|
||||||
|
|
||||||
|
private final BlockedUsersRepository repository;
|
||||||
|
private final MutableLiveData<List<Recipient>> recipients;
|
||||||
|
private final SingleLiveEvent<Event> events = new SingleLiveEvent<>();
|
||||||
|
|
||||||
|
private BlockedUsersViewModel(@NonNull BlockedUsersRepository repository) {
|
||||||
|
this.repository = repository;
|
||||||
|
this.recipients = new MutableLiveData<>();
|
||||||
|
|
||||||
|
loadRecipients();
|
||||||
|
}
|
||||||
|
|
||||||
|
public LiveData<List<Recipient>> getRecipients() {
|
||||||
|
return recipients;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LiveData<Event> getEvents() {
|
||||||
|
return events;
|
||||||
|
}
|
||||||
|
|
||||||
|
void block(@NonNull RecipientId recipientId) {
|
||||||
|
repository.block(recipientId,
|
||||||
|
() -> {
|
||||||
|
loadRecipients();
|
||||||
|
events.postValue(new Event(EventType.BLOCK_SUCCEEDED, Recipient.resolved(recipientId)));
|
||||||
|
},
|
||||||
|
() -> events.postValue(new Event(EventType.BLOCK_FAILED, Recipient.resolved(recipientId))));
|
||||||
|
}
|
||||||
|
|
||||||
|
void createAndBlock(@NonNull String number) {
|
||||||
|
repository.createAndBlock(number, () -> {
|
||||||
|
loadRecipients();
|
||||||
|
events.postValue(new Event(EventType.BLOCK_SUCCEEDED, number));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void unblock(@NonNull RecipientId recipientId) {
|
||||||
|
repository.unblock(recipientId, () -> {
|
||||||
|
loadRecipients();
|
||||||
|
events.postValue(new Event(EventType.UNBLOCK_SUCCEEDED, Recipient.resolved(recipientId)));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadRecipients() {
|
||||||
|
repository.getBlocked(recipients::postValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
enum EventType {
|
||||||
|
BLOCK_SUCCEEDED,
|
||||||
|
BLOCK_FAILED,
|
||||||
|
UNBLOCK_SUCCEEDED
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class Event {
|
||||||
|
|
||||||
|
private final EventType eventType;
|
||||||
|
private final Recipient recipient;
|
||||||
|
private final String number;
|
||||||
|
|
||||||
|
private Event(@NonNull EventType eventType, @NonNull Recipient recipient) {
|
||||||
|
this.eventType = eventType;
|
||||||
|
this.recipient = recipient;
|
||||||
|
this.number = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Event(@NonNull EventType eventType, @NonNull String number) {
|
||||||
|
this.eventType = eventType;
|
||||||
|
this.recipient = null;
|
||||||
|
this.number = number;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable Recipient getRecipient() {
|
||||||
|
return recipient;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable String getNumber() {
|
||||||
|
return number;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NonNull EventType getEventType() {
|
||||||
|
return eventType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Factory implements ViewModelProvider.Factory {
|
||||||
|
|
||||||
|
private final BlockedUsersRepository repository;
|
||||||
|
|
||||||
|
public Factory(@NonNull BlockedUsersRepository repository) {
|
||||||
|
this.repository = repository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NonNull <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
|
||||||
|
return Objects.requireNonNull(modelClass.cast(new BlockedUsersViewModel(repository)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -60,6 +60,7 @@ public class ContactsCursorLoader extends CursorLoader {
|
||||||
public static final int FLAG_ACTIVE_GROUPS = 1 << 2;
|
public static final int FLAG_ACTIVE_GROUPS = 1 << 2;
|
||||||
public static final int FLAG_INACTIVE_GROUPS = 1 << 3;
|
public static final int FLAG_INACTIVE_GROUPS = 1 << 3;
|
||||||
public static final int FLAG_SELF = 1 << 4;
|
public static final int FLAG_SELF = 1 << 4;
|
||||||
|
public static final int FLAG_BLOCK = 1 << 5;
|
||||||
public static final int FLAG_ALL = FLAG_PUSH | FLAG_SMS | FLAG_ACTIVE_GROUPS | FLAG_INACTIVE_GROUPS | FLAG_SELF;
|
public static final int FLAG_ALL = FLAG_PUSH | FLAG_SMS | FLAG_ACTIVE_GROUPS | FLAG_INACTIVE_GROUPS | FLAG_SELF;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -342,8 +343,13 @@ public class ContactsCursorLoader extends CursorLoader {
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getUnknownContactTitle() {
|
private String getUnknownContactTitle() {
|
||||||
return getContext().getString(newConversation(mode) ? R.string.contact_selection_list__unknown_contact
|
if (blockUser(mode)) {
|
||||||
: R.string.contact_selection_list__unknown_contact_add_to_group);
|
return getContext().getString(R.string.contact_selection_list__unknown_contact_block);
|
||||||
|
} else if (newConversation(mode)) {
|
||||||
|
return getContext().getString(R.string.contact_selection_list__unknown_contact);
|
||||||
|
} else {
|
||||||
|
return getContext().getString(R.string.contact_selection_list__unknown_contact_add_to_group);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private @NonNull Cursor filterNonPushContacts(@NonNull Cursor cursor) {
|
private @NonNull Cursor filterNonPushContacts(@NonNull Cursor cursor) {
|
||||||
|
@ -382,6 +388,10 @@ public class ContactsCursorLoader extends CursorLoader {
|
||||||
return flagSet(mode, DisplayMode.FLAG_SELF);
|
return flagSet(mode, DisplayMode.FLAG_SELF);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean blockUser(int mode) {
|
||||||
|
return flagSet(mode, DisplayMode.FLAG_BLOCK);
|
||||||
|
}
|
||||||
|
|
||||||
private static boolean newConversation(int mode) {
|
private static boolean newConversation(int mode) {
|
||||||
return groupsEnabled(mode);
|
return groupsEnabled(mode);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3018,6 +3018,11 @@ public class RecipientDatabase extends Database {
|
||||||
return getCurrent();
|
return getCurrent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getCount() {
|
||||||
|
if (cursor != null) return cursor.getCount();
|
||||||
|
else return 0;
|
||||||
|
}
|
||||||
|
|
||||||
public void close() {
|
public void close() {
|
||||||
cursor.close();
|
cursor.close();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +0,0 @@
|
||||||
package org.thoughtcrime.securesms.database.loaders;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.database.Cursor;
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
|
||||||
import org.thoughtcrime.securesms.util.AbstractCursorLoader;
|
|
||||||
|
|
||||||
public class BlockedContactsLoader extends AbstractCursorLoader {
|
|
||||||
|
|
||||||
public BlockedContactsLoader(Context context) {
|
|
||||||
super(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Cursor getCursor() {
|
|
||||||
return DatabaseFactory.getRecipientDatabase(getContext()).getBlocked();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -30,9 +30,9 @@ import com.google.android.material.snackbar.Snackbar;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.ApplicationContext;
|
import org.thoughtcrime.securesms.ApplicationContext;
|
||||||
import org.thoughtcrime.securesms.ApplicationPreferencesActivity;
|
import org.thoughtcrime.securesms.ApplicationPreferencesActivity;
|
||||||
import org.thoughtcrime.securesms.BlockedContactsActivity;
|
|
||||||
import org.thoughtcrime.securesms.PassphraseChangeActivity;
|
import org.thoughtcrime.securesms.PassphraseChangeActivity;
|
||||||
import org.thoughtcrime.securesms.R;
|
import org.thoughtcrime.securesms.R;
|
||||||
|
import org.thoughtcrime.securesms.blocked.BlockedUsersActivity;
|
||||||
import org.thoughtcrime.securesms.components.SwitchPreferenceCompat;
|
import org.thoughtcrime.securesms.components.SwitchPreferenceCompat;
|
||||||
import org.thoughtcrime.securesms.contactshare.SimpleTextWatcher;
|
import org.thoughtcrime.securesms.contactshare.SimpleTextWatcher;
|
||||||
import org.thoughtcrime.securesms.crypto.MasterSecretUtil;
|
import org.thoughtcrime.securesms.crypto.MasterSecretUtil;
|
||||||
|
@ -280,7 +280,7 @@ public class AppProtectionPreferenceFragment extends CorrectedPreferenceFragment
|
||||||
private class BlockedContactsClickListener implements Preference.OnPreferenceClickListener {
|
private class BlockedContactsClickListener implements Preference.OnPreferenceClickListener {
|
||||||
@Override
|
@Override
|
||||||
public boolean onPreferenceClick(Preference preference) {
|
public boolean onPreferenceClick(Preference preference) {
|
||||||
Intent intent = new Intent(getActivity(), BlockedContactsActivity.class);
|
Intent intent = new Intent(getActivity(), BlockedUsersActivity.class);
|
||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,32 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.preferences.BlockedContactListItem
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="?android:attr/listPreferredItemHeight"
|
|
||||||
android:paddingEnd="25dip">
|
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.components.AvatarImageView
|
|
||||||
android:id="@+id/contact_photo_image"
|
|
||||||
android:layout_width="@dimen/contact_selection_photo_size"
|
|
||||||
android:layout_height="@dimen/contact_selection_photo_size"
|
|
||||||
android:layout_alignParentStart="true"
|
|
||||||
android:layout_centerVertical="true"
|
|
||||||
android:layout_marginStart="10dp"
|
|
||||||
android:cropToPadding="true"
|
|
||||||
android:scaleType="centerCrop"
|
|
||||||
android:contentDescription="@string/SingleContactSelectionActivity_contact_photo" />
|
|
||||||
|
|
||||||
<TextView android:id="@+id/name"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_alignParentTop="true"
|
|
||||||
android:layout_marginStart="14dip"
|
|
||||||
android:checkMark="?android:attr/listChoiceIndicatorMultiple"
|
|
||||||
android:singleLine="true"
|
|
||||||
android:ellipsize="marquee"
|
|
||||||
android:layout_toEndOf="@id/contact_photo_image"
|
|
||||||
android:gravity="center_vertical|start"
|
|
||||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
|
||||||
|
|
||||||
</org.thoughtcrime.securesms.preferences.BlockedContactListItem>
|
|
|
@ -1,23 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:paddingStart="16dip"
|
|
||||||
android:paddingEnd="16dip">
|
|
||||||
|
|
||||||
<ListView android:id="@id/android:list"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:drawSelectorOnTop="false"/>
|
|
||||||
|
|
||||||
<TextView android:id="@id/android:empty"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_gravity="center|center_vertical"
|
|
||||||
android:gravity="center|center_vertical"
|
|
||||||
android:textSize="20sp"
|
|
||||||
android:text="@string/blocked_contacts_fragment__no_blocked_contacts"/>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
41
app/src/main/res/layout/blocked_users_activity.xml
Normal file
41
app/src/main/res/layout/blocked_users_activity.xml
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<ViewSwitcher
|
||||||
|
android:id="@+id/toolbar_switcher"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:inAnimation="@anim/fade_in"
|
||||||
|
android:outAnimation="@anim/fade_out">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.Toolbar
|
||||||
|
android:id="@+id/toolbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="?attr/actionBarSize"
|
||||||
|
android:theme="?attr/actionBarStyle"
|
||||||
|
app:navigationIcon="@drawable/ic_arrow_left_24"
|
||||||
|
app:title="@string/BlockedUsersActivity__blocked_users" />
|
||||||
|
|
||||||
|
<org.thoughtcrime.securesms.components.ContactFilterToolbar
|
||||||
|
android:id="@+id/filter_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:navigationIcon="@drawable/ic_arrow_left_24" />
|
||||||
|
|
||||||
|
</ViewSwitcher>
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/fragment_container"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_weight="1" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
41
app/src/main/res/layout/blocked_users_adapter_item.xml
Normal file
41
app/src/main/res/layout/blocked_users_adapter_item.xml
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
<?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="match_parent"
|
||||||
|
android:layout_height="?attr/listPreferredItemHeight"
|
||||||
|
android:background="?attr/selectableItemBackground">
|
||||||
|
|
||||||
|
<org.thoughtcrime.securesms.components.AvatarImageView
|
||||||
|
android:id="@+id/avatar"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/display_name"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/number_or_username"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/avatar"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintVertical_chainStyle="packed"
|
||||||
|
tools:text="Aadam" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/number_or_username"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textAppearance="@style/TextAppearance.Signal.Body2"
|
||||||
|
android:textColor="?attr/title_text_color_secondary"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/display_name"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/display_name"
|
||||||
|
tools:text="1 234 567 8901" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
81
app/src/main/res/layout/blocked_users_fragment.xml
Normal file
81
app/src/main/res/layout/blocked_users_fragment.xml
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
<?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="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/add_blocked_user"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:paddingTop="7dp"
|
||||||
|
android:text="@string/BlockedUsersActivity__add_blocked_user"
|
||||||
|
android:textAppearance="@style/Signal.Text.Body"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/add_blocked_user_description"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:paddingBottom="7dp"
|
||||||
|
android:text="@string/BlockedUsersActivity__blocked_users_will"
|
||||||
|
android:textAppearance="@style/TextAppearance.Signal.Body2"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/add_blocked_user" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/add_blocked_user_touch_target"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:background="?selectableItemBackground"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@id/add_blocked_user_description"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="@id/add_blocked_user" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/blocked_users_header"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginTop="29dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:text="@string/BlockedUsersActivity__blocked_users"
|
||||||
|
android:textAppearance="@style/TextAppearance.Signal.Subtitle"
|
||||||
|
android:textColor="?colorAccent"
|
||||||
|
android:textStyle="bold"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/add_blocked_user_touch_target" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/no_blocked_users"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="20dp"
|
||||||
|
android:text="@string/BlockedUsersActivity__no_blocked_users"
|
||||||
|
android:textAppearance="@style/TextAppearance.Signal.Body2"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/blocked_users_header" />
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/blocked_users_recycler"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/blocked_users_header"
|
||||||
|
tools:listitem="@layout/contact_selection_list_item" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -97,8 +97,18 @@
|
||||||
<!-- AudioSlidePlayer -->
|
<!-- AudioSlidePlayer -->
|
||||||
<string name="AudioSlidePlayer_error_playing_audio">Error playing audio!</string>
|
<string name="AudioSlidePlayer_error_playing_audio">Error playing audio!</string>
|
||||||
|
|
||||||
<!-- BlockedContactsActivity -->
|
<!-- BlockedUsersActivity -->
|
||||||
<string name="BlockedContactsActivity_blocked_contacts">Blocked contacts</string>
|
<string name="BlockedUsersActivity__blocked_users">Blocked users</string>
|
||||||
|
<string name="BlockedUsersActivity__add_blocked_user">Add blocked user</string>
|
||||||
|
<string name="BlockedUsersActivity__blocked_users_will">Blocked users will not be able to call you or send you messages.</string>
|
||||||
|
<string name="BlockedUsersActivity__no_blocked_users">No blocked users</string>
|
||||||
|
<string name="BlockedUsersActivity__block_user">Block user?</string>
|
||||||
|
<string name="BlockedUserActivity__s_will_not_be_able_to">\"%1$s\" will not be able to call you or send you messages.</string>
|
||||||
|
<string name="BlockedUsersActivity__block">Block</string>
|
||||||
|
<string name="BlockedUsersActivity__unblock_user">Unblock user?</string>
|
||||||
|
<string name="BlockedUsersActivity__do_you_want_to_unblock_s">Do you want to unblock \"%1$s\"?</string>
|
||||||
|
<string name="BlockedUsersActivity__unblock">Unblock</string>
|
||||||
|
|
||||||
|
|
||||||
<!-- BlockUnblockDialog -->
|
<!-- BlockUnblockDialog -->
|
||||||
<string name="BlockUnblockDialog_block_and_leave_s">Block and leave %1$s?</string>
|
<string name="BlockUnblockDialog_block_and_leave_s">Block and leave %1$s?</string>
|
||||||
|
@ -2253,7 +2263,7 @@
|
||||||
<string name="preferences__typing_indicators">Typing indicators</string>
|
<string name="preferences__typing_indicators">Typing indicators</string>
|
||||||
<string name="preferences__if_typing_indicators_are_disabled_you_wont_be_able_to_see_typing_indicators">If typing indicators are disabled, you won\'t be able to see typing indicators from others.</string>
|
<string name="preferences__if_typing_indicators_are_disabled_you_wont_be_able_to_see_typing_indicators">If typing indicators are disabled, you won\'t be able to see typing indicators from others.</string>
|
||||||
<string name="preferences__request_keyboard_to_disable_personalized_learning">Request keyboard to disable personalized learning</string>
|
<string name="preferences__request_keyboard_to_disable_personalized_learning">Request keyboard to disable personalized learning</string>
|
||||||
<string name="preferences_app_protection__blocked_contacts">Blocked contacts</string>
|
<string name="preferences_app_protection__blocked_users">Blocked users</string>
|
||||||
<string name="preferences_chats__when_using_mobile_data">When using mobile data</string>
|
<string name="preferences_chats__when_using_mobile_data">When using mobile data</string>
|
||||||
<string name="preferences_chats__when_using_wifi">When using Wi-Fi</string>
|
<string name="preferences_chats__when_using_wifi">When using Wi-Fi</string>
|
||||||
<string name="preferences_chats__when_roaming">When roaming</string>
|
<string name="preferences_chats__when_roaming">When roaming</string>
|
||||||
|
@ -2351,6 +2361,7 @@
|
||||||
|
|
||||||
<!-- contact_selection_list -->
|
<!-- contact_selection_list -->
|
||||||
<string name="contact_selection_list__unknown_contact">New message to…</string>
|
<string name="contact_selection_list__unknown_contact">New message to…</string>
|
||||||
|
<string name="contact_selection_list__unknown_contact_block">Block user</string>
|
||||||
<string name="contact_selection_list__unknown_contact_add_to_group">Add to group</string>
|
<string name="contact_selection_list__unknown_contact_add_to_group">Add to group</string>
|
||||||
|
|
||||||
<!-- conversation_callable_insecure -->
|
<!-- conversation_callable_insecure -->
|
||||||
|
@ -2824,6 +2835,9 @@
|
||||||
|
|
||||||
<!-- StorageUtil -->
|
<!-- StorageUtil -->
|
||||||
<string name="StorageUtil__s_s">%1$s/%2$s</string>
|
<string name="StorageUtil__s_s">%1$s/%2$s</string>
|
||||||
|
<string name="BlockedUsersActivity__s_has_been_blocked">\"%1$s\" has been blocked.</string>
|
||||||
|
<string name="BlockedUsersActivity__failed_to_block_s">Failed to block \"%1$s\"</string>
|
||||||
|
<string name="BlockedUsersActivity__s_has_been_unblocked">\"%1$s\" has been unblocked.</string>
|
||||||
|
|
||||||
<!-- ReviewCardDialogFragment -->
|
<!-- ReviewCardDialogFragment -->
|
||||||
<string name="ReviewCardDialogFragment__review_members">Review Members</string>
|
<string name="ReviewCardDialogFragment__review_members">Review Members</string>
|
||||||
|
|
|
@ -95,7 +95,7 @@
|
||||||
|
|
||||||
<Preference
|
<Preference
|
||||||
android:key="preference_category_blocked"
|
android:key="preference_category_blocked"
|
||||||
android:title="@string/preferences_app_protection__blocked_contacts" />
|
android:title="@string/preferences_app_protection__blocked_users" />
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
|
|
||||||
<PreferenceCategory android:layout="@layout/preference_divider" />
|
<PreferenceCategory android:layout="@layout/preference_divider" />
|
||||||
|
|
Loading…
Add table
Reference in a new issue