Replaced signal ContactSelection things with loki ones.

Fixed sharing in session (we can only share to one user at a time).
This commit is contained in:
Mikunj 2020-04-15 12:52:23 +10:00
parent 7b8fbcea4e
commit 361dab24d6
17 changed files with 385 additions and 709 deletions

View File

@ -19,6 +19,6 @@
<fragment android:id="@+id/contact_selection_list_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="org.thoughtcrime.securesms.ContactSelectionListFragment" />
android:name="org.thoughtcrime.securesms.loki.redesign.fragments.ContactSelectionListFragment" />
</LinearLayout>

View File

@ -1,17 +1,37 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical">
<LinearLayout
android:id="@+id/emptyStateContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerInParent="true"
android:gravity="center_horizontal|center_vertical"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="You don't have any contacts yet"
android:textColor="@color/text"
android:textSize="@dimen/medium_font_size" />
</LinearLayout>
<LinearLayout
android:id="@+id/mainContentContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/swipe_refresh"
android:id="@+id/swipeRefresh"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
@ -28,65 +48,11 @@
</android.support.v4.widget.SwipeRefreshLayout>
<org.thoughtcrime.securesms.components.RecyclerViewFastScroller
android:id="@+id/fast_scroller"
android:id="@+id/fastScroller"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:visibility="gone"
android:layout_gravity="end"/>
<LinearLayout android:id="@+id/show_contacts_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_gravity="center"
android:visibility="gone"
tools:visibility="visible">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.pnikosis.materialishprogress.ProgressWheel
android:id="@+id/progress"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="invisible"
app:matProg_circleRadius="145dp"
app:matProg_barWidth="6dp"
app:matProg_rimColor="@color/signal_primary"
app:matProg_barColor="@color/signal_primary_dark"
app:matProg_progressIndeterminate="true"
tools:visibility="visible"
/>
<ImageView android:layout_gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/no_contacts"/>
</FrameLayout>
<TextView android:id="@+id/show_contacts_description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:layout_marginStart="50dp"
android:layout_marginEnd="50dp"
android:textSize="15sp"
android:lineSpacingMultiplier="1.3"
android:gravity="center"
android:text="@string/contact_selection_list_fragment__signal_needs_access_to_your_contacts_in_order_to_display_them"/>
<Button android:id="@+id/show_contacts_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:layout_gravity="center_horizontal"
android:background="@color/signal_primary"
android:textColor="@color/white"
android:padding="10dp"
android:text="@string/contact_selection_list_fragment__show_contacts"/>
</LinearLayout>
</FrameLayout>

View File

@ -94,7 +94,7 @@
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:name="org.thoughtcrime.securesms.ContactSelectionListFragment"
android:name="org.thoughtcrime.securesms.loki.redesign.fragments.ContactSelectionListFragment"
tools:layout="@layout/contact_selection_list_fragment"/>
<LinearLayout android:layout_width="match_parent"

View File

@ -49,7 +49,7 @@
android:layout_below="@id/toolbar"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="org.thoughtcrime.securesms.ContactSelectionListFragment" />
android:name="org.thoughtcrime.securesms.loki.redesign.fragments.ContactSelectionListFragment" />
<org.thoughtcrime.securesms.components.SearchToolbar
android:id="@+id/search_toolbar"

View File

@ -1663,12 +1663,14 @@
<string name="session_restore_banner_dismiss_button_title">Dismiss</string>
<string name="session_restore_banner_restore_button_title">Restore</string>
<!-- Loki -->
<!-- Session -->
<string name="activity_register_public_key_copied_message">Copied to clipboard</string>
<string name="activity_home_leave_group_dialog_message">Are you sure you want to leave this group?</string>
<string name="activity_home_delete_conversation_dialog_message">Are you sure you want to delete this conversation?</string>
<string name="activity_home_conversation_deleted_message">Conversation deleted</string>
<!-- ContactSelectionListLoader -->
<string name="ContactSelectionListLoader_contacts">Contacts</string>
<string name="ContactSelectionListLoader_closed_groups">Groups</string>
<string name="ContactSelectionListLoader_open_groups">Public Chats</string>
</resources>

View File

