diff --git a/res/layout/contact_selection_activity.xml b/res/layout/contact_selection_activity.xml
index 748fef44c0..b14d81f2bf 100644
--- a/res/layout/contact_selection_activity.xml
+++ b/res/layout/contact_selection_activity.xml
@@ -19,6 +19,6 @@
+ android:name="org.thoughtcrime.securesms.loki.redesign.fragments.contactselection.ContactSelectionListFragment" />
\ No newline at end of file
diff --git a/res/layout/contact_selection_list_fragment.xml b/res/layout/contact_selection_list_fragment.xml
index 6d6536b6a7..2eb74847f8 100644
--- a/res/layout/contact_selection_list_fragment.xml
+++ b/res/layout/contact_selection_list_fragment.xml
@@ -1,92 +1,53 @@
-
-
-
+
-
-
-
-
-
-
-
+ android:gravity="center"
+ android:orientation="vertical">
-
+
-
+
+
+
+
+
+
+ android:layout_height="match_parent"
+ android:clipToPadding="false"
+ android:scrollbars="vertical" />
-
+
-
+
-
-
-
-
-
-
diff --git a/res/layout/contact_selection_list_item.xml b/res/layout/contact_selection_list_item.xml
deleted file mode 100644
index e639a524f3..0000000000
--- a/res/layout/contact_selection_list_item.xml
+++ /dev/null
@@ -1,79 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/res/layout/invite_activity.xml b/res/layout/invite_activity.xml
index f3949c7506..98bb11c628 100644
--- a/res/layout/invite_activity.xml
+++ b/res/layout/invite_activity.xml
@@ -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.contactselection.ContactSelectionListFragment"
tools:layout="@layout/contact_selection_list_fragment"/>
+ android:name="org.thoughtcrime.securesms.loki.redesign.fragments.contactselection.ContactSelectionListFragment" />
Dismiss
Restore
-
-
Copied to clipboard
Are you sure you want to leave this group?
Are you sure you want to delete this conversation?
Conversation deleted
+ Contacts
+ Closed Groups
+ Open Groups
Push Notifications
There are two ways Session can handle push notifications. Make sure to read the descriptions carefully before you choose.
Firebase Cloud Messaging
diff --git a/src/org/thoughtcrime/securesms/ContactSelectionActivity.java b/src/org/thoughtcrime/securesms/ContactSelectionActivity.java
index e408709009..b74c24453c 100644
--- a/src/org/thoughtcrime/securesms/ContactSelectionActivity.java
+++ b/src/org/thoughtcrime/securesms/ContactSelectionActivity.java
@@ -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.fragments.contactselection.ContactSelectionListFragment;
+import org.thoughtcrime.securesms.loki.redesign.fragments.contactselection.ContactSelectionListLoader.DisplayMode;
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;
@@ -36,8 +36,8 @@ import network.loki.messenger.R;
*
*/
public abstract class ContactSelectionActivity extends PassphraseRequiredActionBarActivity
- implements SwipeRefreshLayout.OnRefreshListener,
- ContactSelectionListFragment.OnContactSelectedListener
+ implements SwipeRefreshLayout.OnRefreshListener,
+ ContactSelectionListFragment.OnContactSelectedListener
{
private static final String TAG = ContactSelectionActivity.class.getSimpleName();
@@ -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);
diff --git a/src/org/thoughtcrime/securesms/ContactSelectionListFragment.java b/src/org/thoughtcrime/securesms/ContactSelectionListFragment.java
deleted file mode 100644
index c95359516f..0000000000
--- a/src/org/thoughtcrime/securesms/ContactSelectionListFragment.java
+++ /dev/null
@@ -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 .
- */
-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
-{
- @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 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 getSelectedContacts() {
- List 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 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 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 loader) {
- ((CursorRecyclerViewAdapter) recyclerView.getAdapter()).changeCursor(null);
- fastScroller.setVisibility(View.GONE);
- }
-
- @SuppressLint("StaticFieldLeak")
- private void handleContactPermissionGranted() {
- new AsyncTask() {
- @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);
- }
-
-}
diff --git a/src/org/thoughtcrime/securesms/GroupCreateActivity.java b/src/org/thoughtcrime/securesms/GroupCreateActivity.java
index d87946c97d..63467e05af 100644
--- a/src/org/thoughtcrime/securesms/GroupCreateActivity.java
+++ b/src/org/thoughtcrime/securesms/GroupCreateActivity.java
@@ -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.fragments.contactselection.ContactSelectionListFragment;
+import org.thoughtcrime.securesms.loki.redesign.fragments.contactselection.ContactSelectionListLoader.DisplayMode;
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);
}
}
diff --git a/src/org/thoughtcrime/securesms/InviteActivity.java b/src/org/thoughtcrime/securesms/InviteActivity.java
index 2aab713921..cd0f14d76b 100644
--- a/src/org/thoughtcrime/securesms/InviteActivity.java
+++ b/src/org/thoughtcrime/securesms/InviteActivity.java
@@ -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.fragments.contactselection.ContactSelectionListFragment;
+import org.thoughtcrime.securesms.loki.redesign.fragments.contactselection.ContactSelectionListLoader.DisplayMode;
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() {
@Override
public void onSuccess(Boolean result) {
- contactsFragment.reset();
}
@Override
diff --git a/src/org/thoughtcrime/securesms/PushContactSelectionActivity.java b/src/org/thoughtcrime/securesms/PushContactSelectionActivity.java
index ea64762e8e..f47dd5bb8c 100644
--- a/src/org/thoughtcrime/securesms/PushContactSelectionActivity.java
+++ b/src/org/thoughtcrime/securesms/PushContactSelectionActivity.java
@@ -19,6 +19,8 @@ package org.thoughtcrime.securesms;
import android.content.Intent;
import android.os.Bundle;
+import org.thoughtcrime.securesms.loki.redesign.fragments.contactselection.ContactSelectionListFragment;
+
import java.util.ArrayList;
import java.util.List;
diff --git a/src/org/thoughtcrime/securesms/ShareActivity.java b/src/org/thoughtcrime/securesms/ShareActivity.java
index 6138c373fc..32ca247994 100644
--- a/src/org/thoughtcrime/securesms/ShareActivity.java
+++ b/src/org/thoughtcrime/securesms/ShareActivity.java
@@ -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.fragments.contactselection.ContactSelectionListFragment;
+import org.thoughtcrime.securesms.loki.redesign.fragments.contactselection.ContactSelectionListLoader.DisplayMode;
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")
diff --git a/src/org/thoughtcrime/securesms/contacts/ContactSelectionListAdapter.java b/src/org/thoughtcrime/securesms/contacts/ContactSelectionListAdapter.java
deleted file mode 100644
index 3976cfc453..0000000000
--- a/src/org/thoughtcrime/securesms/contacts/ContactSelectionListAdapter.java
+++ /dev/null
@@ -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 .
- */
-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
- implements FastScrollAdapter,
- StickyHeaderAdapter
-{
- 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 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 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);
- }
-}
diff --git a/src/org/thoughtcrime/securesms/contacts/ContactSelectionListItem.java b/src/org/thoughtcrime/securesms/contacts/ContactSelectionListItem.java
deleted file mode 100644
index eb41cc5035..0000000000
--- a/src/org/thoughtcrime/securesms/contacts/ContactSelectionListItem.java
+++ /dev/null
@@ -1,161 +0,0 @@
-package org.thoughtcrime.securesms.contacts;
-
-import android.content.Context;
-import android.support.annotation.NonNull;
-import android.text.TextUtils;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.CheckBox;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import org.thoughtcrime.securesms.database.Address;
-import org.thoughtcrime.securesms.database.DatabaseFactory;
-import org.thoughtcrime.securesms.loki.redesign.views.ProfilePictureView;
-import org.thoughtcrime.securesms.mms.GlideRequests;
-import org.thoughtcrime.securesms.recipients.Recipient;
-import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
-import org.thoughtcrime.securesms.util.GroupUtil;
-import org.thoughtcrime.securesms.util.Util;
-import org.thoughtcrime.securesms.util.ViewUtil;
-import org.whispersystems.signalservice.loki.api.LokiAPI;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
-
-import network.loki.messenger.R;
-
-public class ContactSelectionListItem extends LinearLayout implements RecipientModifiedListener {
-
- @SuppressWarnings("unused")
- private static final String TAG = ContactSelectionListItem.class.getSimpleName();
-
- private ProfilePictureView profilePictureView;
- private TextView numberView;
- private TextView nameView;
- private TextView labelView;
- private CheckBox checkBox;
-
- private String number;
- private Recipient recipient;
- private GlideRequests glideRequests;
- private long threadID;
-
- public ContactSelectionListItem(Context context) {
- super(context);
- }
-
- public ContactSelectionListItem(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- this.profilePictureView = findViewById(R.id.profilePictureView);
- this.numberView = findViewById(R.id.number);
- this.labelView = findViewById(R.id.label);
- this.nameView = findViewById(R.id.name);
- this.checkBox = findViewById(R.id.check_box);
-
- ViewUtil.setTextViewGravityStart(this.nameView, getContext());
- }
-
- public void set(@NonNull GlideRequests glideRequests, int type, String name, String number, String label, int color, boolean multiSelect) {
- this.glideRequests = glideRequests;
- this.number = number;
-
- if (type == ContactsDatabase.NEW_TYPE) {
- this.recipient = null;
- } else if (!TextUtils.isEmpty(number)) {
- Address address = Address.fromExternal(getContext(), number);
- this.recipient = Recipient.from(getContext(), address, true);
- this.recipient.addListener(this);
-
- if (this.recipient.getName() != null) {
- name = this.recipient.getName();
- }
- }
-
- threadID = DatabaseFactory.getThreadDatabase(getContext()).getThreadIdFor(recipient);
-
- this.numberView.setTextColor(color);
- updateProfilePicture(glideRequests, name, threadID);
-
- if (!multiSelect && recipient != null && recipient.isLocalNumber()) {
- name = getContext().getString(R.string.note_to_self);
- }
-
- setText(type, name, number, label);
-
- if (multiSelect) this.checkBox.setVisibility(View.VISIBLE);
- else this.checkBox.setVisibility(View.GONE);
- }
-
- public void setChecked(boolean selected) {
- this.checkBox.setChecked(selected);
- }
-
- public void unbind(GlideRequests glideRequests) {
- if (recipient != null) {
- recipient.removeListener(this);
- recipient = null;
- }
- }
-
- private void setText(int type, String name, String number, String label) {
- if (number == null || number.isEmpty() || GroupUtil.isEncodedGroup(number)) {
- this.nameView.setEnabled(false);
- this.numberView.setText("");
- this.labelView.setVisibility(View.GONE);
- } else if (type == ContactsDatabase.PUSH_TYPE) {
- this.numberView.setText(number);
- this.nameView.setEnabled(true);
- this.labelView.setVisibility(View.GONE);
- } else {
- this.numberView.setText(number);
- this.nameView.setEnabled(true);
- this.labelView.setText(label);
- this.labelView.setVisibility(View.VISIBLE);
- }
-
- this.nameView.setText(name);
- }
-
- public String getNumber() {
- return number;
- }
-
- @Override
- public void onModified(final Recipient recipient) {
- if (this.recipient == recipient) {
- Util.runOnMain(() -> {
- threadID = DatabaseFactory.getThreadDatabase(getContext()).getThreadIdFor(recipient);
- updateProfilePicture(glideRequests, recipient.getName(), threadID);
- nameView.setText(recipient.toShortString());
- });
- }
- }
-
- private void updateProfilePicture(GlideRequests glide, String name, long threadID) {
- if (this.recipient.isGroupRecipient()) {
- Set usersAsSet = LokiAPI.Companion.getUserHexEncodedPublicKeyCache().get(threadID);
- if (usersAsSet == null) {
- usersAsSet = new HashSet<>();
- }
- ArrayList users = new ArrayList<>(usersAsSet);
- Collections.sort(users); // Sort to provide a level of stability
- profilePictureView.setHexEncodedPublicKey(users.size() > 0 ? users.get(0) : "");
- profilePictureView.setAdditionalHexEncodedPublicKey(users.size() > 1 ? users.get(1) : "");
- profilePictureView.setRSSFeed(name.equals("Loki News") || name.equals("Session Updates"));
- } else {
- profilePictureView.setHexEncodedPublicKey(this.number);
- profilePictureView.setAdditionalHexEncodedPublicKey(null);
- profilePictureView.setRSSFeed(false);
- }
- profilePictureView.glide = glide;
- profilePictureView.update();
- }
-}
diff --git a/src/org/thoughtcrime/securesms/loki/redesign/activities/CreateClosedGroupAdapter.kt b/src/org/thoughtcrime/securesms/loki/redesign/activities/CreateClosedGroupAdapter.kt
index bd8c6eff02..22f9c76742 100644
--- a/src/org/thoughtcrime/securesms/loki/redesign/activities/CreateClosedGroupAdapter.kt
+++ b/src/org/thoughtcrime/securesms/loki/redesign/activities/CreateClosedGroupAdapter.kt
@@ -3,8 +3,10 @@ package org.thoughtcrime.securesms.loki.redesign.activities
import android.content.Context
import android.support.v7.widget.RecyclerView
import android.view.ViewGroup
+import org.thoughtcrime.securesms.database.Address
import org.thoughtcrime.securesms.loki.redesign.views.UserView
import org.thoughtcrime.securesms.mms.GlideRequests
+import org.thoughtcrime.securesms.recipients.Recipient
class CreateClosedGroupAdapter(private val context: Context) : RecyclerView.Adapter() {
lateinit var glide: GlideRequests
@@ -28,7 +30,7 @@ class CreateClosedGroupAdapter(private val context: Context) : RecyclerView.Adap
val member = members[position]
viewHolder.view.setOnClickListener { memberClickListener?.onMemberClick(member) }
val isSelected = selectedMembers.contains(member)
- viewHolder.view.bind(member, isSelected, glide)
+ viewHolder.view.bind(Recipient.from(context, Address.fromSerialized(member), false), isSelected, glide)
}
fun onMemberClick(member: String) {
diff --git a/src/org/thoughtcrime/securesms/loki/redesign/activities/CreateClosedGroupLoader.kt b/src/org/thoughtcrime/securesms/loki/redesign/activities/CreateClosedGroupLoader.kt
index 2c07fe0772..89fcd2ef26 100644
--- a/src/org/thoughtcrime/securesms/loki/redesign/activities/CreateClosedGroupLoader.kt
+++ b/src/org/thoughtcrime/securesms/loki/redesign/activities/CreateClosedGroupLoader.kt
@@ -1,33 +1,18 @@
package org.thoughtcrime.securesms.loki.redesign.activities
import android.content.Context
-import org.thoughtcrime.securesms.database.DatabaseFactory
+import org.thoughtcrime.securesms.loki.redesign.utilities.ContactUtilities
import org.thoughtcrime.securesms.util.AsyncLoader
-import org.thoughtcrime.securesms.util.TextSecurePreferences
-import org.whispersystems.signalservice.loki.messaging.LokiThreadFriendRequestStatus
class CreateClosedGroupLoader(context: Context) : AsyncLoader>(context) {
override fun loadInBackground(): List {
- val threadDatabase = DatabaseFactory.getThreadDatabase(context)
- val lokiThreadDatabase = DatabaseFactory.getLokiThreadDatabase(context)
- val userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context)
- val deviceLinks = DatabaseFactory.getLokiAPIDatabase(context).getDeviceLinks(userHexEncodedPublicKey)
- val userLinkedDeviceHexEncodedPublicKeys = deviceLinks.flatMap {
- listOf( it.masterHexEncodedPublicKey.toLowerCase(), it.slaveHexEncodedPublicKey.toLowerCase() )
- }.toMutableSet()
- userLinkedDeviceHexEncodedPublicKeys.add(userHexEncodedPublicKey.toLowerCase())
- val cursor = threadDatabase.conversationList
- val reader = threadDatabase.readerFor(cursor)
- val result = mutableListOf()
- while (reader.next != null) {
- val thread = reader.current
- if (thread.recipient.isGroupRecipient) { continue }
- if (lokiThreadDatabase.getFriendRequestStatus(thread.threadId) != LokiThreadFriendRequestStatus.FRIENDS) { continue }
- val hexEncodedPublicKey = thread.recipient.address.toString().toLowerCase()
- if (userLinkedDeviceHexEncodedPublicKeys.contains(hexEncodedPublicKey)) { continue }
- result.add(hexEncodedPublicKey)
+ val contacts = ContactUtilities.getAllContacts(context)
+ // Only show the master devices of the users we are friends with
+ return contacts.filter { contact ->
+ !contact.recipient.isGroupRecipient && contact.isFriend && !contact.isOurDevice && !contact.isSlave
+ }.map {
+ it.recipient.address.toPhoneString()
}
- return result
}
}
\ No newline at end of file
diff --git a/src/org/thoughtcrime/securesms/loki/redesign/fragments/contactselection/ContactSelectionListAdapter.kt b/src/org/thoughtcrime/securesms/loki/redesign/fragments/contactselection/ContactSelectionListAdapter.kt
new file mode 100644
index 0000000000..f0dc5057bf
--- /dev/null
+++ b/src/org/thoughtcrime/securesms/loki/redesign/fragments/contactselection/ContactSelectionListAdapter.kt
@@ -0,0 +1,85 @@
+package org.thoughtcrime.securesms.loki.redesign.fragments.contactselection
+
+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 multiSelect: Boolean) : RecyclerView.Adapter() {
+ lateinit var glide: GlideRequests
+ val selectedContacts = mutableSetOf()
+ var items = listOf()
+ set(value) { field = value; notifyDataSetChanged() }
+ var contactClickListener: ContactClickListener? = null
+
+ private object ViewType {
+ const val Contact = 0
+ const val Divider = 1
+ }
+
+ class UserViewHolder(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 ContactSelectionListItem.Header -> ViewType.Divider
+ else -> ViewType.Contact
+ }
+ }
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
+ return if (viewType == ViewType.Contact) {
+ UserViewHolder(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 UserViewHolder) {
+ item as ContactSelectionListItem.Contact
+ viewHolder.view.setOnClickListener { contactClickListener?.onContactClick(item.recipient) }
+ val isSelected = selectedContacts.contains(item.recipient)
+ viewHolder.view.bind(item.recipient, isSelected, glide)
+ viewHolder.view.setCheckBoxVisible(multiSelect)
+ } else if (viewHolder is DividerViewHolder) {
+ item as ContactSelectionListItem.Header
+ viewHolder.view.label.text = item.name
+ }
+ }
+
+ fun onContactClick(recipient: Recipient) {
+ if (selectedContacts.contains(recipient)) {
+ selectedContacts.remove(recipient)
+ contactClickListener?.onContactDeselected(recipient)
+ } else if (multiSelect || selectedContacts.isEmpty()) {
+ selectedContacts.add(recipient)
+ contactClickListener?.onContactSelected(recipient)
+ }
+ val index = items.indexOfFirst {
+ when (it) {
+ is ContactSelectionListItem.Header -> false
+ is ContactSelectionListItem.Contact -> it.recipient == recipient
+ }
+ }
+ notifyItemChanged(index)
+ }
+}
+
+interface ContactClickListener {
+ fun onContactClick(contact: Recipient)
+ fun onContactSelected(contact: Recipient)
+ fun onContactDeselected(contact: Recipient)
+}
\ No newline at end of file
diff --git a/src/org/thoughtcrime/securesms/loki/redesign/fragments/contactselection/ContactSelectionListFragment.kt b/src/org/thoughtcrime/securesms/loki/redesign/fragments/contactselection/ContactSelectionListFragment.kt
new file mode 100644
index 0000000000..a4dcc92af5
--- /dev/null
+++ b/src/org/thoughtcrime/securesms/loki/redesign/fragments/contactselection/ContactSelectionListFragment.kt
@@ -0,0 +1,112 @@
+package org.thoughtcrime.securesms.loki.redesign.fragments.contactselection
+
+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.mms.GlideApp
+import org.thoughtcrime.securesms.recipients.Recipient
+
+class ContactSelectionListFragment : Fragment(), LoaderManager.LoaderCallbacks>, ContactClickListener {
+ private var cursorFilter: String? = null
+ var onContactSelectedListener: OnContactSelectedListener? = null
+
+ val selectedContacts: List
+ get() = listAdapter.selectedContacts.map { it.address.serialize() }
+
+ private val multiSelect: Boolean by lazy {
+ activity!!.intent.getBooleanExtra(MULTI_SELECT, false)
+ }
+
+ private val listAdapter by lazy {
+ val result = ContactSelectionListAdapter(activity!!, multiSelect)
+ result.glide = GlideApp.with(this)
+ result.contactClickListener = this
+ result
+ }
+
+ companion object {
+ @JvmField val DISPLAY_MODE = "display_mode"
+ @JvmField val MULTI_SELECT = "multi_select"
+ @JvmField val REFRESHABLE = "refreshable"
+ }
+
+ interface OnContactSelectedListener {
+ fun onContactSelected(number: String?)
+ fun onContactDeselected(number: String?)
+ }
+
+ override fun onActivityCreated(savedInstanceState: Bundle?) {
+ super.onActivityCreated(savedInstanceState)
+ recyclerView.layoutManager = LinearLayoutManager(activity)
+ recyclerView.adapter = listAdapter
+ swipeRefreshLayout.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
+ LoaderManager.getInstance(this).restartLoader(0, null, this)
+ }
+
+ fun resetQueryFilter() {
+ setQueryFilter(null)
+ swipeRefreshLayout.isRefreshing = false
+ }
+
+ fun setRefreshing(refreshing: Boolean) {
+ swipeRefreshLayout.isRefreshing = refreshing
+ }
+
+ fun setOnRefreshListener(onRefreshListener: OnRefreshListener?) {
+ swipeRefreshLayout.setOnRefreshListener(onRefreshListener)
+ }
+
+ override fun onCreateLoader(id: Int, args: Bundle?): Loader> {
+ return ContactSelectionListLoader(activity!!,
+ activity!!.intent.getIntExtra(DISPLAY_MODE, ContactsCursorLoader.DisplayMode.FLAG_ALL),
+ cursorFilter)
+ }
+
+ override fun onLoadFinished(loader: Loader>, items: List) {
+ update(items)
+ }
+
+ override fun onLoaderReset(loader: Loader>) {
+ update(listOf())
+ }
+
+ private fun update(items: List) {
+ listAdapter.items = items
+ mainContentContainer.visibility = if (items.isEmpty()) View.GONE else View.VISIBLE
+ emptyStateContainer.visibility = if (items.isEmpty()) View.VISIBLE else 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())
+ }
+}
\ No newline at end of file
diff --git a/src/org/thoughtcrime/securesms/loki/redesign/fragments/contactselection/ContactSelectionListLoader.kt b/src/org/thoughtcrime/securesms/loki/redesign/fragments/contactselection/ContactSelectionListLoader.kt
new file mode 100644
index 0000000000..8e28e8d72b
--- /dev/null
+++ b/src/org/thoughtcrime/securesms/loki/redesign/fragments/contactselection/ContactSelectionListLoader.kt
@@ -0,0 +1,83 @@
+package org.thoughtcrime.securesms.loki.redesign.fragments.contactselection
+
+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 ContactSelectionListItem {
+ class Header(val name: String) : ContactSelectionListItem()
+ class Contact(val recipient: Recipient) : ContactSelectionListItem()
+}
+
+class ContactSelectionListLoader(context: Context, val mode: Int, val filter: String?) : AsyncLoader>(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 {
+ 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()
+
+ 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): List {
+ return getItems(contacts, context.getString(R.string.fragment_contact_selection_contacts_title)) {
+ !it.recipient.isGroupRecipient && it.isFriend && !it.isOurDevice && !it.isSlave
+ }
+ }
+
+ private fun getClosedGroups(contacts: List): List {
+ return getItems(contacts, context.getString(R.string.fragment_contact_selection_closed_groups_title)) {
+ it.recipient.address.isSignalGroup
+ }
+ }
+
+ private fun getOpenGroups(contacts: List): List {
+ return getItems(contacts, context.getString(R.string.fragment_contact_selection_open_groups_title)) {
+ it.recipient.address.isPublicChat
+ }
+ }
+
+ private fun getItems(contacts: List, title: String, contactFilter: (Contact) -> Boolean): List {
+ val items = contacts.filter(contactFilter).map {
+ ContactSelectionListItem.Contact(it.recipient)
+ }
+
+ if (items.isEmpty()) return listOf()
+
+ val header = ContactSelectionListItem.Header(title)
+
+ return listOf(header) + items
+ }
+}
\ No newline at end of file
diff --git a/src/org/thoughtcrime/securesms/loki/redesign/utilities/ContactUtilities.kt b/src/org/thoughtcrime/securesms/loki/redesign/utilities/ContactUtilities.kt
new file mode 100644
index 0000000000..450ce571c3
--- /dev/null
+++ b/src/org/thoughtcrime/securesms/loki/redesign/utilities/ContactUtilities.kt
@@ -0,0 +1,60 @@
+package org.thoughtcrime.securesms.loki.redesign.utilities
+
+import android.content.Context
+import org.thoughtcrime.securesms.database.DatabaseFactory
+import org.thoughtcrime.securesms.recipients.Recipient
+import org.thoughtcrime.securesms.util.TextSecurePreferences
+import org.whispersystems.signalservice.loki.messaging.LokiThreadFriendRequestStatus
+
+data class Contact(
+ val recipient: Recipient,
+ val isFriend: Boolean,
+ val isSlave: Boolean,
+ val isOurDevice: Boolean
+) {
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other?.javaClass != javaClass) return false
+ other as Contact
+ return recipient == other.recipient
+ }
+
+ override fun hashCode(): Int {
+ return recipient.hashCode()
+ }
+}
+
+object ContactUtilities {
+
+ @JvmStatic
+ fun getAllContacts(context: Context): Set {
+ val threadDatabase = DatabaseFactory.getThreadDatabase(context)
+ val lokiThreadDatabase = DatabaseFactory.getLokiThreadDatabase(context)
+ val userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context)
+ val lokiAPIDatabase = DatabaseFactory.getLokiAPIDatabase(context)
+ val ourDeviceLinks = lokiAPIDatabase.getDeviceLinks(userHexEncodedPublicKey)
+ val ourDevices = ourDeviceLinks.flatMap {
+ listOf( it.masterHexEncodedPublicKey.toLowerCase(), it.slaveHexEncodedPublicKey.toLowerCase() )
+ }.toMutableSet()
+ ourDevices.add(userHexEncodedPublicKey.toLowerCase())
+ val cursor = threadDatabase.conversationList
+ val result = mutableSetOf()
+ threadDatabase.readerFor(cursor).use { reader ->
+ while (reader.next != null) {
+ val thread = reader.current
+ val recipient = thread.recipient
+ 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(address)
+ isSlave = deviceLinks.find { it.slaveHexEncodedPublicKey == address } != null
+ }
+ result.add(Contact(recipient, isFriend, isSlave, isOurDevice))
+ }
+ }
+ return result
+ }
+}
\ No newline at end of file
diff --git a/src/org/thoughtcrime/securesms/loki/redesign/views/UserView.kt b/src/org/thoughtcrime/securesms/loki/redesign/views/UserView.kt
index 2b6e6536f7..6df9b4ad60 100644
--- a/src/org/thoughtcrime/securesms/loki/redesign/views/UserView.kt
+++ b/src/org/thoughtcrime/securesms/loki/redesign/views/UserView.kt
@@ -3,16 +3,17 @@ package org.thoughtcrime.securesms.loki.redesign.views
import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
+import android.view.View
import android.widget.LinearLayout
import kotlinx.android.synthetic.main.view_conversation.view.profilePictureView
import kotlinx.android.synthetic.main.view_user.view.*
import network.loki.messenger.R
-import org.thoughtcrime.securesms.database.Address
+import org.thoughtcrime.securesms.groups.GroupManager
import org.thoughtcrime.securesms.mms.GlideRequests
import org.thoughtcrime.securesms.recipients.Recipient
+import org.whispersystems.signalservice.loki.api.LokiAPI
class UserView : LinearLayout {
- var user: String? = null
// region Lifecycle
constructor(context: Context) : super(context) {
@@ -39,13 +40,33 @@ class UserView : LinearLayout {
// endregion
// region Updating
- fun bind(user: String, isSelected: Boolean, glide: GlideRequests) {
- profilePictureView.hexEncodedPublicKey = user
- profilePictureView.additionalHexEncodedPublicKey = null
- profilePictureView.isRSSFeed = false
+ fun setCheckBoxVisible(visible: Boolean) {
+ tickImageView.visibility = if (visible) View.VISIBLE else View.GONE
+ }
+
+ fun bind(user: Recipient, isSelected: Boolean, glide: GlideRequests) {
+ val address = user.address.serialize()
+ if (user.isGroupRecipient) {
+ if ("Session Public Chat" == user.name || user.address.isRSSFeed) {
+ profilePictureView.hexEncodedPublicKey = ""
+ profilePictureView.additionalHexEncodedPublicKey = null
+ profilePictureView.isRSSFeed = true
+ } else {
+ val threadID = GroupManager.getThreadIdFromGroupId(address, context)
+ val users = LokiAPI.userHexEncodedPublicKeyCache[threadID]?.toList() ?: listOf()
+ val randomUsers = users.sorted() // Sort to provide a level of stability
+ profilePictureView.hexEncodedPublicKey = randomUsers.getOrNull(0) ?: ""
+ profilePictureView.additionalHexEncodedPublicKey = randomUsers.getOrNull(1) ?: ""
+ profilePictureView.isRSSFeed = false
+ }
+ } else {
+ profilePictureView.hexEncodedPublicKey = address
+ profilePictureView.additionalHexEncodedPublicKey = null
+ profilePictureView.isRSSFeed = false
+ }
profilePictureView.glide = glide
profilePictureView.update()
- nameTextView.text = Recipient.from(context, Address.fromSerialized(user), false).name ?: "Unknown Contact"
+ nameTextView.text = user.name ?: "Unknown Contact"
tickImageView.setImageResource(if (isSelected) R.drawable.ic_circle_check else R.drawable.ic_circle)
}
// endregion
diff --git a/src/org/thoughtcrime/securesms/recipients/Recipient.java b/src/org/thoughtcrime/securesms/recipients/Recipient.java
index 8e61e4900c..9db3acfa06 100644
--- a/src/org/thoughtcrime/securesms/recipients/Recipient.java
+++ b/src/org/thoughtcrime/securesms/recipients/Recipient.java
@@ -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) {