Started using friends capabilities for group chat and LIME

This commit is contained in:
Sylvain Berfini 2018-12-13 11:24:25 +01:00
parent 134123358b
commit 4363a0659d
11 changed files with 233 additions and 78 deletions

View file

@ -27,6 +27,7 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.CompoundButton;
import android.widget.HorizontalScrollView;
import android.widget.ImageView;
import android.widget.LinearLayout;
@ -52,6 +53,7 @@ import org.linphone.core.Address;
import org.linphone.core.ChatRoom;
import org.linphone.core.ChatRoomListenerStub;
import org.linphone.core.Core;
import org.linphone.core.FriendCapability;
import org.linphone.core.ProxyConfig;
import org.linphone.fragments.FragmentsAvailable;
import org.linphone.mediastream.Log;
@ -134,7 +136,11 @@ public class ChatRoomCreationFragment extends Fragment
mSearchAdapter =
new SearchContactsAdapter(
null, mContactsFetchInProgress, this, !mCreateGroupChatRoom);
null,
mContactsFetchInProgress,
this,
!mCreateGroupChatRoom,
mChatRoomEncrypted);
mSearchField = view.findViewById(R.id.searchField);
mSearchField.setOnQueryTextListener(
@ -155,24 +161,32 @@ public class ChatRoomCreationFragment extends Fragment
mAllContactsToggle = view.findViewById(R.id.layout_all_contacts);
mSecurityToggle = view.findViewById(R.id.security_toogle);
mSecurityToggle.setOnCheckedChangeListener(
new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
setSecurityEnabled(isChecked);
}
});
mSecurityToggleOn = view.findViewById(R.id.security_toogle_on);
mSecurityToggleOff = view.findViewById(R.id.security_toogle_off);
mSecurityToggleOn.setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View v) {
mSecurityToggle.setChecked(true);
setSecurityEnabled(true);
}
});
mSecurityToggleOff.setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View v) {
mSecurityToggle.setChecked(false);
setSecurityEnabled(false);
}
});
mSecurityToggle.setChecked(mChatRoomEncrypted);
mSearchAdapter.setSecurityEnabled(mChatRoomEncrypted);
ProxyConfig lpc = LinphoneManager.getLc().getDefaultProxyConfig();
if ((mChatRoomSubject != null && mChatRoomAddress != null)
|| (lpc == null || lpc.getConferenceFactoryUri() == null)) {
@ -236,7 +250,7 @@ public class ChatRoomCreationFragment extends Fragment
mWaitLayout.setVisibility(View.GONE);
LinphoneActivity.instance().displayChatRoomError();
Log.e(
"Group chat room for address "
"[Chat Room Creation] Group chat room for address "
+ cr.getPeerAddress()
+ " has failed !");
}
@ -286,6 +300,30 @@ public class ChatRoomCreationFragment extends Fragment
super.onPause();
}
private void setSecurityEnabled(boolean enabled) {
mChatRoomEncrypted = enabled;
mSecurityToggle.setChecked(mChatRoomEncrypted);
mSearchAdapter.setSecurityEnabled(mChatRoomEncrypted);
if (enabled) {
// Remove all contacts added before LIME switch was set
// and that can stay because they don't have the capability
for (ContactAddress ca : mContactsSelected) {
mContactsSelectedLayout.removeAllViews();
if (ca.isSelect() && !ca.hasCapability(FriendCapability.LimeX3Dh)) {
mContactsSelected.remove(getIndexOfCa(ca, mContactsSelected));
}
for (ContactAddress contactAddress : mContactsSelected) {
if (contactAddress.getView() != null) {
mContactsSelectedLayout.addView(contactAddress.getView());
}
}
mSearchAdapter.setContactsSelectedList(mContactsSelected);
mContactsSelectedLayout.invalidate();
}
}
}
private void displayChatCreation() {
mNextButton.setVisibility(View.VISIBLE);
mNextButton.setEnabled(mContactsSelected.size() > 0);
@ -507,6 +545,21 @@ public class ChatRoomCreationFragment extends Fragment
Core lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull();
ProxyConfig lpc = lc.getDefaultProxyConfig();
boolean createEncryptedChatRoom = mSecurityToggle.isChecked();
if (createEncryptedChatRoom && !ca.hasCapability(FriendCapability.LimeX3Dh)) {
Log.w(
"[Chat Room Creation] Contact "
+ ca.getContact()
+ " doesn't have LIME X3DH capability !");
return;
} else if (mCreateGroupChatRoom && !ca.hasCapability(FriendCapability.GroupChat)) {
Log.w(
"[Chat Room Creation] Contact "
+ ca.getContact()
+ " doesn't have group chat capability !");
return;
}
if (lpc == null || lpc.getConferenceFactoryUri() == null || !mCreateGroupChatRoom) {
if (createEncryptedChatRoom && lpc != null && lpc.getConferenceFactoryUri() != null) {
mChatRoom =

View file

@ -245,6 +245,8 @@ class AsyncContactsLoader extends AsyncTask<Void, Void, AsyncContactsLoader.Asyn
contact.createOrUpdateFriendFromNativeContact();
}
ContactsManager.getInstance().clearGroupChatContacts();
ContactsManager.getInstance().clearLimeX3dhContacts();
// Now that contact fetching is asynchronous, this is required to ensure
// presence subscription event will be sent with all friends
for (FriendList list : LinphoneManager.getLc().getFriendsLists()) {

View file

@ -23,6 +23,7 @@ import android.view.View;
import java.io.Serializable;
import org.linphone.core.Address;
import org.linphone.core.Factory;
import org.linphone.core.FriendCapability;
public class ContactAddress implements Serializable {
private LinphoneContact mContact;
@ -118,6 +119,10 @@ public class ContactAddress implements Serializable {
return mIsLinphoneContact;
}
public boolean hasCapability(FriendCapability capability) {
return mContact.hasFriendCapability(capability);
}
private void init(LinphoneContact c, String a, String pn, boolean isLC) {
mContact = c;
mAddress = a;

View file

@ -42,6 +42,7 @@ import org.linphone.core.ChatRoom;
import org.linphone.core.ChatRoomListenerStub;
import org.linphone.core.Core;
import org.linphone.core.Factory;
import org.linphone.core.FriendCapability;
import org.linphone.core.PresenceBasicStatus;
import org.linphone.core.PresenceModel;
import org.linphone.core.ProxyConfig;
@ -297,9 +298,9 @@ public class ContactDetailsFragment extends Fragment implements OnClickListener
v.findViewById(R.id.contact_chat_secured).setTag(value);
}
if (v.findViewById(R.id.friendLinphone).getVisibility()
== View.VISIBLE /* TODO Does mContact have LIME capability ?*/
&& lpc.getConferenceFactoryUri() != null) {
if (v.findViewById(R.id.friendLinphone).getVisibility() == View.VISIBLE
&& lpc.getConferenceFactoryUri() != null
&& mContact.hasFriendCapability(FriendCapability.LimeX3Dh)) {
v.findViewById(R.id.contact_chat_secured).setVisibility(View.VISIBLE);
} else {
v.findViewById(R.id.contact_chat_secured).setVisibility(View.GONE);

View file

@ -49,6 +49,7 @@ import org.linphone.R;
import org.linphone.core.Address;
import org.linphone.core.Core;
import org.linphone.core.Friend;
import org.linphone.core.FriendCapability;
import org.linphone.core.FriendList;
import org.linphone.core.FriendListListener;
import org.linphone.core.MagicSearch;
@ -59,7 +60,7 @@ import org.linphone.settings.LinphonePreferences;
public class ContactsManager extends ContentObserver implements FriendListListener {
private static ContactsManager sInstance;
private List<LinphoneContact> mContacts, mSipContacts;
private List<LinphoneContact> mContacts, mSipContacts, mGroupChatContacts, mLimeX3dhContacts;
private ArrayList<ContactsUpdatedListener> mContactsUpdatedListeners;
private MagicSearch mMagicSearch;
private final Bitmap mDefaultAvatar;
@ -80,6 +81,9 @@ public class ContactsManager extends ContentObserver implements FriendListListen
mContactsUpdatedListeners = new ArrayList<>();
mContacts = new ArrayList<>();
mSipContacts = new ArrayList<>();
mGroupChatContacts = new ArrayList<>();
mLimeX3dhContacts = new ArrayList<>();
if (LinphoneManager.getLcIfManagerNotDestroyedOrNull() != null) {
mMagicSearch = LinphoneManager.getLcIfManagerNotDestroyedOrNull().createMagicSearch();
}
@ -127,6 +131,22 @@ public class ContactsManager extends ContentObserver implements FriendListListen
mSipContacts = c;
}
public synchronized List<LinphoneContact> getGroupChatContacts() {
return mGroupChatContacts;
}
synchronized void clearGroupChatContacts() {
mGroupChatContacts.clear();
}
public synchronized List<LinphoneContact> getLimeX3dhContacts() {
return mLimeX3dhContacts;
}
synchronized void clearLimeX3dhContacts() {
mLimeX3dhContacts.clear();
}
public void destroy() {
Core lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull();
if (lc != null) {
@ -379,10 +399,27 @@ public class ContactsManager extends ContentObserver implements FriendListListen
private synchronized boolean refreshSipContact(Friend lf) {
LinphoneContact contact = (LinphoneContact) lf.getUserData();
if (contact != null && !mSipContacts.contains(contact)) {
mSipContacts.add(contact);
Collections.sort(mSipContacts);
return true;
if (contact != null) {
if (lf.hasCapability(FriendCapability.GroupChat)
&& !mGroupChatContacts.contains(contact)) {
mGroupChatContacts.add(contact);
Collections.sort(mGroupChatContacts);
Log.i("[Contacts Manager] Contact " + contact + " has group chat capability");
// Contact may only have LimeX3DH capability if it already has GroupChat capability
if (lf.hasCapability(FriendCapability.LimeX3Dh)
&& !mLimeX3dhContacts.contains(contact)) {
mLimeX3dhContacts.add(contact);
Collections.sort(mLimeX3dhContacts);
Log.i("[Contacts Manager] Contact " + contact + " has lime x3dh capability");
}
}
if (!mSipContacts.contains(contact)) {
mSipContacts.add(contact);
Collections.sort(mSipContacts);
return true;
}
}
return false;
}

View file

@ -28,6 +28,7 @@ import org.linphone.LinphoneManager;
import org.linphone.core.Address;
import org.linphone.core.Core;
import org.linphone.core.Friend;
import org.linphone.core.FriendCapability;
import org.linphone.core.FriendList;
import org.linphone.core.PresenceBasicStatus;
import org.linphone.core.PresenceModel;
@ -487,4 +488,10 @@ public class LinphoneContact extends AndroidContact
deleteFriend();
}
}
public boolean hasFriendCapability(FriendCapability capability) {
if (!isFriend()) return false;
return getFriend().hasCapability(capability);
}
}

View file

@ -33,16 +33,20 @@ public class SearchContactViewHolder extends RecyclerView.ViewHolder
public final ImageView linphoneContact;
public final ImageView isSelect;
public final RelativeLayout avatarLayout;
public final View disabled;
private final ClickListener mListener;
public SearchContactViewHolder(View view, ClickListener listener) {
super(view);
name = view.findViewById(R.id.contact_name);
address = view.findViewById(R.id.contact_address);
linphoneContact = view.findViewById(R.id.contact_linphone);
isSelect = view.findViewById(R.id.contact_is_select);
avatarLayout = view.findViewById(R.id.avatar_layout);
disabled = view.findViewById(R.id.disabled);
mListener = listener;
view.setOnClickListener(this);
}

View file

@ -32,6 +32,7 @@ import org.linphone.LinphoneManager;
import org.linphone.R;
import org.linphone.core.Address;
import org.linphone.core.Factory;
import org.linphone.core.FriendCapability;
import org.linphone.core.PresenceBasicStatus;
import org.linphone.core.PresenceModel;
import org.linphone.core.ProxyConfig;
@ -47,20 +48,23 @@ public class SearchContactsAdapter extends RecyclerView.Adapter<SearchContactVie
private final ProgressBar mProgressBar;
private boolean mOnlySipContact = false;
private SearchContactViewHolder.ClickListener mListener;
private final boolean mHideSelectionMark;
private final boolean mIsOnlyOnePersonSelection;
private String mPreviousSearch;
private boolean mSecurityEnabled;
public SearchContactsAdapter(
List<ContactAddress> contactsList,
ProgressBar pB,
SearchContactViewHolder.ClickListener clickListener,
boolean hideSelectionMark) {
mHideSelectionMark = hideSelectionMark;
boolean hideSelectionMark,
boolean isSecurityEnabled) {
mIsOnlyOnePersonSelection = hideSelectionMark;
mListener = clickListener;
mProgressBar = pB;
setContactsSelectedList(null);
setContactsList(contactsList);
mPreviousSearch = null;
mSecurityEnabled = isSecurityEnabled;
}
public List<ContactAddress> getContacts() {
@ -71,6 +75,11 @@ public class SearchContactsAdapter extends RecyclerView.Adapter<SearchContactVie
mOnlySipContact = enable;
}
public void setSecurityEnabled(boolean enable) {
mSecurityEnabled = enable;
notifyDataSetChanged();
}
@NonNull
@Override
public SearchContactViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
@ -113,12 +122,19 @@ public class SearchContactsAdapter extends RecyclerView.Adapter<SearchContactVie
holder.name.setVisibility(View.GONE);
}
holder.disabled.setVisibility(View.GONE);
if (c != null) {
if (c.getFullName() == null && c.getFirstName() == null && c.getLastName() == null) {
c.setFullName(holder.name.getText().toString());
}
ContactAvatar.displayAvatar(c, holder.avatarLayout);
// TODO get if contact has security capabilities
ContactAvatar.displayAvatar(
c, c.hasFriendCapability(FriendCapability.LimeX3Dh), holder.avatarLayout);
if ((!mIsOnlyOnePersonSelection && !c.hasFriendCapability(FriendCapability.GroupChat))
|| (mSecurityEnabled && !c.hasFriendCapability(FriendCapability.LimeX3Dh))) {
// Disable row, contact doesn't have the required capabilities
holder.disabled.setVisibility(View.VISIBLE);
}
} else {
ContactAvatar.displayAvatar(holder.name.getText().toString(), holder.avatarLayout);
}
@ -137,7 +153,7 @@ public class SearchContactsAdapter extends RecyclerView.Adapter<SearchContactVie
} else {
holder.isSelect.setVisibility(View.INVISIBLE);
}
if (mHideSelectionMark) {
if (mIsOnlyOnePersonSelection) {
holder.isSelect.setVisibility(View.GONE);
}
}

View file

@ -85,6 +85,16 @@ public class ContactAvatar {
}
}
private static void showHasLimeX3dhCapability(View v) {
ContactAvatarHolder holder = new ContactAvatarHolder(v);
if (holder.securityLevel != null) {
holder.securityLevel.setVisibility(View.VISIBLE);
holder.securityLevel.setImageResource(R.drawable.security_toogle_icon_green);
} else {
holder.securityLevel.setVisibility(View.GONE);
}
}
public static void setAvatarMask(View v, int resourceId) {
ContactAvatarHolder holder = new ContactAvatarHolder(v);
holder.avatarMask.setImageResource(resourceId);
@ -151,6 +161,14 @@ public class ContactAvatar {
holder.securityLevel.setVisibility(View.GONE);
}
public static void displayAvatar(
LinphoneContact contact, boolean hasLimeX3dhCapability, View v) {
displayAvatar(contact, v);
if (hasLimeX3dhCapability) {
showHasLimeX3dhCapability(v);
}
}
public static void displayAvatar(
LinphoneContact contact, ChatRoomSecurityLevel securityLevel, View v) {
displayAvatar(contact, v);

View file

@ -1,80 +1,91 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="60dp"
android:background="@drawable/list_selector"
android:gravity="center_vertical"
android:orientation="vertical"
android:padding="5dp">
android:layout_height="60dp">
<RelativeLayout
android:id="@+id/avatar"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center">
<include layout="@layout/contact_avatar" />
</RelativeLayout>
<LinearLayout
android:id="@+id/layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_alignParentBottom="true"
android:layout_marginLeft="5dp"
android:layout_marginRight="70dp"
android:layout_toRightOf="@id/avatar"
android:layout_height="match_parent"
android:background="@drawable/list_selector"
android:gravity="center_vertical"
android:orientation="vertical">
android:orientation="vertical"
android:padding="5dp">
<TextView
android:id="@+id/contact_name"
style="@style/font6"
<RelativeLayout
android:id="@+id/avatar"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center">
<include layout="@layout/contact_avatar" />
</RelativeLayout>
<LinearLayout
android:id="@+id/layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="top|left"
android:lines="1" />
android:layout_alignParentTop="true"
android:layout_alignParentBottom="true"
android:layout_marginLeft="5dp"
android:layout_marginRight="70dp"
android:layout_toRightOf="@id/avatar"
android:background="@drawable/list_selector"
android:gravity="center_vertical"
android:orientation="vertical">
<TextView
android:id="@+id/contact_address"
style="@style/font2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:gravity="bottom|left"
android:lines="1" />
<TextView
android:id="@+id/contact_name"
style="@style/font6"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="top|left"
android:lines="1" />
</LinearLayout>
<TextView
android:id="@+id/contact_address"
style="@style/font2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:gravity="bottom|left"
android:lines="1" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_toLeftOf="@+id/contact_is_select">
</LinearLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_toLeftOf="@+id/contact_is_select">
<ImageView
android:id="@+id/contact_linphone"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_gravity="right"
android:paddingRight="10dp"
android:src="@drawable/linphone_user" />
</RelativeLayout>
<ImageView
android:id="@+id/contact_linphone"
android:layout_width="30dp"
android:layout_height="30dp"
android:id="@+id/contact_is_select"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_centerInParent="true"
android:layout_gravity="right"
android:paddingRight="10dp"
android:src="@drawable/linphone_user" />
android:paddingRight="20dp"
android:src="@drawable/check_selected"
android:visibility="invisible" />
</RelativeLayout>
<ImageView
android:id="@+id/contact_is_select"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_alignParentRight="true"
android:layout_centerInParent="true"
android:layout_gravity="right"
android:paddingRight="20dp"
android:src="@drawable/check_selected"
android:visibility="invisible" />
</RelativeLayout>
<View
android:id="@+id/disabled"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/contact_disabled"/>
</FrameLayout>

View file

@ -23,4 +23,5 @@
<color name="transparent">#00000000</color>
<color name="linphone_launcher_icon_background">@color/colorA</color>
<color name="contact_disabled">#99ffffff</color>
</resources>