@ -20,11 +20,11 @@ import android.os.Bundle;
import android.support.v4.widget.SwipeRefreshLayout;
import org.thoughtcrime.securesms.components.ContactFilterToolbar;
import org.thoughtcrime.securesms.contacts.ContactsCursorLoader.DisplayMode;
import org.thoughtcrime.securesms.loki.redesign.activities.ContactSelectionListLoader.DisplayMode;
import org.thoughtcrime.securesms.loki.redesign.fragments.ContactSelectionListFragment;
import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme;
import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.ViewUtil;
import network.loki.messenger.R;
@ -57,9 +57,7 @@ public abstract class ContactSelectionActivity extends PassphraseRequiredActionB
@Override
protected void onCreate(Bundle icicle, boolean ready) {
if (!getIntent().hasExtra(ContactSelectionListFragment.DISPLAY_MODE)) {
int displayMode = TextSecurePreferences.isSmsEnabled(this) ? DisplayMode.FLAG_ALL
: DisplayMode.FLAG_PUSH | DisplayMode.FLAG_GROUPS;
getIntent().putExtra(ContactSelectionListFragment.DISPLAY_MODE, displayMode);
getIntent().putExtra(ContactSelectionListFragment.DISPLAY_MODE, DisplayMode.FLAG_ALL);
}
setContentView(R.layout.contact_selection_activity);

View File

@ -1,303 +0,0 @@
/*
* Copyright (C) 2015 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.thoughtcrime.securesms;
import android.annotation.SuppressLint;
import android.database.Cursor;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.Fragment;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import com.pnikosis.materialishprogress.ProgressWheel;
import org.thoughtcrime.securesms.components.RecyclerViewFastScroller;
import org.thoughtcrime.securesms.contacts.ContactSelectionListAdapter;
import org.thoughtcrime.securesms.contacts.ContactSelectionListItem;
import org.thoughtcrime.securesms.contacts.ContactsCursorLoader;
import org.thoughtcrime.securesms.contacts.ContactsCursorLoader.DisplayMode;
import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter;
import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.util.StickyHeaderDecoration;
import org.thoughtcrime.securesms.util.ViewUtil;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import network.loki.messenger.R;
/**
* Fragment for selecting a one or more contacts from a list.
*
* @author Moxie Marlinspike
*
*/
public class ContactSelectionListFragment extends Fragment
implements LoaderManager.LoaderCallbacks<Cursor>
{
@SuppressWarnings("unused")
private static final String TAG = ContactSelectionListFragment.class.getSimpleName();
public static final String DISPLAY_MODE = "display_mode";
public static final String MULTI_SELECT = "multi_select";
public static final String REFRESHABLE = "refreshable";
public static final String RECENTS = "recents";
private TextView emptyText;
private Set<String> selectedContacts;
private OnContactSelectedListener onContactSelectedListener;
private SwipeRefreshLayout swipeRefresh;
private View showContactsLayout;
private Button showContactsButton;
private TextView showContactsDescription;
private ProgressWheel showContactsProgress;
private String cursorFilter;
private RecyclerView recyclerView;
private RecyclerViewFastScroller fastScroller;
@Override
public void onActivityCreated(Bundle icicle) {
super.onActivityCreated(icicle);
initializeCursor();
}
@Override
public void onStart() {
super.onStart();
handleContactPermissionGranted();
// Permissions.with(this)
// .request(Manifest.permission.WRITE_CONTACTS, Manifest.permission.READ_CONTACTS)
// .ifNecessary()
// .onAllGranted(() -> {
// if (!TextSecurePreferences.hasSuccessfullyRetrievedDirectory(getActivity())) {
// handleContactPermissionGranted();
// } else {
// this.getLoaderManager().initLoader(0, null, this);
// }
// })
// .onAnyDenied(() -> {
// getActivity().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
//
// if (getActivity().getIntent().getBooleanExtra(RECENTS, false)) {
// getLoaderManager().initLoader(0, null, ContactSelectionListFragment.this);
// } else {
// initializeNoContactsPermission();
// }
// })
// .execute();
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.contact_selection_list_fragment, container, false);
emptyText = ViewUtil.findById(view, android.R.id.empty);
recyclerView = ViewUtil.findById(view, R.id.recycler_view);
swipeRefresh = ViewUtil.findById(view, R.id.swipe_refresh);
fastScroller = ViewUtil.findById(view, R.id.fast_scroller);
showContactsLayout = view.findViewById(R.id.show_contacts_container);
showContactsButton = view.findViewById(R.id.show_contacts_button);
showContactsDescription = view.findViewById(R.id.show_contacts_description);
showContactsProgress = view.findViewById(R.id.progress);
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
swipeRefresh.setEnabled(getActivity().getIntent().getBooleanExtra(REFRESHABLE, true));
return view;
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
Permissions.onRequestPermissionsResult(this, requestCode, permissions, grantResults);
}
public @NonNull List<String> getSelectedContacts() {
List<String> selected = new LinkedList<>();
if (selectedContacts != null) {
selected.addAll(selectedContacts);
}
return selected;
}
private boolean isMulti() {
return getActivity().getIntent().getBooleanExtra(MULTI_SELECT, false);
}
private void initializeCursor() {
ContactSelectionListAdapter adapter = new ContactSelectionListAdapter(getActivity(),
GlideApp.with(this),
null,
new ListClickListener(),
isMulti());
selectedContacts = adapter.getSelectedContacts();
recyclerView.setAdapter(adapter);
recyclerView.addItemDecoration(new StickyHeaderDecoration(adapter, true, true));
}
private void initializeNoContactsPermission() {
swipeRefresh.setVisibility(View.GONE);
showContactsLayout.setVisibility(View.VISIBLE);
showContactsProgress.setVisibility(View.INVISIBLE);
showContactsDescription.setText(R.string.contact_selection_list_fragment__signal_needs_access_to_your_contacts_in_order_to_display_them);
showContactsButton.setVisibility(View.VISIBLE);
/*
showContactsButton.setOnClickListener(v -> {
Permissions.with(this)
.request(Manifest.permission.WRITE_CONTACTS, Manifest.permission.READ_CONTACTS)
.ifNecessary()
.withPermanentDenialDialog(getString(R.string.ContactSelectionListFragment_signal_requires_the_contacts_permission_in_order_to_display_your_contacts))
.onSomeGranted(permissions -> {
if (permissions.contains(Manifest.permission.WRITE_CONTACTS)) {
handleContactPermissionGranted();
}
})
.execute();
});
*/
}
public void setQueryFilter(String filter) {
this.cursorFilter = filter;
this.getLoaderManager().restartLoader(0, null, this);
}
public void resetQueryFilter() {
setQueryFilter(null);
swipeRefresh.setRefreshing(false);
}
public void setRefreshing(boolean refreshing) {
swipeRefresh.setRefreshing(refreshing);
}
public void reset() {
selectedContacts.clear();
if (!isDetached() && !isRemoving() && getActivity() != null && !getActivity().isFinishing()) {
getLoaderManager().restartLoader(0, null, this);
}
}
@Override
public @NonNull Loader<Cursor> onCreateLoader(int id, Bundle args) {
return new ContactsCursorLoader(getActivity(),
getActivity().getIntent().getIntExtra(DISPLAY_MODE, DisplayMode.FLAG_ALL),
cursorFilter, getActivity().getIntent().getBooleanExtra(RECENTS, false));
}
@Override
public void onLoadFinished(@NonNull Loader<Cursor> loader, Cursor data) {
swipeRefresh.setVisibility(View.VISIBLE);
showContactsLayout.setVisibility(View.GONE);
((CursorRecyclerViewAdapter) recyclerView.getAdapter()).changeCursor(data);
emptyText.setText(R.string.contact_selection_group_activity__no_contacts);
boolean useFastScroller = (recyclerView.getAdapter().getItemCount() > 20);
recyclerView.setVerticalScrollBarEnabled(!useFastScroller);
if (useFastScroller) {
fastScroller.setVisibility(View.VISIBLE);
fastScroller.setRecyclerView(recyclerView);
}
}
@Override
public void onLoaderReset(@NonNull Loader<Cursor> loader) {
((CursorRecyclerViewAdapter) recyclerView.getAdapter()).changeCursor(null);
fastScroller.setVisibility(View.GONE);
}
@SuppressLint("StaticFieldLeak")
private void handleContactPermissionGranted() {
new AsyncTask<Void, Void, Boolean>() {
@Override
protected void onPreExecute() {
swipeRefresh.setVisibility(View.GONE);
showContactsLayout.setVisibility(View.VISIBLE);
showContactsButton.setVisibility(View.INVISIBLE);
showContactsDescription.setText(R.string.ConversationListFragment_loading);
showContactsProgress.setVisibility(View.VISIBLE);
showContactsProgress.spin();
}
@Override
protected Boolean doInBackground(Void... voids) {
return false;
}
@Override
protected void onPostExecute(Boolean result) {
if (result) {
showContactsLayout.setVisibility(View.GONE);
swipeRefresh.setVisibility(View.VISIBLE);
reset();
} else {
Toast.makeText(getContext(), R.string.ContactSelectionListFragment_error_retrieving_contacts_check_your_network_connection, Toast.LENGTH_LONG).show();
initializeNoContactsPermission();
}
}
}.execute();
}
private class ListClickListener implements ContactSelectionListAdapter.ItemClickListener {
@Override
public void onItemClick(ContactSelectionListItem contact) {
if (!isMulti() || !selectedContacts.contains(contact.getNumber())) {
selectedContacts.add(contact.getNumber());
contact.setChecked(true);
if (onContactSelectedListener != null) onContactSelectedListener.onContactSelected(contact.getNumber());
} else {
selectedContacts.remove(contact.getNumber());
contact.setChecked(false);
if (onContactSelectedListener != null) onContactSelectedListener.onContactDeselected(contact.getNumber());
}
}
}
public void setOnContactSelectedListener(OnContactSelectedListener onContactSelectedListener) {
this.onContactSelectedListener = onContactSelectedListener;
}
public void setOnRefreshListener(SwipeRefreshLayout.OnRefreshListener onRefreshListener) {
this.swipeRefresh.setOnRefreshListener(onRefreshListener);
}
public interface OnContactSelectedListener {
void onContactSelected(String number);
void onContactDeselected(String number);
}
}

