mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-07-13 17:23:56 +01:00
Remove cluster of orphaned code around CameraContactSelectionViewController.
This commit is contained in:
committed by
Michelle Tang
parent
faadc9854f
commit
71967b278e
@@ -1,253 +0,0 @@
|
||||
package org.thoughtcrime.securesms.mediasend;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.bumptech.glide.RequestManager;
|
||||
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.components.AvatarImageView;
|
||||
import org.thoughtcrime.securesms.components.FromTextView;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.util.adapter.SectionedRecyclerViewAdapter;
|
||||
import org.thoughtcrime.securesms.util.adapter.StableIdGenerator;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
class CameraContactAdapter extends SectionedRecyclerViewAdapter<String, CameraContactAdapter.ContactSection> {
|
||||
|
||||
private static final int TYPE_INVITE = 1337;
|
||||
private static final long ID_INVITE = Long.MAX_VALUE;
|
||||
|
||||
private static final String TAG_RECENT = "recent";
|
||||
private static final String TAG_ALL = "all";
|
||||
private static final String TAG_GROUPS = "groups";
|
||||
|
||||
private final RequestManager requestManager;
|
||||
private final Set<Recipient> selected;
|
||||
private final CameraContactListener cameraContactListener;
|
||||
|
||||
|
||||
private final List<ContactSection> sections = new ArrayList<ContactSection>(3) {{
|
||||
ContactSection recentContacts = new ContactSection(TAG_RECENT, R.string.CameraContacts_recent_contacts, Collections.emptyList(), 0);
|
||||
ContactSection allContacts = new ContactSection(TAG_ALL, R.string.CameraContacts_signal_contacts, Collections.emptyList(), recentContacts.size());
|
||||
ContactSection groups = new ContactSection(TAG_GROUPS, R.string.CameraContacts_signal_groups, Collections.emptyList(), recentContacts.size() + allContacts.size());
|
||||
|
||||
add(recentContacts);
|
||||
add(allContacts);
|
||||
add(groups);
|
||||
}};
|
||||
|
||||
CameraContactAdapter(@NonNull RequestManager requestManager, @NonNull CameraContactListener listener) {
|
||||
this.requestManager = requestManager;
|
||||
this.selected = new HashSet<>();
|
||||
this.cameraContactListener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NonNull List<ContactSection> getSections() {
|
||||
return sections;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int globalPosition) {
|
||||
if (isInvitePosition(globalPosition)) {
|
||||
return ID_INVITE;
|
||||
} else {
|
||||
return super.getItemId(globalPosition);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int globalPosition) {
|
||||
if (isInvitePosition(globalPosition)) {
|
||||
return TYPE_INVITE;
|
||||
} else {
|
||||
return super.getItemViewType(globalPosition);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int viewType) {
|
||||
if (viewType == TYPE_INVITE) {
|
||||
return new InviteViewHolder(LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.camera_contact_invite_item, viewGroup, false));
|
||||
} else {
|
||||
return super.onCreateViewHolder(viewGroup, viewType);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NonNull RecyclerView.ViewHolder createHeaderViewHolder(@NonNull ViewGroup parent) {
|
||||
return new HeaderViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.camera_contact_header_item, parent, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NonNull RecyclerView.ViewHolder createContentViewHolder(@NonNull ViewGroup parent) {
|
||||
return new ContactViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.camera_contact_contact_item, parent, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable RecyclerView.ViewHolder createEmptyViewHolder(@NonNull ViewGroup viewGroup) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int globalPosition) {
|
||||
if (isInvitePosition(globalPosition)) {
|
||||
((InviteViewHolder) holder).bind(cameraContactListener);
|
||||
} else {
|
||||
super.onBindViewHolder(holder, globalPosition);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void bindViewHolder(@NonNull RecyclerView.ViewHolder holder, @NonNull ContactSection section, int localPosition) {
|
||||
section.bind(holder, localPosition, selected, requestManager, cameraContactListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return super.getItemCount() + 1;
|
||||
}
|
||||
|
||||
public void setContacts(@NonNull CameraContacts contacts, @NonNull Collection<Recipient> selected) {
|
||||
ContactSection recentContacts = new ContactSection(TAG_RECENT, R.string.CameraContacts_recent_contacts, contacts.getRecents(), 0);
|
||||
ContactSection allContacts = new ContactSection(TAG_ALL, R.string.CameraContacts_signal_contacts, contacts.getContacts(), recentContacts.size());
|
||||
ContactSection groups = new ContactSection(TAG_GROUPS, R.string.CameraContacts_signal_groups, contacts.getGroups(), recentContacts.size() + allContacts.size());
|
||||
|
||||
sections.clear();
|
||||
sections.add(recentContacts);
|
||||
sections.add(allContacts);
|
||||
sections.add(groups);
|
||||
|
||||
this.selected.clear();
|
||||
this.selected.addAll(selected);
|
||||
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
private boolean isInvitePosition(int globalPosition) {
|
||||
return globalPosition == getItemCount() - 1;
|
||||
}
|
||||
|
||||
public static class ContactSection extends SectionedRecyclerViewAdapter.Section<String> {
|
||||
|
||||
private final String tag;
|
||||
private final int titleResId;
|
||||
private final List<Recipient> recipients;
|
||||
|
||||
public ContactSection(@NonNull String tag, @StringRes int titleResId, @NonNull List<Recipient> recipients, int offset) {
|
||||
super(offset);
|
||||
this.tag = tag;
|
||||
this.titleResId = titleResId;
|
||||
this.recipients = recipients;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasEmptyState() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getContentSize() {
|
||||
return recipients.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(@NonNull StableIdGenerator<String> idGenerator, int globalPosition) {
|
||||
int localPosition = getLocalPosition(globalPosition);
|
||||
|
||||
if (localPosition == 0) {
|
||||
return idGenerator.getId(tag);
|
||||
} else {
|
||||
return idGenerator.getId(recipients.get(localPosition - 1).getId().serialize());
|
||||
}
|
||||
}
|
||||
|
||||
void bind(@NonNull RecyclerView.ViewHolder viewHolder,
|
||||
int localPosition,
|
||||
@NonNull Set<Recipient> selected,
|
||||
@NonNull RequestManager requestManager,
|
||||
@NonNull CameraContactListener cameraContactListener)
|
||||
{
|
||||
if (localPosition == 0) {
|
||||
((HeaderViewHolder) viewHolder).bind(titleResId);
|
||||
} else {
|
||||
Recipient recipient = recipients.get(localPosition - 1);
|
||||
((ContactViewHolder) viewHolder).bind(recipient, selected.contains(recipient), requestManager, cameraContactListener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class HeaderViewHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
private final TextView title;
|
||||
|
||||
HeaderViewHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
this.title = itemView.findViewById(R.id.camera_contact_header);
|
||||
}
|
||||
|
||||
void bind(@StringRes int titleResId) {
|
||||
this.title.setText(titleResId);
|
||||
}
|
||||
}
|
||||
|
||||
private static class ContactViewHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
private final AvatarImageView avatar;
|
||||
private final FromTextView name;
|
||||
private final CheckBox checkbox;
|
||||
|
||||
ContactViewHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
|
||||
this.avatar = itemView.findViewById(R.id.camera_contact_item_avatar);
|
||||
this.name = itemView.findViewById(R.id.camera_contact_item_name);
|
||||
this.checkbox = itemView.findViewById(R.id.camera_contact_item_checkbox);
|
||||
}
|
||||
|
||||
void bind(@NonNull Recipient recipient,
|
||||
boolean selected,
|
||||
@NonNull RequestManager requestManager,
|
||||
@NonNull CameraContactListener listener)
|
||||
{
|
||||
avatar.setAvatar(requestManager, recipient, false);
|
||||
name.setText(recipient);
|
||||
itemView.setOnClickListener(v -> listener.onContactClicked(recipient));
|
||||
checkbox.setChecked(selected);
|
||||
}
|
||||
}
|
||||
|
||||
private static class InviteViewHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
private final View inviteButton;
|
||||
|
||||
public InviteViewHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
inviteButton = itemView.findViewById(R.id.camera_contact_invite);
|
||||
}
|
||||
|
||||
void bind(@NonNull CameraContactListener listener) {
|
||||
inviteButton.setOnClickListener(v -> listener.onInviteContactsClicked());
|
||||
}
|
||||
}
|
||||
|
||||
interface CameraContactListener {
|
||||
void onContactClicked(@NonNull Recipient recipient);
|
||||
void onInviteContactsClicked();
|
||||
}
|
||||
}
|
||||
-66
@@ -1,66 +0,0 @@
|
||||
package org.thoughtcrime.securesms.mediasend;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.components.FromTextView;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.util.adapter.StableIdGenerator;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
class CameraContactSelectionAdapter extends RecyclerView.Adapter<CameraContactSelectionAdapter.RecipientViewHolder> {
|
||||
|
||||
private final List<Recipient> recipients = new ArrayList<>();
|
||||
private final StableIdGenerator<String> idGenerator = new StableIdGenerator<>();
|
||||
|
||||
CameraContactSelectionAdapter() {
|
||||
setHasStableIds(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return idGenerator.getId(recipients.get(position).getId().serialize());
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull RecipientViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
return new RecipientViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.camera_contact_selection_item, parent, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull RecipientViewHolder holder, int position) {
|
||||
holder.bind(recipients.get(position), position == recipients.size() - 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return recipients.size();
|
||||
}
|
||||
|
||||
void setRecipients(@NonNull List<Recipient> recipients) {
|
||||
this.recipients.clear();
|
||||
this.recipients.addAll(recipients);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
static class RecipientViewHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
private final FromTextView name;
|
||||
|
||||
RecipientViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
name = (FromTextView) itemView;
|
||||
}
|
||||
|
||||
void bind(@NonNull Recipient recipient, boolean isLast) {
|
||||
name.setText(recipient, isLast ? null : ",");
|
||||
}
|
||||
}
|
||||
}
|
||||
-192
@@ -1,192 +0,0 @@
|
||||
package org.thoughtcrime.securesms.mediasend;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.widget.SearchView;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.constraintlayout.widget.Group;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
|
||||
import org.signal.core.ui.logging.LoggingFragment;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.components.settings.app.AppSettingsActivity;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.util.DynamicTheme;
|
||||
import org.signal.core.ui.util.ThemeUtil;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Fragment that selects Signal contacts. Intended to be used in the camera-first capture flow.
|
||||
*/
|
||||
public class CameraContactSelectionFragment extends LoggingFragment implements CameraContactAdapter.CameraContactListener {
|
||||
|
||||
private Controller controller;
|
||||
private CameraContactSelectionViewModel contactViewModel;
|
||||
private RecyclerView contactList;
|
||||
private CameraContactAdapter contactAdapter;
|
||||
private RecyclerView selectionList;
|
||||
private CameraContactSelectionAdapter selectionAdapter;
|
||||
private Toolbar toolbar;
|
||||
private View sendButton;
|
||||
private Group selectionFooterGroup;
|
||||
private ViewGroup cameraContactsEmpty;
|
||||
private View inviteButton;
|
||||
|
||||
public static Fragment newInstance() {
|
||||
return new CameraContactSelectionFragment();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setHasOptionsMenu(true);
|
||||
|
||||
CameraContactSelectionViewModel.Factory factory = new CameraContactSelectionViewModel.Factory(new CameraContactsRepository(requireContext()));
|
||||
|
||||
this.contactViewModel = new ViewModelProvider(requireActivity(), factory).get(CameraContactSelectionViewModel.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(@NonNull Context context) {
|
||||
super.onAttach(context);
|
||||
if (!(getActivity() instanceof Controller)) {
|
||||
throw new IllegalStateException("Parent activity must implement controller interface.");
|
||||
}
|
||||
controller = (Controller) getActivity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
int theme = DynamicTheme.isDarkTheme(inflater.getContext()) ? R.style.TextSecure_DarkTheme
|
||||
: R.style.TextSecure_LightTheme;
|
||||
return ThemeUtil.getThemedInflater(inflater.getContext(), inflater, theme)
|
||||
.inflate(R.layout.camera_contact_selection_fragment, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
this.contactList = view.findViewById(R.id.camera_contacts_list);
|
||||
this.selectionList = view.findViewById(R.id.camera_contacts_selected_list);
|
||||
this.toolbar = view.findViewById(R.id.camera_contacts_toolbar);
|
||||
this.sendButton = view.findViewById(R.id.camera_contacts_send_button);
|
||||
this.selectionFooterGroup = view.findViewById(R.id.camera_contacts_footer_group);
|
||||
this.cameraContactsEmpty = view.findViewById(R.id.camera_contacts_empty);
|
||||
this.inviteButton = view.findViewById(R.id.camera_contacts_invite_button);
|
||||
this.contactAdapter = new CameraContactAdapter(Glide.with(this), this);
|
||||
this.selectionAdapter = new CameraContactSelectionAdapter();
|
||||
|
||||
contactList.setLayoutManager(new LinearLayoutManager(requireContext()));
|
||||
contactList.setAdapter(contactAdapter);
|
||||
|
||||
selectionList.setLayoutManager(new LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false));
|
||||
selectionList.setAdapter(selectionAdapter);
|
||||
|
||||
((AppCompatActivity) requireActivity()).setSupportActionBar(toolbar);
|
||||
((AppCompatActivity) requireActivity()).getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
toolbar.setNavigationOnClickListener(v -> requireActivity().onBackPressed());
|
||||
|
||||
inviteButton.setOnClickListener(v -> onInviteContactsClicked());
|
||||
|
||||
initViewModel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPrepareOptionsMenu(@NonNull Menu menu) {
|
||||
requireActivity().getMenuInflater().inflate(R.menu.camera_contacts, menu);
|
||||
|
||||
MenuItem searchViewItem = menu.findItem(R.id.menu_search);
|
||||
SearchView searchView = (SearchView) searchViewItem.getActionView();
|
||||
SearchView.OnQueryTextListener queryListener = new SearchView.OnQueryTextListener() {
|
||||
@Override
|
||||
public boolean onQueryTextSubmit(String query) {
|
||||
contactViewModel.onQueryUpdated(query);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onQueryTextChange(String query) {
|
||||
contactViewModel.onQueryUpdated(query);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
searchViewItem.setOnActionExpandListener(new MenuItem.OnActionExpandListener() {
|
||||
@Override
|
||||
public boolean onMenuItemActionExpand(MenuItem item) {
|
||||
searchView.setOnQueryTextListener(queryListener);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMenuItemActionCollapse(MenuItem item) {
|
||||
searchView.setOnQueryTextListener(null);
|
||||
contactViewModel.onSearchClosed();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onContactClicked(@NonNull Recipient recipient) {
|
||||
contactViewModel.onContactClicked(recipient);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInviteContactsClicked() {
|
||||
startActivity(AppSettingsActivity.invite(requireContext()));
|
||||
}
|
||||
|
||||
private void initViewModel() {
|
||||
contactViewModel.getContacts().observe(getViewLifecycleOwner(), contactState -> {
|
||||
if (contactState == null) return;
|
||||
|
||||
if (contactState.getContacts().isEmpty() && TextUtils.isEmpty(contactState.getQuery())) {
|
||||
cameraContactsEmpty.setVisibility(View.VISIBLE);
|
||||
contactList.setVisibility(View.GONE);
|
||||
selectionFooterGroup.setVisibility(View.GONE);
|
||||
} else {
|
||||
cameraContactsEmpty.setVisibility(View.GONE);
|
||||
contactList.setVisibility(View.VISIBLE);
|
||||
|
||||
sendButton.setOnClickListener(v -> controller.onCameraContactsSendClicked(contactState.getSelected()));
|
||||
|
||||
contactAdapter.setContacts(contactState.getContacts(), contactState.getSelected());
|
||||
selectionAdapter.setRecipients(contactState.getSelected());
|
||||
|
||||
selectionFooterGroup.setVisibility(contactState.getSelected().isEmpty() ? View.GONE : View.VISIBLE);
|
||||
}
|
||||
});
|
||||
|
||||
contactViewModel.getError().observe(getViewLifecycleOwner(), error -> {
|
||||
if (error == null) return;
|
||||
|
||||
if (error == CameraContactSelectionViewModel.Error.MAX_SELECTION) {
|
||||
String message = getResources().getQuantityString(R.plurals.CameraContacts_you_can_share_with_a_maximum_of_n_conversations, CameraContactSelectionViewModel.MAX_SELECTION_COUNT, CameraContactSelectionViewModel.MAX_SELECTION_COUNT);
|
||||
Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public interface Controller {
|
||||
void onCameraContactsSendClicked(@NonNull List<Recipient> recipients);
|
||||
}
|
||||
}
|
||||
-132
@@ -1,132 +0,0 @@
|
||||
package org.thoughtcrime.securesms.mediasend;
|
||||
|
||||
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.signal.core.util.ThreadUtil;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.util.SingleLiveEvent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
class CameraContactSelectionViewModel extends ViewModel {
|
||||
|
||||
static final int MAX_SELECTION_COUNT = 16;
|
||||
|
||||
private final CameraContactsRepository repository;
|
||||
private final MutableLiveData<ContactState> contacts;
|
||||
private final SingleLiveEvent<Error> error;
|
||||
private final Set<Recipient> selected;
|
||||
|
||||
private String currentQuery;
|
||||
|
||||
private CameraContactSelectionViewModel(@NonNull CameraContactsRepository repository) {
|
||||
this.repository = repository;
|
||||
this.contacts = new MutableLiveData<>();
|
||||
this.error = new SingleLiveEvent<>();
|
||||
this.selected = new LinkedHashSet<>();
|
||||
|
||||
repository.getCameraContacts(cameraContacts -> {
|
||||
ThreadUtil.runOnMain(() -> {
|
||||
contacts.postValue(new ContactState(cameraContacts, new ArrayList<>(selected), currentQuery));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
LiveData<ContactState> getContacts() {
|
||||
return contacts;
|
||||
}
|
||||
|
||||
LiveData<Error> getError() {
|
||||
return error;
|
||||
}
|
||||
|
||||
void onSearchClosed() {
|
||||
onQueryUpdated("");
|
||||
}
|
||||
|
||||
void onQueryUpdated(String query) {
|
||||
this.currentQuery = query;
|
||||
|
||||
repository.getCameraContacts(query, cameraContacts -> {
|
||||
ThreadUtil.runOnMain(() -> {
|
||||
contacts.postValue(new ContactState(cameraContacts, new ArrayList<>(selected), query));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void onRefresh() {
|
||||
repository.getCameraContacts(cameraContacts -> {
|
||||
ThreadUtil.runOnMain(() -> {
|
||||
contacts.postValue(new ContactState(cameraContacts, new ArrayList<>(selected), currentQuery));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void onContactClicked(@NonNull Recipient recipient) {
|
||||
if (selected.contains(recipient)) {
|
||||
selected.remove(recipient);
|
||||
} else if (selected.size() < MAX_SELECTION_COUNT) {
|
||||
selected.add(recipient);
|
||||
} else {
|
||||
error.postValue(Error.MAX_SELECTION);
|
||||
}
|
||||
|
||||
ContactState currentState = contacts.getValue();
|
||||
|
||||
if (currentState != null) {
|
||||
contacts.setValue(new ContactState(currentState.getContacts(), new ArrayList<>(selected), currentQuery));
|
||||
}
|
||||
}
|
||||
|
||||
static class ContactState {
|
||||
private final CameraContacts contacts;
|
||||
private final List<Recipient> selected;
|
||||
private final String query;
|
||||
|
||||
ContactState(@NonNull CameraContacts contacts, @NonNull List<Recipient> selected, @Nullable String query) {
|
||||
this.contacts = contacts;
|
||||
this.selected = selected;
|
||||
this.query = query;
|
||||
}
|
||||
|
||||
public CameraContacts getContacts() {
|
||||
return contacts;
|
||||
}
|
||||
|
||||
public List<Recipient> getSelected() {
|
||||
return selected;
|
||||
}
|
||||
|
||||
public @Nullable String getQuery() {
|
||||
return query;
|
||||
}
|
||||
}
|
||||
|
||||
enum Error {
|
||||
MAX_SELECTION
|
||||
}
|
||||
|
||||
static class Factory extends ViewModelProvider.NewInstanceFactory {
|
||||
|
||||
private final CameraContactsRepository repository;
|
||||
|
||||
Factory(CameraContactsRepository repository) {
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
|
||||
//noinspection ConstantConditions
|
||||
return modelClass.cast(new CameraContactSelectionViewModel(repository));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
package org.thoughtcrime.securesms.mediasend;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Represents the list of results to display in the {@link CameraContactSelectionFragment}.
|
||||
*/
|
||||
public class CameraContacts {
|
||||
|
||||
private final List<Recipient> recents;
|
||||
private final List<Recipient> contacts;
|
||||
private final List<Recipient> groups;
|
||||
|
||||
public CameraContacts(@NonNull List<Recipient> recents, @NonNull List<Recipient> contacts, @NonNull List<Recipient> groups) {
|
||||
this.recents = recents;
|
||||
this.contacts = contacts;
|
||||
this.groups = groups;
|
||||
}
|
||||
|
||||
public @NonNull List<Recipient> getRecents() {
|
||||
return recents;
|
||||
}
|
||||
|
||||
public @NonNull List<Recipient> getContacts() {
|
||||
return contacts;
|
||||
}
|
||||
|
||||
public @NonNull List<Recipient> getGroups() {
|
||||
return groups;
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return recents.isEmpty() && contacts.isEmpty() && groups.isEmpty();
|
||||
}
|
||||
}
|
||||
@@ -1,138 +0,0 @@
|
||||
package org.thoughtcrime.securesms.mediasend;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.WorkerThread;
|
||||
|
||||
import org.signal.core.util.concurrent.SignalExecutors;
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.contacts.ContactRepository;
|
||||
import org.thoughtcrime.securesms.database.GroupTable;
|
||||
import org.thoughtcrime.securesms.database.RecipientTable;
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase;
|
||||
import org.thoughtcrime.securesms.database.ThreadTable;
|
||||
import org.thoughtcrime.securesms.database.model.GroupRecord;
|
||||
import org.thoughtcrime.securesms.database.model.ThreadRecord;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
/**
|
||||
* Handles retrieving the data to be shown in {@link CameraContactSelectionFragment}.
|
||||
*/
|
||||
class CameraContactsRepository {
|
||||
|
||||
private static final String TAG = Log.tag(CameraContactsRepository.class);
|
||||
|
||||
private static final int RECENT_MAX = 25;
|
||||
|
||||
private final Context context;
|
||||
private final ThreadTable threadTable;
|
||||
private final GroupTable groupDatabase;
|
||||
private final RecipientTable recipientTable;
|
||||
private final ContactRepository contactRepository;
|
||||
private final Executor serialExecutor;
|
||||
private final ExecutorService parallelExecutor;
|
||||
|
||||
CameraContactsRepository(@NonNull Context context) {
|
||||
this.context = context.getApplicationContext();
|
||||
this.threadTable = SignalDatabase.threads();
|
||||
this.groupDatabase = SignalDatabase.groups();
|
||||
this.recipientTable = SignalDatabase.recipients();
|
||||
this.contactRepository = new ContactRepository(context.getString(R.string.note_to_self));
|
||||
this.serialExecutor = SignalExecutors.SERIAL;
|
||||
this.parallelExecutor = SignalExecutors.BOUNDED;
|
||||
}
|
||||
|
||||
void getCameraContacts(@NonNull Callback<CameraContacts> callback) {
|
||||
getCameraContacts("", callback);
|
||||
}
|
||||
|
||||
void getCameraContacts(@NonNull String query, @NonNull Callback<CameraContacts> callback) {
|
||||
serialExecutor.execute(() -> {
|
||||
Future<List<Recipient>> recents = parallelExecutor.submit(() -> getRecents(query));
|
||||
Future<List<Recipient>> contacts = parallelExecutor.submit(() -> getContacts(query));
|
||||
Future<List<Recipient>> groups = parallelExecutor.submit(() -> getGroups(query));
|
||||
|
||||
try {
|
||||
long startTime = System.currentTimeMillis();
|
||||
CameraContacts result = new CameraContacts(recents.get(), contacts.get(), groups.get());
|
||||
|
||||
Log.d(TAG, "Total time: " + (System.currentTimeMillis() - startTime) + " ms");
|
||||
|
||||
callback.onComplete(result);
|
||||
} catch (InterruptedException | ExecutionException e) {
|
||||
Log.w(TAG, "Failed to perform queries.", e);
|
||||
callback.onComplete(new CameraContacts(Collections.emptyList(), Collections.emptyList(), Collections.emptyList()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@WorkerThread
|
||||
private @NonNull List<Recipient> getRecents(@NonNull String query) {
|
||||
if (!TextUtils.isEmpty(query)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<Recipient> recipients = new ArrayList<>(RECENT_MAX);
|
||||
|
||||
try (ThreadTable.Reader threadReader = threadTable.readerFor(threadTable.getRecentPushConversationList(RECENT_MAX))) {
|
||||
ThreadRecord threadRecord;
|
||||
while ((threadRecord = threadReader.getNext()) != null) {
|
||||
recipients.add(threadRecord.getRecipient().resolve());
|
||||
}
|
||||
}
|
||||
|
||||
return recipients;
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
private @NonNull List<Recipient> getContacts(@NonNull String query) {
|
||||
List<Recipient> recipients = new ArrayList<>();
|
||||
|
||||
try (Cursor cursor = contactRepository.querySignalContacts(query)) {
|
||||
while (cursor.moveToNext()) {
|
||||
RecipientId id = RecipientId.from(cursor.getLong(cursor.getColumnIndexOrThrow(ContactRepository.ID_COLUMN)));
|
||||
Recipient recipient = Recipient.resolved(id);
|
||||
recipients.add(recipient);
|
||||
}
|
||||
}
|
||||
|
||||
return recipients;
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
private @NonNull List<Recipient> getGroups(@NonNull String query) {
|
||||
if (TextUtils.isEmpty(query)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<Recipient> recipients = new ArrayList<>();
|
||||
|
||||
try (GroupTable.Reader reader = groupDatabase.queryGroupsByTitle(query, false, true, true)) {
|
||||
GroupRecord groupRecord;
|
||||
while ((groupRecord = reader.getNext()) != null) {
|
||||
RecipientId recipientId = recipientTable.getOrInsertFromGroupId(groupRecord.getId());
|
||||
recipients.add(Recipient.resolved(recipientId));
|
||||
}
|
||||
}
|
||||
|
||||
return recipients;
|
||||
}
|
||||
|
||||
interface Callback<E> {
|
||||
void onComplete(E result);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user