/* * Copyright (C) 2013-2017 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.database.Cursor; import android.database.MatrixCursor; import android.database.MergeCursor; import android.provider.ContactsContract; import android.text.TextUtils; import androidx.annotation.NonNull; import androidx.loader.content.CursorLoader; import org.session.libsession.utilities.GroupRecord; import org.thoughtcrime.securesms.database.GroupDatabase; import org.thoughtcrime.securesms.database.ThreadDatabase; import org.thoughtcrime.securesms.database.model.ThreadRecord; import org.thoughtcrime.securesms.dependencies.DatabaseComponent; import java.util.ArrayList; import java.util.List; import network.loki.messenger.R; /** * CursorLoader that initializes a ContactsDatabase instance * * @author Jake McGinty */ public class ContactsCursorLoader extends CursorLoader { private static final String TAG = ContactsCursorLoader.class.getSimpleName(); static final int NORMAL_TYPE = 0; static final int PUSH_TYPE = 1; static final int NEW_TYPE = 2; static final int RECENT_TYPE = 3; static final int DIVIDER_TYPE = 4; static final String CONTACT_TYPE_COLUMN = "contact_type"; static final String LABEL_COLUMN = "label"; static final String NUMBER_TYPE_COLUMN = "number_type"; static final String NUMBER_COLUMN = "number"; static final String NAME_COLUMN = "name"; public static final class DisplayMode { public static final int FLAG_PUSH = 1; public static final int FLAG_SMS = 1 << 1; public static final int FLAG_GROUPS = 1 << 2; public static final int FLAG_ALL = FLAG_PUSH | FLAG_SMS | FLAG_GROUPS; } private static final String[] CONTACT_PROJECTION = new String[]{NAME_COLUMN, NUMBER_COLUMN, NUMBER_TYPE_COLUMN, LABEL_COLUMN, CONTACT_TYPE_COLUMN}; private static final int RECENT_CONVERSATION_MAX = 25; private final String filter; private final int mode; private final boolean recents; public ContactsCursorLoader(@NonNull Context context, int mode, String filter, boolean recents) { super(context); this.filter = filter; this.mode = mode; this.recents = recents; } @Override public Cursor loadInBackground() { List cursorList = TextUtils.isEmpty(filter) ? getUnfilteredResults() : getFilteredResults(); if (cursorList.size() > 0) { return new MergeCursor(cursorList.toArray(new Cursor[0])); } return null; } private List getUnfilteredResults() { ArrayList cursorList = new ArrayList<>(); if (recents) { Cursor recentConversations = getRecentConversationsCursor(); if (recentConversations.getCount() > 0) { cursorList.add(getRecentsHeaderCursor()); cursorList.add(recentConversations); cursorList.add(getContactsHeaderCursor()); } } cursorList.addAll(getContactsCursors()); return cursorList; } private List getFilteredResults() { ArrayList cursorList = new ArrayList<>(); if (groupsEnabled(mode)) { Cursor groups = getGroupsCursor(); if (groups.getCount() > 0) { List contacts = getContactsCursors(); if (!isCursorListEmpty(contacts)) { cursorList.add(getContactsHeaderCursor()); cursorList.addAll(contacts); cursorList.add(getGroupsHeaderCursor()); } cursorList.add(groups); } else { cursorList.addAll(getContactsCursors()); } } else { cursorList.addAll(getContactsCursors()); } return cursorList; } private Cursor getRecentsHeaderCursor() { MatrixCursor recentsHeader = new MatrixCursor(CONTACT_PROJECTION); /* recentsHeader.addRow(new Object[]{ getContext().getString(R.string.ContactsCursorLoader_recent_chats), "", ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE, "", ContactsDatabase.DIVIDER_TYPE }); */ return recentsHeader; } private Cursor getContactsHeaderCursor() { MatrixCursor contactsHeader = new MatrixCursor(CONTACT_PROJECTION, 1); /* contactsHeader.addRow(new Object[] { getContext().getString(R.string.ContactsCursorLoader_contacts), "", ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE, "", ContactsDatabase.DIVIDER_TYPE }); */ return contactsHeader; } private Cursor getGroupsHeaderCursor() { MatrixCursor groupHeader = new MatrixCursor(CONTACT_PROJECTION, 1); groupHeader.addRow(new Object[]{ getContext().getString(R.string.ContactsCursorLoader_groups), "", ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE, "", DIVIDER_TYPE }); return groupHeader; } private Cursor getRecentConversationsCursor() { ThreadDatabase threadDatabase = DatabaseComponent.get(getContext()).threadDatabase(); MatrixCursor recentConversations = new MatrixCursor(CONTACT_PROJECTION, RECENT_CONVERSATION_MAX); try (Cursor rawConversations = threadDatabase.getRecentConversationList(RECENT_CONVERSATION_MAX)) { ThreadDatabase.Reader reader = threadDatabase.readerFor(rawConversations); ThreadRecord threadRecord; while ((threadRecord = reader.getNext()) != null) { recentConversations.addRow(new Object[] { threadRecord.getRecipient().toShortString(), threadRecord.getRecipient().getAddress().serialize(), ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE, "", RECENT_TYPE }); } } return recentConversations; } private List getContactsCursors() { return new ArrayList<>(2); /* if (!Permissions.hasAny(getContext(), Manifest.permission.READ_CONTACTS, Manifest.permission.WRITE_CONTACTS)) { return cursorList; } if (pushEnabled(mode)) { cursorList.add(contactsDatabase.queryTextSecureContacts(filter)); } if (pushEnabled(mode) && smsEnabled(mode)) { cursorList.add(contactsDatabase.querySystemContacts(filter)); } else if (smsEnabled(mode)) { cursorList.add(filterNonPushContacts(contactsDatabase.querySystemContacts(filter))); } return cursorList; */ } private Cursor getGroupsCursor() { MatrixCursor groupContacts = new MatrixCursor(CONTACT_PROJECTION); try (GroupDatabase.Reader reader = DatabaseComponent.get(getContext()).groupDatabase().getGroupsFilteredByTitle(filter)) { GroupRecord groupRecord; while ((groupRecord = reader.getNext()) != null) { groupContacts.addRow(new Object[] { groupRecord.getTitle(), groupRecord.getEncodedId(), ContactsContract.CommonDataKinds.Phone.TYPE_CUSTOM, "", NORMAL_TYPE }); } } return groupContacts; } private Cursor getNewNumberCursor() { MatrixCursor newNumberCursor = new MatrixCursor(CONTACT_PROJECTION, 1); newNumberCursor.addRow(new Object[] { getContext().getString(R.string.contact_selection_list__unknown_contact), filter, ContactsContract.CommonDataKinds.Phone.TYPE_CUSTOM, "\u21e2", NEW_TYPE }); return newNumberCursor; } private static boolean isCursorListEmpty(List list) { int sum = 0; for (Cursor cursor : list) { sum += cursor.getCount(); } return sum == 0; } private static boolean pushEnabled(int mode) { return (mode & DisplayMode.FLAG_PUSH) > 0; } private static boolean smsEnabled(int mode) { return (mode & DisplayMode.FLAG_SMS) > 0; } private static boolean groupsEnabled(int mode) { return (mode & DisplayMode.FLAG_GROUPS) > 0; } }