View File

@ -43,7 +43,6 @@ import com.bumptech.glide.request.transition.Transition;
import org.thoughtcrime.securesms.avatar.AvatarSelection;
import org.thoughtcrime.securesms.components.PushRecipientsPanel;
import org.thoughtcrime.securesms.components.PushRecipientsPanel.RecipientsPanelChangedListener;
import org.thoughtcrime.securesms.contacts.ContactsCursorLoader.DisplayMode;
import org.thoughtcrime.securesms.contacts.RecipientsEditor;
import org.thoughtcrime.securesms.contacts.avatars.ContactColors;
import org.thoughtcrime.securesms.contacts.avatars.ResourceContactPhoto;
@ -57,6 +56,8 @@ import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.groups.GroupManager;
import org.thoughtcrime.securesms.groups.GroupManager.GroupActionResult;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.loki.redesign.activities.ContactSelectionListLoader.DisplayMode;
import org.thoughtcrime.securesms.loki.redesign.fragments.ContactSelectionListFragment;
import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.BitmapUtil;
@ -321,11 +322,7 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
@Override
public void onClick(View v) {
Intent intent = new Intent(GroupCreateActivity.this, PushContactSelectionActivity.class);
if (groupToUpdate.isPresent()) {
intent.putExtra(ContactSelectionListFragment.DISPLAY_MODE, DisplayMode.FLAG_PUSH);
} else {
intent.putExtra(ContactSelectionListFragment.DISPLAY_MODE, DisplayMode.FLAG_PUSH | DisplayMode.FLAG_SMS);
}
intent.putExtra(ContactSelectionListFragment.DISPLAY_MODE, DisplayMode.FLAG_FRIENDS);
startActivityForResult(intent, PICK_CONTACT);
}
}

View File

