Commit fdf5ded0 authored by Adrien Béraud's avatar Adrien Béraud Committed by Sébastien Blin
Browse files

swarm: various fixes and improvements

Change-Id: Ib36417ad4d68acfbb7d257c76ac739f6f02c4e01
parent b8190512
...@@ -65,6 +65,18 @@ import com.bumptech.glide.load.resource.bitmap.CenterInside; ...@@ -65,6 +65,18 @@ import com.bumptech.glide.load.resource.bitmap.CenterInside;
import com.bumptech.glide.load.resource.bitmap.RoundedCorners; import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
import com.bumptech.glide.request.target.DrawableImageViewTarget; import com.bumptech.glide.request.target.DrawableImageViewTarget;
import net.jami.conversation.ConversationPresenter;
import net.jami.model.Account;
import net.jami.model.Call;
import net.jami.model.Contact;
import net.jami.model.ContactEvent;
import net.jami.model.DataTransfer;
import net.jami.model.Interaction;
import net.jami.model.Interaction.InteractionStatus;
import net.jami.model.Interaction.InteractionType;
import net.jami.model.TextMessage;
import net.jami.utils.StringUtils;
import java.io.File; import java.io.File;
import java.text.DateFormat; import java.text.DateFormat;
import java.util.ArrayList; import java.util.ArrayList;
...@@ -75,24 +87,11 @@ import java.util.concurrent.TimeUnit; ...@@ -75,24 +87,11 @@ import java.util.concurrent.TimeUnit;
import cx.ring.R; import cx.ring.R;
import cx.ring.client.MediaViewerActivity; import cx.ring.client.MediaViewerActivity;
import net.jami.conversation.ConversationPresenter;
import cx.ring.fragments.ConversationFragment; import cx.ring.fragments.ConversationFragment;
import net.jami.model.Account;
import net.jami.model.Contact;
import net.jami.model.ContactEvent;
import net.jami.model.DataTransfer;
import net.jami.model.Interaction;
import net.jami.model.Interaction.InteractionStatus;
import net.jami.model.Interaction.InteractionType;
import net.jami.model.Call;
import net.jami.model.TextMessage;
import cx.ring.service.DRingService;
import cx.ring.utils.AndroidFileUtils;
import cx.ring.utils.ContentUriHandler; import cx.ring.utils.ContentUriHandler;
import cx.ring.utils.GlideApp; import cx.ring.utils.GlideApp;
import cx.ring.utils.GlideOptions; import cx.ring.utils.GlideOptions;
import cx.ring.utils.ResourceMapper; import cx.ring.utils.ResourceMapper;
import net.jami.utils.StringUtils;
import cx.ring.views.ConversationViewHolder; import cx.ring.views.ConversationViewHolder;
import io.reactivex.Observable; import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.android.schedulers.AndroidSchedulers;
...@@ -178,6 +177,7 @@ public class ConversationAdapter extends RecyclerView.Adapter<ConversationViewHo ...@@ -178,6 +177,7 @@ public class ConversationAdapter extends RecyclerView.Adapter<ConversationViewHo
} }
for (int i = 0, n = mInteractions.size(); i<n; i++) { for (int i = 0, n = mInteractions.size(); i<n; i++) {
if (mInteractions.get(i).getParentIds().contains(e.getMessageId())) { if (mInteractions.get(i).getParentIds().contains(e.getMessageId())) {
Log.w(TAG, "Adding message at " + i + " previous count " + n);
mInteractions.add(i, e); mInteractions.add(i, e);
notifyItemInserted(i); notifyItemInserted(i);
return i == n-1; return i == n-1;
...@@ -194,6 +194,7 @@ public class ConversationAdapter extends RecyclerView.Adapter<ConversationViewHo ...@@ -194,6 +194,7 @@ public class ConversationAdapter extends RecyclerView.Adapter<ConversationViewHo
} }
public void update(Interaction e) { public void update(Interaction e) {
Log.w(TAG, "update " + e.getMessageId());
if (!e.isIncoming() && e.getStatus() == InteractionStatus.SUCCESS) { if (!e.isIncoming() && e.getStatus() == InteractionStatus.SUCCESS) {
notifyItemChanged(lastDeliveredPosition); notifyItemChanged(lastDeliveredPosition);
} }
...@@ -207,18 +208,33 @@ public class ConversationAdapter extends RecyclerView.Adapter<ConversationViewHo ...@@ -207,18 +208,33 @@ public class ConversationAdapter extends RecyclerView.Adapter<ConversationViewHo
} }
public void remove(Interaction e) { public void remove(Interaction e) {
for (int i = mInteractions.size() - 1; i >= 0; i--) { if (e.getMessageId() != null) {
Interaction element = mInteractions.get(i); for (int i = mInteractions.size() - 1; i >= 0; i--) {
if (e.getId() == element.getId()) { if (e.getMessageId().equals(mInteractions.get(i).getMessageId())) {
mInteractions.remove(i); mInteractions.remove(i);
notifyItemRemoved(i); notifyItemRemoved(i);
if (i > 0) { if (i > 0) {
notifyItemChanged(i - 1); notifyItemChanged(i - 1);
}
if (i != mInteractions.size()) {
notifyItemChanged(i);
}
break;
} }
if (i != mInteractions.size()) { }
notifyItemChanged(i); } else {
for (int i = mInteractions.size() - 1; i >= 0; i--) {
if (e.getId() == mInteractions.get(i).getId()) {
mInteractions.remove(i);
notifyItemRemoved(i);
if (i > 0) {
notifyItemChanged(i - 1);
}
if (i != mInteractions.size()) {
notifyItemChanged(i);
}
break;
} }
break;
} }
} }
} }
......
...@@ -106,7 +106,7 @@ public class SmartListAdapter extends RecyclerView.Adapter<SmartListViewHolder> ...@@ -106,7 +106,7 @@ public class SmartListAdapter extends RecyclerView.Adapter<SmartListViewHolder>
public void update(SmartListViewModel smartListViewModel) { public void update(SmartListViewModel smartListViewModel) {
for (int i = 0; i < mSmartListViewModels.size(); i++) { for (int i = 0; i < mSmartListViewModels.size(); i++) {
SmartListViewModel old = mSmartListViewModels.get(i); SmartListViewModel old = mSmartListViewModels.get(i);
if (old.getContact() == smartListViewModel.getContact()) { if (old.getContacts() == smartListViewModel.getContacts()) {
mSmartListViewModels.set(i, smartListViewModel); mSmartListViewModels.set(i, smartListViewModel);
notifyItemChanged(i); notifyItemChanged(i);
return; return;
......
...@@ -51,11 +51,11 @@ public class SmartListDiffUtil extends DiffUtil.Callback { ...@@ -51,11 +51,11 @@ public class SmartListDiffUtil extends DiffUtil.Callback {
SmartListViewModel newItem = mNewList.get(newItemPosition); SmartListViewModel newItem = mNewList.get(newItemPosition);
if (newItem.getHeaderTitle() != oldItem.getHeaderTitle()) if (newItem.getHeaderTitle() != oldItem.getHeaderTitle())
return false; return false;
if (newItem.getContact() != oldItem.getContact()) { if (newItem.getContacts() != oldItem.getContacts()) {
if (newItem.getContact().size() != oldItem.getContact().size()) if (newItem.getContacts().size() != oldItem.getContacts().size())
return false; return false;
for (int i = 0; i < newItem.getContact().size(); i++) { for (int i = 0; i < newItem.getContacts().size(); i++) {
if (newItem.getContact().get(i) != oldItem.getContact().get(i)) if (newItem.getContacts().get(i) != oldItem.getContacts().get(i))
return false; return false;
} }
} }
......
...@@ -26,48 +26,53 @@ import android.content.Intent; ...@@ -26,48 +26,53 @@ import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.res.ColorStateList; import android.content.res.ColorStateList;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.Bundle; import android.os.Bundle;
import android.util.Log;
import com.google.android.material.appbar.CollapsingToolbarLayout; import android.view.LayoutInflater;
import com.google.android.material.dialog.MaterialAlertDialogBuilder; import android.view.View;
import com.google.android.material.floatingactionbutton.FloatingActionButton; import android.view.ViewGroup;
import com.google.android.material.snackbar.Snackbar; import android.widget.Toast;
import androidx.annotation.DrawableRes; import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.StringRes; import androidx.annotation.StringRes;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import androidx.core.view.ViewCompat;
import androidx.recyclerview.widget.RecyclerView;
import android.util.Log; import com.google.android.material.appbar.CollapsingToolbarLayout;
import android.view.LayoutInflater; import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import android.view.ViewGroup; import com.google.android.material.floatingactionbutton.FloatingActionButton;
import android.widget.Toast; import com.google.android.material.snackbar.Snackbar;
import net.jami.facades.ConversationFacade;
import net.jami.model.Call;
import net.jami.model.Conference;
import net.jami.model.Contact;
import net.jami.model.Conversation;
import net.jami.model.Uri;
import net.jami.services.AccountService;
import net.jami.services.NotificationService;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
import androidx.core.view.ViewCompat;
import androidx.recyclerview.widget.RecyclerView;
import cx.ring.R; import cx.ring.R;
import cx.ring.application.JamiApplication; import cx.ring.application.JamiApplication;
import cx.ring.views.AvatarFactory; import cx.ring.views.AvatarFactory;
import cx.ring.databinding.ActivityContactDetailsBinding; import cx.ring.databinding.ActivityContactDetailsBinding;
import cx.ring.databinding.ItemContactActionBinding; import cx.ring.databinding.ItemContactActionBinding;
import net.jami.facades.ConversationFacade; import cx.ring.databinding.ItemContactHorizontalBinding;
import cx.ring.fragments.CallFragment; import cx.ring.fragments.CallFragment;
import cx.ring.fragments.ConversationFragment; import cx.ring.fragments.ConversationFragment;
import net.jami.model.Contact; import cx.ring.services.SharedPreferencesServiceImpl;
import net.jami.model.Conference;
import net.jami.model.Conversation;
import net.jami.model.Call;
import net.jami.model.Uri;
import net.jami.services.AccountService;
import net.jami.services.NotificationService;
import cx.ring.utils.ConversationPath; import cx.ring.utils.ConversationPath;
import cx.ring.views.AvatarDrawable; import cx.ring.views.AvatarDrawable;
import io.reactivex.Single;
import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable; import io.reactivex.disposables.CompositeDisposable;
...@@ -93,6 +98,8 @@ public class ContactDetailsActivity extends AppCompatActivity { ...@@ -93,6 +98,8 @@ public class ContactDetailsActivity extends AppCompatActivity {
static class ContactAction { static class ContactAction {
@DrawableRes @DrawableRes
final int icon; final int icon;
final Single<Drawable> drawable;
final CharSequence title; final CharSequence title;
final IContactAction callback; final IContactAction callback;
...@@ -104,6 +111,7 @@ public class ContactDetailsActivity extends AppCompatActivity { ...@@ -104,6 +111,7 @@ public class ContactDetailsActivity extends AppCompatActivity {
iconTint = tint; iconTint = tint;
title = t; title = t;
callback = cb; callback = cb;
drawable = null;
} }
ContactAction(@DrawableRes int i, CharSequence t, IContactAction cb) { ContactAction(@DrawableRes int i, CharSequence t, IContactAction cb) {
...@@ -111,6 +119,14 @@ public class ContactDetailsActivity extends AppCompatActivity { ...@@ -111,6 +119,14 @@ public class ContactDetailsActivity extends AppCompatActivity {
iconTint = Color.BLACK; iconTint = Color.BLACK;
title = t; title = t;
callback = cb; callback = cb;
drawable = null;
}
ContactAction(Single<Drawable> d, CharSequence t, IContactAction cb) {
drawable = d;
icon = 0;
iconTint = Color.BLACK;
title = t;
callback = cb;
} }
void setIconTint(int tint) { void setIconTint(int tint) {
...@@ -125,10 +141,12 @@ public class ContactDetailsActivity extends AppCompatActivity { ...@@ -125,10 +141,12 @@ public class ContactDetailsActivity extends AppCompatActivity {
static class ContactActionView extends RecyclerView.ViewHolder { static class ContactActionView extends RecyclerView.ViewHolder {
final ItemContactActionBinding binding; final ItemContactActionBinding binding;
IContactAction callback; IContactAction callback;
final CompositeDisposable disposable = new CompositeDisposable();
ContactActionView(@NonNull ItemContactActionBinding b) { ContactActionView(@NonNull ItemContactActionBinding b, CompositeDisposable parentDisposable) {
super(b.getRoot()); super(b.getRoot());
binding = b; binding = b;
parentDisposable.add(disposable);
itemView.setOnClickListener(view -> { itemView.setOnClickListener(view -> {
try { try {
if (callback != null) if (callback != null)
...@@ -142,34 +160,112 @@ public class ContactDetailsActivity extends AppCompatActivity { ...@@ -142,34 +160,112 @@ public class ContactDetailsActivity extends AppCompatActivity {
private static class ContactActionAdapter extends RecyclerView.Adapter<ContactActionView> { private static class ContactActionAdapter extends RecyclerView.Adapter<ContactActionView> {
private final ArrayList<ContactAction> actions = new ArrayList<>(); private final ArrayList<ContactAction> actions = new ArrayList<>();
private final CompositeDisposable disposable;
private ContactActionAdapter(CompositeDisposable disposable) {
this.disposable = disposable;
}
@NonNull @NonNull
@Override @Override
public ContactActionView onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { public ContactActionView onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext()); LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
ItemContactActionBinding itemBinding = ItemContactActionBinding.inflate(layoutInflater, parent, false); ItemContactActionBinding itemBinding = ItemContactActionBinding.inflate(layoutInflater, parent, false);
return new ContactActionView(itemBinding); return new ContactActionView(itemBinding, disposable);
} }
@Override @Override
public void onBindViewHolder(@NonNull ContactActionView holder, int position) { public void onBindViewHolder(@NonNull ContactActionView holder, int position) {
ContactAction action = actions.get(position); ContactAction action = actions.get(position);
holder.binding.actionIcon.setBackgroundResource(action.icon); holder.disposable.clear();
holder.binding.actionIcon.setText(action.iconSymbol); if (action.drawable != null) {
if (action.iconTint != Color.BLACK) holder.disposable.add(action.drawable.subscribe(holder.binding.actionIcon::setBackground));
ViewCompat.setBackgroundTintList(holder.binding.actionIcon, ColorStateList.valueOf(action.iconTint)); } else {
holder.binding.actionIcon.setBackgroundResource(action.icon);
holder.binding.actionIcon.setText(action.iconSymbol);
if (action.iconTint != Color.BLACK)
ViewCompat.setBackgroundTintList(holder.binding.actionIcon, ColorStateList.valueOf(action.iconTint));
}
holder.binding.actionTitle.setText(action.title); holder.binding.actionTitle.setText(action.title);
holder.callback = action.callback; holder.callback = action.callback;
} }
@Override
public void onViewRecycled(@NonNull ContactActionView holder) {
holder.disposable.clear();
holder.binding.actionIcon.setBackground(null);
}
@Override @Override
public int getItemCount() { public int getItemCount() {
return actions.size(); return actions.size();
} }
} }
private final ContactActionAdapter adapter = new ContactActionAdapter(); static class ContactView extends RecyclerView.ViewHolder {
final ItemContactHorizontalBinding binding;
IContactAction callback;
final CompositeDisposable disposable = new CompositeDisposable();
ContactView(@NonNull ItemContactHorizontalBinding b, CompositeDisposable parentDisposable) {
super(b.getRoot());
binding = b;
parentDisposable.add(disposable);
itemView.setOnClickListener(view -> {
try {
if (callback != null)
callback.onAction();
} catch (Exception e) {
Log.w(TAG, "Error performing action", e);
}
});
}
}
private static class ContactViewAdapter extends RecyclerView.Adapter<ContactView> {
private final List<Contact> contacts;
private final CompositeDisposable disposable;
interface ContactCallback {
void onContactClicked(Contact contact);
}
private final ContactCallback callback;
private ContactViewAdapter(CompositeDisposable disposable, List<Contact> contacts, ContactCallback cb) {
this.disposable = disposable;
this.contacts = contacts;
this.callback = cb;
}
@NonNull
@Override
public ContactView onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
ItemContactHorizontalBinding itemBinding = ItemContactHorizontalBinding.inflate(layoutInflater, parent, false);
return new ContactView(itemBinding, disposable);
}
@Override
public void onBindViewHolder(@NonNull ContactView holder, int position) {
Contact contact = contacts.get(position);
holder.disposable.clear();
holder.disposable.add(AvatarFactory.getAvatar(holder.itemView.getContext(), contact, false).subscribe(holder.binding.photo::setImageDrawable));
holder.binding.displayName.setText(contact.isUser() ? holder.itemView.getContext().getText(R.string.conversation_info_contact_you) : contact.getDisplayName());
holder.itemView.setOnClickListener(v -> callback.onContactClicked(contact));
}
@Override
public void onViewRecycled(@NonNull ContactView holder) {
holder.disposable.clear();
holder.binding.photo.setImageDrawable(null);
}
@Override
public int getItemCount() {
return contacts.size();
}
}
private final CompositeDisposable mDisposableBag = new CompositeDisposable(); private final CompositeDisposable mDisposableBag = new CompositeDisposable();
private final ContactActionAdapter adapter = new ContactActionAdapter(mDisposableBag);
private ContactAction colorAction; private ContactAction colorAction;
private ContactAction symbolAction; private ContactAction symbolAction;
...@@ -194,7 +290,7 @@ public class ContactDetailsActivity extends AppCompatActivity { ...@@ -194,7 +290,7 @@ public class ContactDetailsActivity extends AppCompatActivity {
setSupportActionBar(findViewById(R.id.toolbar)); setSupportActionBar(findViewById(R.id.toolbar));
FloatingActionButton fab = findViewById(R.id.fab); FloatingActionButton fab = findViewById(R.id.sendMessage);
fab.setOnClickListener(view -> goToConversationActivity(mConversation.getAccountId(), mConversation.getUri())); fab.setOnClickListener(view -> goToConversationActivity(mConversation.getAccountId(), mConversation.getUri()));
colorActionPosition = 1; colorActionPosition = 1;
...@@ -205,7 +301,7 @@ public class ContactDetailsActivity extends AppCompatActivity { ...@@ -205,7 +301,7 @@ public class ContactDetailsActivity extends AppCompatActivity {
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe(conversation -> { .subscribe(conversation -> {
mConversation = conversation; mConversation = conversation;
mPreferences = getSharedPreferences(conversation.getAccountId() + "_" + conversation.getUri().getUri(), Context.MODE_PRIVATE); mPreferences = SharedPreferencesServiceImpl.getConversationPreferences(this, conversation.getAccountId(), conversation.getUri());
binding.contactImage.setImageDrawable( binding.contactImage.setImageDrawable(
new AvatarDrawable.Builder() new AvatarDrawable.Builder()
.withConversation(conversation) .withConversation(conversation)
...@@ -221,10 +317,11 @@ public class ContactDetailsActivity extends AppCompatActivity { ...@@ -221,10 +317,11 @@ public class ContactDetailsActivity extends AppCompatActivity {
@StringRes int infoString = conversation.isSwarm() @StringRes int infoString = conversation.isSwarm()
? (conversation.getMode() == Conversation.Mode.OneToOne ? (conversation.getMode() == Conversation.Mode.OneToOne
? R.string.conversation_type_private ? R.string.conversation_type_private
: R.string.conversation_type_group) : R.string.conversation_type_group)
: R.string.conversation_type_contact; : R.string.conversation_type_contact;
adapter.actions.add(new ContactAction(R.drawable.baseline_info_24, getText(infoString), () -> {})); adapter.actions.add(new ContactAction(R.drawable.baseline_info_24, getText(infoString), () -> {
}));
colorAction = new ContactAction(R.drawable.item_color_background, 0, getText(R.string.conversation_preference_color), () -> { colorAction = new ContactAction(R.drawable.item_color_background, 0, getText(R.string.conversation_preference_color), () -> {
ColorChooserBottomSheet frag = new ColorChooserBottomSheet(); ColorChooserBottomSheet frag = new ColorChooserBottomSheet();
...@@ -288,7 +385,8 @@ public class ContactDetailsActivity extends AppCompatActivity { ...@@ -288,7 +385,8 @@ public class ContactDetailsActivity extends AppCompatActivity {
.create() .create()
.show())); .show()));
} }
contactAction = new ContactAction(R.drawable.baseline_person_24, conversation.getUriTitle(), () -> { String conversationUri = conversation.isSwarm() ? conversation.getUri().toString() : conversation.getUriTitle();
contactAction = new ContactAction(conversation.isSwarm() ? R.drawable.baseline_group_24 : R.drawable.baseline_person_24, conversationUri, () -> {
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
if (clipboard != null) { if (clipboard != null) {
clipboard.setPrimaryClip(ClipData.newPlainText(getText(R.string.clip_contact_uri), path.getConversationId())); clipboard.setPrimaryClip(ClipData.newPlainText(getText(R.string.clip_contact_uri), path.getConversationId()));
...@@ -297,6 +395,18 @@ public class ContactDetailsActivity extends AppCompatActivity { ...@@ -297,6 +395,18 @@ public class ContactDetailsActivity extends AppCompatActivity {
}); });
adapter.actions.add(contactAction); adapter.actions.add(contactAction);
binding.contactActionList.setAdapter(adapter); binding.contactActionList.setAdapter(adapter);
binding.contactList.setVisibility(conversation.isSwarm() ? View.VISIBLE : View.GONE);
if (conversation.isSwarm()) {
binding.contactList.setAdapter(new ContactViewAdapter(mDisposableBag, conversation.getContacts(), contact -> {
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
if (clipboard != null) {
String toCopy = contact.getUri().getRawUriString();
clipboard.setPrimaryClip(ClipData.newPlainText(getText(R.string.clip_contact_uri), toCopy));
Snackbar.make(binding.getRoot(), getString(R.string.conversation_action_copied_peer_number_clipboard, toCopy), Snackbar.LENGTH_LONG).show();
}
}));
}
})); }));
} }
......
...@@ -24,14 +24,11 @@ import android.content.Intent; ...@@ -24,14 +24,11 @@ import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.Menu; import android.view.Menu;
import android.view.View;
import androidx.annotation.ColorInt; import androidx.annotation.ColorInt;