@ -26,9 +26,10 @@ import android.widget.Toast;
import org.thoughtcrime.securesms.components.ContactFilterToolbar;
import org.thoughtcrime.securesms.components.ContactFilterToolbar.OnFilterChangedListener;
import org.thoughtcrime.securesms.contacts.ContactsCursorLoader.DisplayMode;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.loki.redesign.activities.ContactSelectionListLoader.DisplayMode;
import org.thoughtcrime.securesms.loki.redesign.fragments.ContactSelectionListFragment;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.sms.MessageSender;
import org.thoughtcrime.securesms.sms.OutgoingTextMessage;
@ -40,7 +41,7 @@ import java.util.concurrent.ExecutionException;
import network.loki.messenger.R;
public class InviteActivity extends PassphraseRequiredActionBarActivity implements ContactSelectionListFragment.OnContactSelectedListener {
public class InviteActivity extends PassphraseRequiredActionBarActivity {
private ContactSelectionListFragment contactsFragment;
private EditText inviteText;
@ -52,7 +53,7 @@ public class InviteActivity extends PassphraseRequiredActionBarActivity implemen
@Override
protected void onCreate(Bundle savedInstanceState, boolean ready) {
getIntent().putExtra(ContactSelectionListFragment.DISPLAY_MODE, DisplayMode.FLAG_SMS);
getIntent().putExtra(ContactSelectionListFragment.DISPLAY_MODE, DisplayMode.FLAG_FRIENDS);
getIntent().putExtra(ContactSelectionListFragment.MULTI_SELECT, true);
getIntent().putExtra(ContactSelectionListFragment.REFRESHABLE, false);
@ -84,7 +85,6 @@ public class InviteActivity extends PassphraseRequiredActionBarActivity implemen
if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
heart.getViewTreeObserver().addOnPreDrawListener(new HeartPreDrawListener());
}
contactsFragment.setOnContactSelectedListener(this);
shareButton.setOnClickListener(new ShareClickListener());
smsButton.setOnClickListener(new SmsClickListener());
smsCancelButton.setOnClickListener(new SmsCancelClickListener());
@ -99,12 +99,10 @@ public class InviteActivity extends PassphraseRequiredActionBarActivity implemen
return animation;
}
@Override
public void onContactSelected(String number) {
updateSmsButtonText();
}
@Override
public void onContactDeselected(String number) {
updateSmsButtonText();
}
@ -132,7 +130,6 @@ public class InviteActivity extends PassphraseRequiredActionBarActivity implemen
}
private void cancelSmsSelection() {
contactsFragment.reset();
updateSmsButtonText();
ViewUtil.animateOut(smsSendFrame, slideOutAnimation, View.GONE);
}
@ -241,7 +238,6 @@ public class InviteActivity extends PassphraseRequiredActionBarActivity implemen
ViewUtil.animateOut(smsSendFrame, slideOutAnimation, View.GONE).addListener(new Listener<Boolean>() {
@Override
public void onSuccess(Boolean result) {
contactsFragment.reset();
}
@Override

View File

@ -19,6 +19,8 @@ package org.thoughtcrime.securesms;
import android.content.Intent;
import android.os.Bundle;
import org.thoughtcrime.securesms.loki.redesign.fragments.ContactSelectionListFragment;
import java.util.ArrayList;
import java.util.List;

View File

@ -29,19 +29,19 @@ import android.os.Process;
import android.provider.OpenableColumns;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.Toolbar;
import android.view.MenuItem;
import android.view.View;
import android.widget.ImageView;
import org.thoughtcrime.securesms.components.SearchToolbar;
import org.thoughtcrime.securesms.contacts.ContactsCursorLoader.DisplayMode;
import org.thoughtcrime.securesms.conversation.ConversationActivity;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.loki.redesign.activities.ContactSelectionListLoader.DisplayMode;
import org.thoughtcrime.securesms.loki.redesign.fragments.ContactSelectionListFragment;
import org.thoughtcrime.securesms.mediasend.Media;
import org.thoughtcrime.securesms.mms.PartAuthority;
import org.thoughtcrime.securesms.providers.BlobProvider;
@ -52,7 +52,6 @@ import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme;
import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.FileUtils;
import org.thoughtcrime.securesms.util.MediaUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.ViewUtil;
import java.io.FileInputStream;
@ -68,7 +67,7 @@ import network.loki.messenger.R;
* @author Jake McGinty
*/
public class ShareActivity extends PassphraseRequiredActionBarActivity
implements ContactSelectionListFragment.OnContactSelectedListener, SwipeRefreshLayout.OnRefreshListener
implements ContactSelectionListFragment.OnContactSelectedListener
{
private static final String TAG = ShareActivity.class.getSimpleName();
@ -96,14 +95,10 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity
@Override
protected void onCreate(Bundle icicle, boolean ready) {
if (!getIntent().hasExtra(ContactSelectionListFragment.DISPLAY_MODE)) {
getIntent().putExtra(ContactSelectionListFragment.DISPLAY_MODE,
TextSecurePreferences.isSmsEnabled(this)
? DisplayMode.FLAG_ALL
: DisplayMode.FLAG_PUSH | DisplayMode.FLAG_GROUPS);
getIntent().putExtra(ContactSelectionListFragment.DISPLAY_MODE, DisplayMode.FLAG_ALL);
}
getIntent().putExtra(ContactSelectionListFragment.REFRESHABLE, false);
getIntent().putExtra(ContactSelectionListFragment.RECENTS, true);
setContentView(R.layout.share_activity);
@ -170,7 +165,6 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity
searchAction = findViewById(R.id.search_action);
contactsFragment = (ContactSelectionListFragment) getSupportFragmentManager().findFragmentById(R.id.contact_selection_list_fragment);
contactsFragment.setOnContactSelectedListener(this);
contactsFragment.setOnRefreshListener(this);
}
private void initializeSearch() {
@ -281,12 +275,6 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity
@Override
public void onContactDeselected(String number) {
}
@Override
public void onRefresh() {
}
@SuppressLint("StaticFieldLeak")

View File

@ -1,271 +0,0 @@
/**
* Copyright (C) 2014 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.thoughtcrime.securesms.contacts;
import android.content.Context;
import android.content.res.TypedArray;
import android.database.Cursor;
import android.provider.ContactsContract;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.widget.RecyclerView;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.style.ForegroundColorSpan;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import network.loki.messenger.R;
import org.thoughtcrime.securesms.components.RecyclerViewFastScroller.FastScrollAdapter;
import org.thoughtcrime.securesms.contacts.ContactSelectionListAdapter.HeaderViewHolder;
import org.thoughtcrime.securesms.contacts.ContactSelectionListAdapter.ViewHolder;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.util.StickyHeaderDecoration.StickyHeaderAdapter;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* List adapter to display all contacts and their related information
*
* @author Jake McGinty
*/
public class ContactSelectionListAdapter extends CursorRecyclerViewAdapter<ViewHolder>
implements FastScrollAdapter,
StickyHeaderAdapter<HeaderViewHolder>
{
private final static String TAG = ContactSelectionListAdapter.class.getSimpleName();
private static final int VIEW_TYPE_CONTACT = 0;
private static final int VIEW_TYPE_DIVIDER = 1;
private final static int STYLE_ATTRIBUTES[] = new int[]{R.attr.contact_selection_push_user,
R.attr.contact_selection_lay_user};
private final boolean multiSelect;
private final LayoutInflater li;
private final TypedArray drawables;
private final ItemClickListener clickListener;
private final GlideRequests glideRequests;
private final Set<String> selectedContacts = new HashSet<>();
public abstract static class ViewHolder extends RecyclerView.ViewHolder {
public ViewHolder(View itemView) {
super(itemView);
}
public abstract void bind(@NonNull GlideRequests glideRequests, int type, String name, String number, String label, int color, boolean multiSelect);
public abstract void unbind(@NonNull GlideRequests glideRequests);
public abstract void setChecked(boolean checked);
}
public static class ContactViewHolder extends ViewHolder {
ContactViewHolder(@NonNull final View itemView,
@Nullable final ItemClickListener clickListener)
{
super(itemView);
itemView.setOnClickListener(v -> {
if (clickListener != null) clickListener.onItemClick(getView());
});
}
public ContactSelectionListItem getView() {
return (ContactSelectionListItem) itemView;
}
public void bind(@NonNull GlideRequests glideRequests, int type, String name, String number, String label, int color, boolean multiSelect) {
getView().set(glideRequests, type, name, number, label, color, multiSelect);
}
@Override
public void unbind(@NonNull GlideRequests glideRequests) {
getView().unbind(glideRequests);
}
@Override
public void setChecked(boolean checked) {
getView().setChecked(checked);
}
}
public static class DividerViewHolder extends ViewHolder {
private final TextView label;
DividerViewHolder(View itemView) {
super(itemView);
this.label = itemView.findViewById(R.id.label);
}
@Override
public void bind(@NonNull GlideRequests glideRequests, int type, String name, String number, String label, int color, boolean multiSelect) {
this.label.setText(name);
}
@Override
public void unbind(@NonNull GlideRequests glideRequests) {}
@Override
public void setChecked(boolean checked) {}
}
static class HeaderViewHolder extends RecyclerView.ViewHolder {
HeaderViewHolder(View itemView) {
super(itemView);
}
}
public ContactSelectionListAdapter(@NonNull Context context,
@NonNull GlideRequests glideRequests,
@Nullable Cursor cursor,
@Nullable ItemClickListener clickListener,
boolean multiSelect)
{
super(context, cursor);
this.li = LayoutInflater.from(context);
this.glideRequests = glideRequests;
this.drawables = context.obtainStyledAttributes(STYLE_ATTRIBUTES);
this.multiSelect = multiSelect;
this.clickListener = clickListener;
}
@Override
public long getHeaderId(int i) {
if (!isActiveCursor()) return -1;
int contactType = getContactType(i);
if (contactType == ContactsDatabase.DIVIDER_TYPE) return -1;
return Util.hashCode(getHeaderString(i), getContactType(i));
}
@Override
public ViewHolder onCreateItemViewHolder(ViewGroup parent, int viewType) {
if (viewType == VIEW_TYPE_CONTACT) {
return new ContactViewHolder(li.inflate(R.layout.contact_selection_list_item, parent, false), clickListener);
} else {
return new DividerViewHolder(li.inflate(R.layout.contact_selection_list_divider, parent, false));
}
}
@Override
public void onBindItemViewHolder(ViewHolder viewHolder, @NonNull Cursor cursor) {
int contactType = cursor.getInt(cursor.getColumnIndexOrThrow(ContactsDatabase.CONTACT_TYPE_COLUMN));
String name = cursor.getString(cursor.getColumnIndexOrThrow(ContactsDatabase.NAME_COLUMN));
String number = cursor.getString(cursor.getColumnIndexOrThrow(ContactsDatabase.NUMBER_COLUMN));
int numberType = cursor.getInt(cursor.getColumnIndexOrThrow(ContactsDatabase.NUMBER_TYPE_COLUMN));
String label = cursor.getString(cursor.getColumnIndexOrThrow(ContactsDatabase.LABEL_COLUMN));
String labelText = ContactsContract.CommonDataKinds.Phone.getTypeLabel(getContext().getResources(),
numberType, label).toString();
int color = (contactType == ContactsDatabase.PUSH_TYPE) ? drawables.getColor(0, 0xa0000000) :
drawables.getColor(1, 0xff000000);
viewHolder.unbind(glideRequests);
viewHolder.bind(glideRequests, contactType, name, number, labelText, color, multiSelect);
viewHolder.setChecked(selectedContacts.contains(number));
}
@Override
public int getItemViewType(@NonNull Cursor cursor) {
if (cursor.getInt(cursor.getColumnIndexOrThrow(ContactsDatabase.CONTACT_TYPE_COLUMN)) == ContactsDatabase.DIVIDER_TYPE) {
return VIEW_TYPE_DIVIDER;
} else {
return VIEW_TYPE_CONTACT;
}
}
@Override
public HeaderViewHolder onCreateHeaderViewHolder(ViewGroup parent) {
return new HeaderViewHolder(LayoutInflater.from(getContext()).inflate(R.layout.contact_selection_recyclerview_header, parent, false));
}
@Override
public void onBindHeaderViewHolder(HeaderViewHolder viewHolder, int position) {
((TextView)viewHolder.itemView).setText(getSpannedHeaderString(position));
}
@Override
public void onItemViewRecycled(ViewHolder holder) {
holder.unbind(glideRequests);
}
@Override
public CharSequence getBubbleText(int position) {
return getHeaderString(position);
}
public Set<String> getSelectedContacts() {
return selectedContacts;
}
private CharSequence getSpannedHeaderString(int position) {
final String headerString = getHeaderString(position);
if (isPush(position)) {
SpannableString spannable = new SpannableString(headerString);
spannable.setSpan(new ForegroundColorSpan(getContext().getResources().getColor(R.color.signal_primary)), 0, headerString.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
return spannable;
} else {
return headerString;
}
}
private @NonNull String getHeaderString(int position) {
int contactType = getContactType(position);
if (contactType == ContactsDatabase.RECENT_TYPE || contactType == ContactsDatabase.DIVIDER_TYPE) {
return " ";
}
Cursor cursor = getCursorAtPositionOrThrow(position);
String letter = cursor.getString(cursor.getColumnIndexOrThrow(ContactsDatabase.NAME_COLUMN));
if (!TextUtils.isEmpty(letter)) {
String firstChar = letter.trim().substring(0, 1).toUpperCase();
if (Character.isLetterOrDigit(firstChar.codePointAt(0))) {
return firstChar;
}
}
return "#";
}
private int getContactType(int position) {
final Cursor cursor = getCursorAtPositionOrThrow(position);
return cursor.getInt(cursor.getColumnIndexOrThrow(ContactsDatabase.CONTACT_TYPE_COLUMN));
}
private boolean isPush(int position) {
return getContactType(position) == ContactsDatabase.PUSH_TYPE;
}
public interface ItemClickListener {
void onItemClick(ContactSelectionListItem item);
}
}

View File

@ -0,0 +1,85 @@
package org.thoughtcrime.securesms.loki.redesign.activities
import android.content.Context
import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import kotlinx.android.synthetic.main.contact_selection_list_divider.view.*
import network.loki.messenger.R
import org.thoughtcrime.securesms.loki.redesign.views.UserView
import org.thoughtcrime.securesms.mms.GlideRequests
import org.thoughtcrime.securesms.recipients.Recipient
class ContactSelectionListAdapter(private val context: Context, private val isMulti: Boolean) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private object ViewType {
const val Contact = 0
const val Divider = 1
}
lateinit var glide: GlideRequests
val selectedContacts = mutableSetOf<Recipient>()
var items = listOf<ContactSelectionListLoaderItem>()
set(value) { field = value; notifyDataSetChanged() }
var contactClickListener: ContactClickListener? = null
class ViewHolder(val view: UserView) : RecyclerView.ViewHolder(view)
class DividerViewHolder(val view: View): RecyclerView.ViewHolder(view)
override fun getItemCount(): Int {
return items.size
}
override fun getItemViewType(position: Int): Int {
return when (items[position]) {
is ContactSelectionListLoaderItem.Header -> ViewType.Divider
else -> ViewType.Contact
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return if (viewType == ViewType.Contact) {
ViewHolder(UserView(context))
} else {
val view = LayoutInflater.from(context).inflate(R.layout.contact_selection_list_divider, parent, false)
DividerViewHolder(view)
}
}
override fun onBindViewHolder(viewHolder: RecyclerView.ViewHolder, position: Int) {
val item = items[position]
if (viewHolder is ViewHolder) {
item as ContactSelectionListLoaderItem.Contact
viewHolder.view.setOnClickListener { contactClickListener?.onContactClick(item.recipient) }
val isSelected = selectedContacts.contains(item.recipient)
viewHolder.view.bind(item.recipient.address.serialize(), isSelected, glide)
} else if (viewHolder is DividerViewHolder) {
item as ContactSelectionListLoaderItem.Header
viewHolder.view.label.text = item.name
}
}
fun onContactClick(recipient: Recipient) {
if (selectedContacts.contains(recipient)) {
selectedContacts.remove(recipient)
contactClickListener?.onContactDeselected(recipient)
} else {
selectedContacts.add(recipient)
contactClickListener?.onContactSelected(recipient)
}
val index = items.indexOfFirst {
when (it) {
is ContactSelectionListLoaderItem.Header -> false
is ContactSelectionListLoaderItem.Contact -> it.recipient == recipient
}
}
notifyItemChanged(index)
}
}
interface ContactClickListener {
fun onContactClick(contact: Recipient)
fun onContactSelected(contact: Recipient)
fun onContactDeselected(contact: Recipient)
}

View File

@ -0,0 +1,80 @@
package org.thoughtcrime.securesms.loki.redesign.activities
import android.content.Context
import network.loki.messenger.R
import org.thoughtcrime.securesms.loki.redesign.utilities.Contact
import org.thoughtcrime.securesms.loki.redesign.utilities.ContactUtilities
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.util.AsyncLoader
sealed class ContactSelectionListLoaderItem {
class Header(val name: String): ContactSelectionListLoaderItem()
class Contact(val recipient: Recipient): ContactSelectionListLoaderItem()
}
class ContactSelectionListLoader(context: Context, val mode: Int, val filter: String?) : AsyncLoader<List<ContactSelectionListLoaderItem>>(context) {
object DisplayMode {
const val FLAG_FRIENDS = 1
const val FLAG_CLOSED_GROUPS = 1 shl 1
const val FLAG_OPEN_GROUPS = 1 shl 2
const val FLAG_ALL = FLAG_FRIENDS or FLAG_CLOSED_GROUPS or FLAG_OPEN_GROUPS
}
private fun isFlagSet(flag: Int): Boolean {
return mode and flag > 0
}
override fun loadInBackground(): List<ContactSelectionListLoaderItem> {
val contacts = ContactUtilities.getAllContacts(context).filter {
if (filter.isNullOrEmpty()) return@filter true
it.recipient.toShortString().contains(filter.trim(), true) || it.recipient.address.serialize().contains(filter.trim(), true)
}.sortedBy {
it.recipient.toShortString()
}
val list = mutableListOf<ContactSelectionListLoaderItem>()
if (isFlagSet(DisplayMode.FLAG_CLOSED_GROUPS)) {
list.addAll(getClosedGroups(contacts))
}
if (isFlagSet(DisplayMode.FLAG_OPEN_GROUPS)) {
list.addAll(getOpenGroups(contacts))
}
if (isFlagSet(DisplayMode.FLAG_FRIENDS)) {
list.addAll(getFriends(contacts))
}
return list
}
private fun getFriends(contacts: List<Contact>): List<ContactSelectionListLoaderItem> {
return getItems(contacts, context.getString(R.string.ContactSelectionListLoader_contacts)) {
!it.recipient.isGroupRecipient && it.isFriend && !it.isOurDevice && !it.isSlave
}
}
private fun getClosedGroups(contacts: List<Contact>): List<ContactSelectionListLoaderItem> {
return getItems(contacts, context.getString(R.string.ContactSelectionListLoader_closed_groups)) {
it.recipient.address.isSignalGroup
}
}
private fun getOpenGroups(contacts: List<Contact>): List<ContactSelectionListLoaderItem> {
return getItems(contacts, context.getString(R.string.ContactSelectionListLoader_open_groups)) {
it.recipient.address.isPublicChat
}
}
private fun getItems(contacts: List<Contact>, title: String, contactFilter: (Contact) -> Boolean): List<ContactSelectionListLoaderItem> {
val items = contacts.filter(contactFilter).map {
ContactSelectionListLoaderItem.Contact(it.recipient)
}
if (items.isEmpty()) return listOf()
val header = ContactSelectionListLoaderItem.Header(title)
return listOf(header) + items
}
}

View File

@ -0,0 +1,130 @@
package org.thoughtcrime.securesms.loki.redesign.fragments
import android.os.Bundle
import android.support.v4.app.Fragment
import android.support.v4.app.LoaderManager
import android.support.v4.content.Loader
import android.support.v4.widget.SwipeRefreshLayout.OnRefreshListener
import android.support.v7.widget.LinearLayoutManager
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import kotlinx.android.synthetic.main.contact_selection_list_fragment.*
import network.loki.messenger.R
import org.thoughtcrime.securesms.contacts.ContactsCursorLoader
import org.thoughtcrime.securesms.loki.redesign.activities.ContactClickListener
import org.thoughtcrime.securesms.loki.redesign.activities.ContactSelectionListAdapter
import org.thoughtcrime.securesms.loki.redesign.activities.ContactSelectionListLoader
import org.thoughtcrime.securesms.loki.redesign.activities.ContactSelectionListLoaderItem
import org.thoughtcrime.securesms.mms.GlideApp
import org.thoughtcrime.securesms.recipients.Recipient
class ContactSelectionListFragment : Fragment(), LoaderManager.LoaderCallbacks<List<ContactSelectionListLoaderItem>>, ContactClickListener {
companion object {
@JvmField val DISPLAY_MODE = "display_mode"
@JvmField val MULTI_SELECT = "multi_select"
@JvmField val REFRESHABLE = "refreshable"
}
val selectedContacts: List<String>
get() = listAdapter.selectedContacts.map { it.address.serialize() }
private var items = listOf<ContactSelectionListLoaderItem>()
set(value) { field = value; listAdapter.items = value }
private val listAdapter by lazy {
val result = ContactSelectionListAdapter(activity!!, isMulti)
result.glide = GlideApp.with(this)
result.contactClickListener = this
result
}
private val isMulti: Boolean by lazy {
activity!!.intent.getBooleanExtra(MULTI_SELECT, false)
}
private var cursorFilter: String? = null
var onContactSelectedListener: OnContactSelectedListener? = null
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
recyclerView.layoutManager = LinearLayoutManager(activity)
recyclerView.adapter = listAdapter
swipeRefresh.isEnabled = activity!!.intent.getBooleanExtra(REFRESHABLE, true)
}
override fun onStart() {
super.onStart()
LoaderManager.getInstance(this).initLoader(0, null, this)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.contact_selection_list_fragment, container, false)
}
fun setQueryFilter(filter: String?) {
cursorFilter = filter
this.loaderManager.restartLoader<List<ContactSelectionListLoaderItem>>(0, null, this)
}
fun resetQueryFilter() {
setQueryFilter(null)
swipeRefresh.isRefreshing = false
}
fun setRefreshing(refreshing: Boolean) {
swipeRefresh.isRefreshing = refreshing
}
fun setOnRefreshListener(onRefreshListener: OnRefreshListener?) {
this.swipeRefresh.setOnRefreshListener(onRefreshListener)
}
override fun onCreateLoader(id: Int, args: Bundle?): Loader<List<ContactSelectionListLoaderItem>> {
return ContactSelectionListLoader(activity!!,
activity!!.intent.getIntExtra(DISPLAY_MODE, ContactsCursorLoader.DisplayMode.FLAG_ALL),
cursorFilter)
}
override fun onLoadFinished(loader: Loader<List<ContactSelectionListLoaderItem>>, items: List<ContactSelectionListLoaderItem>) {
update(items)
}
override fun onLoaderReset(loader: Loader<List<ContactSelectionListLoaderItem>>) {
update(listOf())
}
private fun update(items: List<ContactSelectionListLoaderItem>) {
this.items = items
mainContentContainer.visibility = if (items.isEmpty()) View.GONE else View.VISIBLE
emptyStateContainer.visibility = if (items.isEmpty()) View.VISIBLE else View.GONE
val useFastScroller = items.count() > 20
recyclerView.isVerticalScrollBarEnabled = !useFastScroller
if (useFastScroller) {
fastScroller.visibility = View.VISIBLE
fastScroller.setRecyclerView(recyclerView)
} else {
fastScroller.visibility = View.GONE
}
}
override fun onContactClick(contact: Recipient) {
listAdapter.onContactClick(contact)
}
override fun onContactSelected(contact: Recipient) {
onContactSelectedListener?.onContactSelected(contact.address.serialize())
}
override fun onContactDeselected(contact: Recipient) {
onContactSelectedListener?.onContactDeselected(contact.address.serialize())
}
interface OnContactSelectedListener {
fun onContactSelected(number: String?)
fun onContactDeselected(number: String?)
}
}

View File

@ -8,7 +8,6 @@ import org.whispersystems.signalservice.loki.messaging.LokiThreadFriendRequestSt
data class Contact(
val recipient: Recipient,
val threadId: Long,
val isFriend: Boolean,
val isSlave: Boolean,
val isOurDevice: Boolean
@ -35,6 +34,8 @@ object ContactUtilities {
val lokiThreadDatabase = DatabaseFactory.getLokiThreadDatabase(context)
val userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context)
val lokiAPIDatabase = DatabaseFactory.getLokiAPIDatabase(context)
val groupDatabase = DatabaseFactory.getGroupDatabase(context)
val lokiUserDatabase = DatabaseFactory.getLokiUserDatabase(context)
val ourDeviceLinks = lokiAPIDatabase.getDeviceLinks(userHexEncodedPublicKey)
val ourDevices = ourDeviceLinks.flatMap {
@ -43,23 +44,27 @@ object ContactUtilities {
ourDevices.add(userHexEncodedPublicKey.toLowerCase())
val cursor = threadDatabase.conversationList
val reader = threadDatabase.readerFor(cursor)
val result = mutableSetOf<Contact>()
threadDatabase.readerFor(cursor).use { reader ->
while (reader.next != null) {
val thread = reader.current
val recipient = thread.recipient
val hexEncodedPublicKey = recipient.address.serialize()
val address = recipient.address.serialize()
val isOurDevice = ourDevices.contains(address)
val isFriend = lokiThreadDatabase.getFriendRequestStatus(thread.threadId) == LokiThreadFriendRequestStatus.FRIENDS
var isSlave = false
if (!recipient.isGroupRecipient) {
val deviceLinks = lokiAPIDatabase.getDeviceLinks(hexEncodedPublicKey)
isSlave = deviceLinks.find { it.slaveHexEncodedPublicKey == hexEncodedPublicKey } != null
}
val isOurDevice = ourDevices.contains(hexEncodedPublicKey)
var displayName = ""
result.add(Contact(recipient, thread.threadId, isFriend, isSlave, isOurDevice))
if (!recipient.isGroupRecipient) {
val deviceLinks = lokiAPIDatabase.getDeviceLinks(address)
isSlave = deviceLinks.find { it.slaveHexEncodedPublicKey == address } != null
}
result.add(Contact(recipient, isFriend, isSlave, isOurDevice))
}
}
return result
}

View File

@ -455,7 +455,8 @@ public class Recipient implements RecipientModifiedListener {
}
public synchronized String toShortString() {
return (getName() == null ? address.serialize() : getName());
String name = getName();
return (name == null ? address.serialize() : name);
}
public synchronized @NonNull Drawable getFallbackContactPhotoDrawable(Context context, boolean inverted) {