mirror of
https://github.com/oxen-io/session-android.git
synced 2024-11-27 12:05:22 +00:00
Remove DirectoryRefresh.
This was causing crashes on some devices and it's used for accessing contacts on a device which we don't need.
This commit is contained in:
parent
9228c7399a
commit
e484a95fcb
@ -615,11 +615,6 @@
|
||||
<action android:name="network.loki.securesms.RESTART" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
<receiver android:name="org.thoughtcrime.securesms.service.DirectoryRefreshListener">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
<receiver android:name="org.thoughtcrime.securesms.service.RotateSignedPreKeyListener">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
|
@ -75,7 +75,6 @@ import org.thoughtcrime.securesms.profiles.AvatarHelper;
|
||||
import org.thoughtcrime.securesms.providers.BlobProvider;
|
||||
import org.thoughtcrime.securesms.push.SignalServiceNetworkAccess;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.service.DirectoryRefreshListener;
|
||||
import org.thoughtcrime.securesms.service.ExpiringMessageManager;
|
||||
import org.thoughtcrime.securesms.service.IncomingMessageObserver;
|
||||
import org.thoughtcrime.securesms.service.KeyCachingService;
|
||||
@ -353,7 +352,6 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
||||
|
||||
private void initializePeriodicTasks() {
|
||||
RotateSignedPreKeyListener.schedule(this);
|
||||
DirectoryRefreshListener.schedule(this);
|
||||
LocalBackupListener.schedule(this);
|
||||
RotateSenderCertificateListener.schedule(this);
|
||||
BackgroundPollWorker.schedule(this); // Session
|
||||
|
@ -16,24 +16,17 @@
|
||||
*/
|
||||
package org.thoughtcrime.securesms;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.AsyncTask;
|
||||
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.logging.Log;
|
||||
import org.thoughtcrime.securesms.util.DirectoryHelper;
|
||||
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 java.io.IOException;
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
import network.loki.messenger.R;
|
||||
|
||||
/**
|
||||
@ -109,44 +102,11 @@ public abstract class ContactSelectionActivity extends PassphraseRequiredActionB
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRefresh() {
|
||||
new RefreshDirectoryTask(this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, getApplicationContext());
|
||||
}
|
||||
public void onRefresh() { }
|
||||
|
||||
@Override
|
||||
public void onContactSelected(String number) {}
|
||||
|
||||
@Override
|
||||
public void onContactDeselected(String number) {}
|
||||
|
||||
private static class RefreshDirectoryTask extends AsyncTask<Context, Void, Void> {
|
||||
|
||||
private final WeakReference<ContactSelectionActivity> activity;
|
||||
|
||||
private RefreshDirectoryTask(ContactSelectionActivity activity) {
|
||||
this.activity = new WeakReference<>(activity);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void doInBackground(Context... params) {
|
||||
|
||||
try {
|
||||
DirectoryHelper.refreshDirectory(params[0], true);
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void result) {
|
||||
ContactSelectionActivity activity = this.activity.get();
|
||||
|
||||
if (activity != null && !activity.isFinishing()) {
|
||||
activity.toolbar.clear();
|
||||
activity.contactsFragment.resetQueryFilter();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,6 @@
|
||||
package org.thoughtcrime.securesms;
|
||||
|
||||
|
||||
import android.Manifest;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.database.Cursor;
|
||||
import android.os.AsyncTask;
|
||||
@ -44,14 +43,11 @@ 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.logging.Log;
|
||||
import org.thoughtcrime.securesms.mms.GlideApp;
|
||||
import org.thoughtcrime.securesms.permissions.Permissions;
|
||||
import org.thoughtcrime.securesms.util.DirectoryHelper;
|
||||
import org.thoughtcrime.securesms.util.StickyHeaderDecoration;
|
||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
@ -259,12 +255,6 @@ public class ContactSelectionListFragment extends Fragment
|
||||
|
||||
@Override
|
||||
protected Boolean doInBackground(Void... voids) {
|
||||
try {
|
||||
DirectoryHelper.refreshDirectory(getContext(), false);
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -40,7 +40,6 @@ import org.thoughtcrime.securesms.database.PushDatabase;
|
||||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||
import org.thoughtcrime.securesms.jobs.AttachmentDownloadJob;
|
||||
import org.thoughtcrime.securesms.jobs.CreateSignedPreKeyJob;
|
||||
import org.thoughtcrime.securesms.jobs.DirectoryRefreshJob;
|
||||
import org.thoughtcrime.securesms.jobs.PushDecryptJob;
|
||||
import org.thoughtcrime.securesms.jobs.RefreshAttributesJob;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
@ -250,12 +249,6 @@ public class DatabaseUpgradeActivity extends BaseActivity {
|
||||
scheduleMessagesInPushDatabase(context);;
|
||||
}
|
||||
|
||||
if (params[0] < CONTACTS_ACCOUNT_VERSION) {
|
||||
ApplicationContext.getInstance(getApplicationContext())
|
||||
.getJobManager()
|
||||
.add(new DirectoryRefreshJob(false));
|
||||
}
|
||||
|
||||
if (params[0] < MEDIA_DOWNLOAD_CONTROLS_VERSION) {
|
||||
schedulePendingIncomingParts(context);
|
||||
}
|
||||
@ -264,15 +257,6 @@ public class DatabaseUpgradeActivity extends BaseActivity {
|
||||
ApplicationContext.getInstance(getApplicationContext())
|
||||
.getJobManager()
|
||||
.add(new RefreshAttributesJob());
|
||||
ApplicationContext.getInstance(getApplicationContext())
|
||||
.getJobManager()
|
||||
.add(new DirectoryRefreshJob(false));
|
||||
}
|
||||
|
||||
if (params[0] < PROFILES) {
|
||||
ApplicationContext.getInstance(getApplicationContext())
|
||||
.getJobManager()
|
||||
.add(new DirectoryRefreshJob(false));
|
||||
}
|
||||
|
||||
if (params[0] < SCREENSHOTS) {
|
||||
|
@ -49,7 +49,6 @@ import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.IdentityDatabase;
|
||||
import org.thoughtcrime.securesms.database.NoExternalStorageException;
|
||||
import org.thoughtcrime.securesms.jobs.DirectoryRefreshJob;
|
||||
import org.thoughtcrime.securesms.jobs.RotateCertificateJob;
|
||||
import org.thoughtcrime.securesms.lock.RegistrationLockReminders;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
@ -57,7 +56,6 @@ import org.thoughtcrime.securesms.notifications.NotificationChannels;
|
||||
import org.thoughtcrime.securesms.permissions.Permissions;
|
||||
import org.thoughtcrime.securesms.push.AccountManagerFactory;
|
||||
import org.thoughtcrime.securesms.registration.CaptchaActivity;
|
||||
import org.thoughtcrime.securesms.service.DirectoryRefreshListener;
|
||||
import org.thoughtcrime.securesms.service.RotateSignedPreKeyListener;
|
||||
import org.thoughtcrime.securesms.util.BackupUtil;
|
||||
import org.thoughtcrime.securesms.util.DateUtils;
|
||||
@ -648,10 +646,8 @@ public class RegistrationActivity extends BaseActionBarActivity implements Verif
|
||||
}
|
||||
|
||||
private void handleSuccessfulRegistration() {
|
||||
ApplicationContext.getInstance(RegistrationActivity.this).getJobManager().add(new DirectoryRefreshJob(false));
|
||||
ApplicationContext.getInstance(RegistrationActivity.this).getJobManager().add(new RotateCertificateJob(RegistrationActivity.this));
|
||||
|
||||
DirectoryRefreshListener.schedule(RegistrationActivity.this);
|
||||
RotateSignedPreKeyListener.schedule(RegistrationActivity.this);
|
||||
|
||||
Intent nextIntent = getIntent().getParcelableExtra("next_intent");
|
||||
|
@ -16,31 +16,19 @@
|
||||
*/
|
||||
package org.thoughtcrime.securesms.contacts;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.MergeCursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.provider.ContactsContract.CommonDataKinds.Phone;
|
||||
import android.provider.ContactsContract.Contacts;
|
||||
import android.provider.ContactsContract.PhoneLookup;
|
||||
import android.telephony.PhoneNumberUtils;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import network.loki.messenger.R;
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.GroupDatabase;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import network.loki.messenger.R;
|
||||
|
||||
import static org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord;
|
||||
|
||||
@ -58,91 +46,14 @@ import static org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord;
|
||||
|
||||
public class ContactAccessor {
|
||||
|
||||
public static final String PUSH_COLUMN = "push";
|
||||
|
||||
private static final ContactAccessor instance = new ContactAccessor();
|
||||
|
||||
public static synchronized ContactAccessor getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
public Set<Address> getAllContactsWithNumbers(Context context) {
|
||||
Set<Address> results = new HashSet<>();
|
||||
|
||||
try (Cursor cursor = context.getContentResolver().query(Phone.CONTENT_URI, new String[] {Phone.NUMBER}, null ,null, null)) {
|
||||
while (cursor != null && cursor.moveToNext()) {
|
||||
if (!TextUtils.isEmpty(cursor.getString(0))) {
|
||||
results.add(Address.fromExternal(context, cursor.getString(0)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
public Cursor getAllSystemContacts(Context context) {
|
||||
return context.getContentResolver().query(Phone.CONTENT_URI, new String[] {Phone.NUMBER, Phone.DISPLAY_NAME, Phone.LABEL, Phone.PHOTO_URI, Phone._ID, Phone.LOOKUP_KEY}, null, null, null);
|
||||
}
|
||||
|
||||
public boolean isSystemContact(Context context, String number) {
|
||||
Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(number));
|
||||
String[] projection = new String[]{PhoneLookup.DISPLAY_NAME, PhoneLookup.LOOKUP_KEY,
|
||||
PhoneLookup._ID, PhoneLookup.NUMBER};
|
||||
Cursor cursor = context.getContentResolver().query(uri, projection, null, null, null);
|
||||
|
||||
try {
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
return true;
|
||||
}
|
||||
} finally {
|
||||
if (cursor != null) cursor.close();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public Collection<ContactData> getContactsWithPush(Context context) {
|
||||
final ContentResolver resolver = context.getContentResolver();
|
||||
final String[] inProjection = new String[]{PhoneLookup._ID, PhoneLookup.DISPLAY_NAME};
|
||||
|
||||
final List<Address> registeredAddresses = DatabaseFactory.getRecipientDatabase(context).getRegistered();
|
||||
final Collection<ContactData> lookupData = new ArrayList<>(registeredAddresses.size());
|
||||
|
||||
for (Address registeredAddress : registeredAddresses) {
|
||||
Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(registeredAddress.serialize()));
|
||||
Cursor lookupCursor = resolver.query(uri, inProjection, null, null, null);
|
||||
|
||||
try {
|
||||
if (lookupCursor != null && lookupCursor.moveToFirst()) {
|
||||
final ContactData contactData = new ContactData(lookupCursor.getLong(0), lookupCursor.getString(1));
|
||||
contactData.numbers.add(new NumberData("TextSecure", registeredAddress.serialize()));
|
||||
lookupData.add(contactData);
|
||||
}
|
||||
} finally {
|
||||
if (lookupCursor != null)
|
||||
lookupCursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
return lookupData;
|
||||
}
|
||||
|
||||
public String getNameFromContact(Context context, Uri uri) {
|
||||
Cursor cursor = null;
|
||||
|
||||
try {
|
||||
cursor = context.getContentResolver().query(uri, new String[] {Contacts.DISPLAY_NAME},
|
||||
null, null, null);
|
||||
|
||||
if (cursor != null && cursor.moveToFirst())
|
||||
return cursor.getString(0);
|
||||
|
||||
} finally {
|
||||
if (cursor != null)
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
return null;
|
||||
return "Anonymous";
|
||||
}
|
||||
|
||||
public ContactData getContactData(Context context, Uri uri) {
|
||||
@ -150,47 +61,11 @@ public class ContactAccessor {
|
||||
}
|
||||
|
||||
private ContactData getContactData(Context context, String displayName, long id) {
|
||||
ContactData contactData = new ContactData(id, displayName);
|
||||
Cursor numberCursor = null;
|
||||
|
||||
try {
|
||||
numberCursor = context.getContentResolver().query(Phone.CONTENT_URI, null,
|
||||
Phone.CONTACT_ID + " = ?",
|
||||
new String[] {contactData.id + ""}, null);
|
||||
|
||||
while (numberCursor != null && numberCursor.moveToNext()) {
|
||||
int type = numberCursor.getInt(numberCursor.getColumnIndexOrThrow(Phone.TYPE));
|
||||
String label = numberCursor.getString(numberCursor.getColumnIndexOrThrow(Phone.LABEL));
|
||||
String number = numberCursor.getString(numberCursor.getColumnIndexOrThrow(Phone.NUMBER));
|
||||
String typeLabel = Phone.getTypeLabel(context.getResources(), type, label).toString();
|
||||
|
||||
contactData.numbers.add(new NumberData(typeLabel, number));
|
||||
}
|
||||
} finally {
|
||||
if (numberCursor != null)
|
||||
numberCursor.close();
|
||||
}
|
||||
|
||||
return contactData;
|
||||
return new ContactData(id, displayName);
|
||||
}
|
||||
|
||||
public List<String> getNumbersForThreadSearchFilter(Context context, String constraint) {
|
||||
LinkedList<String> numberList = new LinkedList<>();
|
||||
Cursor cursor = null;
|
||||
|
||||
try {
|
||||
cursor = context.getContentResolver().query(Uri.withAppendedPath(Phone.CONTENT_FILTER_URI,
|
||||
Uri.encode(constraint)),
|
||||
null, null, null, null);
|
||||
|
||||
while (cursor != null && cursor.moveToNext()) {
|
||||
numberList.add(cursor.getString(cursor.getColumnIndexOrThrow(Phone.NUMBER)));
|
||||
}
|
||||
|
||||
} finally {
|
||||
if (cursor != null)
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
GroupDatabase.Reader reader = null;
|
||||
GroupRecord record;
|
||||
@ -216,7 +91,7 @@ public class ContactAccessor {
|
||||
}
|
||||
|
||||
public CharSequence phoneTypeToString(Context mContext, int type, CharSequence label) {
|
||||
return Phone.getTypeLabel(mContext.getResources(), type, label);
|
||||
return label;
|
||||
}
|
||||
|
||||
public static class NumberData implements Parcelable {
|
||||
@ -294,82 +169,4 @@ public class ContactAccessor {
|
||||
}
|
||||
}
|
||||
|
||||
/***
|
||||
* If the code below looks shitty to you, that's because it was taken
|
||||
* directly from the Android source, where shitty code is all you get.
|
||||
*/
|
||||
|
||||
public Cursor getCursorForRecipientFilter(CharSequence constraint,
|
||||
ContentResolver mContentResolver)
|
||||
{
|
||||
final String SORT_ORDER = Contacts.TIMES_CONTACTED + " DESC," +
|
||||
Contacts.DISPLAY_NAME + "," +
|
||||
Contacts.Data.IS_SUPER_PRIMARY + " DESC," +
|
||||
Phone.TYPE;
|
||||
|
||||
final String[] PROJECTION_PHONE = {
|
||||
Phone._ID, // 0
|
||||
Phone.CONTACT_ID, // 1
|
||||
Phone.TYPE, // 2
|
||||
Phone.NUMBER, // 3
|
||||
Phone.LABEL, // 4
|
||||
Phone.DISPLAY_NAME, // 5
|
||||
};
|
||||
|
||||
String phone = "";
|
||||
String cons = null;
|
||||
|
||||
if (constraint != null) {
|
||||
cons = constraint.toString();
|
||||
|
||||
if (RecipientsAdapter.usefulAsDigits(cons)) {
|
||||
phone = PhoneNumberUtils.convertKeypadLettersToDigits(cons);
|
||||
if (phone.equals(cons) && !PhoneNumberUtils.isWellFormedSmsAddress(phone)) {
|
||||
phone = "";
|
||||
} else {
|
||||
phone = phone.trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
Uri uri = Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, Uri.encode(cons));
|
||||
String selection = String.format("%s=%s OR %s=%s OR %s=%s",
|
||||
Phone.TYPE,
|
||||
Phone.TYPE_MOBILE,
|
||||
Phone.TYPE,
|
||||
Phone.TYPE_WORK_MOBILE,
|
||||
Phone.TYPE,
|
||||
Phone.TYPE_MMS);
|
||||
|
||||
Cursor phoneCursor = mContentResolver.query(uri,
|
||||
PROJECTION_PHONE,
|
||||
null,
|
||||
null,
|
||||
SORT_ORDER);
|
||||
|
||||
if (phone.length() > 0) {
|
||||
ArrayList result = new ArrayList();
|
||||
result.add(Integer.valueOf(-1)); // ID
|
||||
result.add(Long.valueOf(-1)); // CONTACT_ID
|
||||
result.add(Integer.valueOf(Phone.TYPE_CUSTOM)); // TYPE
|
||||
result.add(phone); // NUMBER
|
||||
|
||||
/*
|
||||
* The "\u00A0" keeps Phone.getDisplayLabel() from deciding
|
||||
* to display the default label ("Home") next to the transformation
|
||||
* of the letters into numbers.
|
||||
*/
|
||||
result.add("\u00A0"); // LABEL
|
||||
result.add(cons); // NAME
|
||||
|
||||
ArrayList<ArrayList> wrap = new ArrayList<ArrayList>();
|
||||
wrap.add(result);
|
||||
|
||||
ArrayListCursor translated = new ArrayListCursor(PROJECTION_PHONE, wrap);
|
||||
|
||||
return new MergeCursor(new Cursor[] { translated, phoneCursor });
|
||||
} else {
|
||||
return phoneCursor;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -17,9 +17,6 @@
|
||||
|
||||
package org.thoughtcrime.securesms.contacts;
|
||||
|
||||
import network.loki.messenger.R;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientsFormatter;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
@ -31,6 +28,10 @@ import android.view.View;
|
||||
import android.widget.ResourceCursorAdapter;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.thoughtcrime.securesms.recipients.RecipientsFormatter;
|
||||
|
||||
import network.loki.messenger.R;
|
||||
|
||||
/**
|
||||
* This adapter is used to filter contacts on both name and number.
|
||||
*/
|
||||
@ -118,7 +119,7 @@ public class RecipientsAdapter extends ResourceCursorAdapter {
|
||||
|
||||
@Override
|
||||
public Cursor runQueryOnBackgroundThread(CharSequence constraint) {
|
||||
return mContactAccessor.getCursorForRecipientFilter( constraint, mContentResolver );
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -20,11 +20,8 @@ import android.widget.TextView;
|
||||
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
||||
|
||||
import org.thoughtcrime.securesms.ApplicationContext;
|
||||
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity;
|
||||
import network.loki.messenger.R;
|
||||
import org.thoughtcrime.securesms.database.RecipientDatabase;
|
||||
import org.thoughtcrime.securesms.jobs.DirectoryRefreshJob;
|
||||
import org.thoughtcrime.securesms.mms.GlideApp;
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
@ -41,7 +38,9 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.*;
|
||||
import network.loki.messenger.R;
|
||||
|
||||
import static org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri;
|
||||
|
||||
public class SharedContactDetailsActivity extends PassphraseRequiredActionBarActivity implements RecipientModifiedListener {
|
||||
|
||||
@ -246,14 +245,4 @@ public class SharedContactDetailsActivity extends PassphraseRequiredActionBarAct
|
||||
engageContainerView.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
|
||||
if (requestCode == CODE_ADD_EDIT_CONTACT && contact != null) {
|
||||
ApplicationContext.getInstance(getApplicationContext())
|
||||
.getJobManager()
|
||||
.add(new DirectoryRefreshJob(false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -211,7 +211,6 @@ import org.thoughtcrime.securesms.util.BitmapUtil;
|
||||
import org.thoughtcrime.securesms.util.CommunicationActions;
|
||||
import org.thoughtcrime.securesms.util.DateUtils;
|
||||
import org.thoughtcrime.securesms.util.Dialogs;
|
||||
import org.thoughtcrime.securesms.util.DirectoryHelper;
|
||||
import org.thoughtcrime.securesms.util.DynamicLanguage;
|
||||
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme;
|
||||
import org.thoughtcrime.securesms.util.ExpirationUtil;
|
||||
@ -348,8 +347,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
private int distributionType;
|
||||
private boolean archived;
|
||||
private boolean isSecureText;
|
||||
private boolean isDefaultSms = true;
|
||||
private boolean isMmsEnabled = true;
|
||||
private boolean isDefaultSms = false;
|
||||
private boolean isMmsEnabled = false;
|
||||
private boolean isSecurityInitialized = false;
|
||||
private int expandedKeyboardHeight = 0;
|
||||
private int collapsedKeyboardHeight = Integer.MAX_VALUE;
|
||||
@ -1478,41 +1477,11 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
new AsyncTask<Recipient, Void, boolean[]>() {
|
||||
@Override
|
||||
protected boolean[] doInBackground(Recipient... params) {
|
||||
Context context = ConversationActivity.this;
|
||||
Recipient recipient = params[0];
|
||||
Log.i(TAG, "Resolving registered state...");
|
||||
RegisteredState registeredState;
|
||||
|
||||
if (recipient.isPushGroupRecipient()) {
|
||||
Log.i(TAG, "Push group recipient...");
|
||||
registeredState = RegisteredState.REGISTERED;
|
||||
} else if (recipient.isResolving()) {
|
||||
Log.i(TAG, "Talking to DB directly.");
|
||||
registeredState = DatabaseFactory.getRecipientDatabase(ConversationActivity.this).isRegistered(recipient.getAddress());
|
||||
} else {
|
||||
Log.i(TAG, "Checking through resolved recipient");
|
||||
registeredState = recipient.resolve().getRegistered();
|
||||
}
|
||||
|
||||
// Loki - Override the flag below
|
||||
registeredState = RegisteredState.REGISTERED;
|
||||
|
||||
Log.i(TAG, "Resolved registered state: " + registeredState);
|
||||
// Loki - Override the flag below
|
||||
boolean signalEnabled = true; // TextSecurePreferences.isPushRegistered(context);
|
||||
|
||||
if (registeredState == RegisteredState.UNKNOWN) {
|
||||
try {
|
||||
Log.i(TAG, "Refreshing directory for user: " + recipient.getAddress().serialize());
|
||||
registeredState = DirectoryHelper.refreshDirectoryFor(context, recipient);
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
}
|
||||
|
||||
Log.i(TAG, "Returning registered state...");
|
||||
return new boolean[] {registeredState == RegisteredState.REGISTERED && signalEnabled,
|
||||
Util.isDefaultSmsProvider(context)};
|
||||
return new boolean[] { signalEnabled, false};
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -77,7 +77,6 @@ import org.thoughtcrime.securesms.database.loaders.ConversationLoader;
|
||||
import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord;
|
||||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||
import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
|
||||
import org.thoughtcrime.securesms.jobs.DirectoryRefreshJob;
|
||||
import org.thoughtcrime.securesms.linkpreview.LinkPreview;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.loki.redesign.views.FriendRequestViewDelegate;
|
||||
@ -1076,17 +1075,6 @@ public class ConversationFragment extends Fragment
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
|
||||
if (requestCode == CODE_ADD_EDIT_CONTACT && getContext() != null) {
|
||||
ApplicationContext.getInstance(getContext().getApplicationContext())
|
||||
.getJobManager()
|
||||
.add(new DirectoryRefreshJob(false));
|
||||
}
|
||||
}
|
||||
|
||||
private class ActionModeCallback implements ActionMode.Callback {
|
||||
|
||||
private int statusBarColor;
|
||||
|
@ -8,7 +8,6 @@ import org.thoughtcrime.securesms.jobs.AttachmentUploadJob;
|
||||
import org.thoughtcrime.securesms.jobs.AvatarDownloadJob;
|
||||
import org.thoughtcrime.securesms.jobs.CleanPreKeysJob;
|
||||
import org.thoughtcrime.securesms.jobs.CreateSignedPreKeyJob;
|
||||
import org.thoughtcrime.securesms.jobs.DirectoryRefreshJob;
|
||||
import org.thoughtcrime.securesms.jobs.LocalBackupJob;
|
||||
import org.thoughtcrime.securesms.jobs.MmsDownloadJob;
|
||||
import org.thoughtcrime.securesms.jobs.MmsReceiveJob;
|
||||
@ -57,7 +56,6 @@ public class WorkManagerFactoryMappings {
|
||||
put(AvatarDownloadJob.class.getName(), AvatarDownloadJob.KEY);
|
||||
put(CleanPreKeysJob.class.getName(), CleanPreKeysJob.KEY);
|
||||
put(CreateSignedPreKeyJob.class.getName(), CreateSignedPreKeyJob.KEY);
|
||||
put(DirectoryRefreshJob.class.getName(), DirectoryRefreshJob.KEY);
|
||||
put(LocalBackupJob.class.getName(), LocalBackupJob.KEY);
|
||||
put(MmsDownloadJob.class.getName(), MmsDownloadJob.KEY);
|
||||
put(MmsReceiveJob.class.getName(), MmsReceiveJob.KEY);
|
||||
|
@ -1,102 +0,0 @@
|
||||
package org.thoughtcrime.securesms.jobs;
|
||||
|
||||
import android.app.Application;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.jobmanager.Data;
|
||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
||||
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class DirectoryRefreshJob extends BaseJob {
|
||||
|
||||
public static final String KEY = "DirectoryRefreshJob";
|
||||
|
||||
private static final String TAG = DirectoryRefreshJob.class.getSimpleName();
|
||||
|
||||
private static final String KEY_ADDRESS = "address";
|
||||
private static final String KEY_NOTIFY_OF_NEW_USERS = "notify_of_new_users";
|
||||
|
||||
@Nullable private Recipient recipient;
|
||||
private boolean notifyOfNewUsers;
|
||||
|
||||
public DirectoryRefreshJob(boolean notifyOfNewUsers) {
|
||||
this(null, notifyOfNewUsers);
|
||||
}
|
||||
|
||||
public DirectoryRefreshJob(@Nullable Recipient recipient,
|
||||
boolean notifyOfNewUsers)
|
||||
{
|
||||
this(new Job.Parameters.Builder()
|
||||
.setQueue("DirectoryRefreshJob")
|
||||
.addConstraint(NetworkConstraint.KEY)
|
||||
.setMaxAttempts(10)
|
||||
.build(),
|
||||
recipient,
|
||||
notifyOfNewUsers);
|
||||
}
|
||||
|
||||
private DirectoryRefreshJob(@NonNull Job.Parameters parameters, @Nullable Recipient recipient, boolean notifyOfNewUsers) {
|
||||
super(parameters);
|
||||
|
||||
this.recipient = recipient;
|
||||
this.notifyOfNewUsers = notifyOfNewUsers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull Data serialize() {
|
||||
return new Data.Builder().putString(KEY_ADDRESS, recipient != null ? recipient.getAddress().serialize() : null)
|
||||
.putBoolean(KEY_NOTIFY_OF_NEW_USERS, notifyOfNewUsers)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull String getFactoryKey() {
|
||||
return KEY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRun() throws IOException {
|
||||
Log.i(TAG, "DirectoryRefreshJob.onRun()");
|
||||
|
||||
// if (recipient == null) {
|
||||
// DirectoryHelper.refreshDirectory(context, notifyOfNewUsers);
|
||||
// } else {
|
||||
// DirectoryHelper.refreshDirectoryFor(context, recipient);
|
||||
// }
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onShouldRetry(@NonNull Exception exception) {
|
||||
if (exception instanceof PushNetworkException) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCanceled() {}
|
||||
|
||||
public static final class Factory implements Job.Factory<DirectoryRefreshJob> {
|
||||
|
||||
private final Application application;
|
||||
|
||||
public Factory(@NonNull Application application) {
|
||||
this.application = application;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull DirectoryRefreshJob create(@NonNull Parameters parameters, @NonNull Data data) {
|
||||
String serializedAddress = data.getString(KEY_ADDRESS);
|
||||
Address address = serializedAddress != null ? Address.fromSerialized(serializedAddress) : null;
|
||||
Recipient recipient = address != null ? Recipient.from(application, address, true) : null;
|
||||
boolean notifyOfNewUsers = data.getBoolean(KEY_NOTIFY_OF_NEW_USERS);
|
||||
|
||||
return new DirectoryRefreshJob(parameters, recipient, notifyOfNewUsers);
|
||||
}
|
||||
}
|
||||
}
|
@ -31,7 +31,6 @@ public final class JobManagerFactories {
|
||||
put(AvatarDownloadJob.KEY, new AvatarDownloadJob.Factory());
|
||||
put(CleanPreKeysJob.KEY, new CleanPreKeysJob.Factory());
|
||||
put(CreateSignedPreKeyJob.KEY, new CreateSignedPreKeyJob.Factory());
|
||||
put(DirectoryRefreshJob.KEY, new DirectoryRefreshJob.Factory(application));
|
||||
put(LocalBackupJob.KEY, new LocalBackupJob.Factory());
|
||||
put(MmsDownloadJob.KEY, new MmsDownloadJob.Factory());
|
||||
put(MmsReceiveJob.KEY, new MmsReceiveJob.Factory());
|
||||
|
@ -224,7 +224,6 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
|
||||
if (messageId >= 0) {
|
||||
database.markAsPendingInsecureSmsFallback(messageId);
|
||||
notifyMediaMessageDeliveryFailed(context, messageId);
|
||||
ApplicationContext.getInstance(context).getJobManager().add(new DirectoryRefreshJob(false));
|
||||
}
|
||||
} catch (UntrustedIdentityException uie) {
|
||||
warn(TAG, "Failure", uie);
|
||||
|
@ -3,7 +3,6 @@ package org.thoughtcrime.securesms.jobs;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import org.thoughtcrime.securesms.ApplicationContext;
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId;
|
||||
@ -32,7 +31,6 @@ public abstract class PushReceivedJob extends BaseJob {
|
||||
|
||||
if (!isActiveNumber(recipient)) {
|
||||
DatabaseFactory.getRecipientDatabase(context).setRegistered(recipient, RecipientDatabase.RegisteredState.REGISTERED);
|
||||
ApplicationContext.getInstance(context).getJobManager().add(new DirectoryRefreshJob(recipient, false));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -165,7 +165,6 @@ public class PushTextSendJob extends PushSendJob implements InjectableType {
|
||||
if (messageId >= 0) {
|
||||
database.markAsPendingInsecureSmsFallback(record.getId());
|
||||
MessageNotifier.notifyMessageDeliveryFailed(context, record.getRecipient(), record.getThreadId());
|
||||
ApplicationContext.getInstance(context).getJobManager().add(new DirectoryRefreshJob(false));
|
||||
}
|
||||
} catch (UntrustedIdentityException e) {
|
||||
warn(TAG, "Failure", e);
|
||||
|
@ -12,7 +12,9 @@ import android.view.View
|
||||
import android.widget.Toast
|
||||
import kotlinx.android.synthetic.main.activity_linked_devices.*
|
||||
import network.loki.messenger.R
|
||||
import nl.komponents.kovenant.Kovenant
|
||||
import nl.komponents.kovenant.functional.bind
|
||||
import nl.komponents.kovenant.task
|
||||
import nl.komponents.kovenant.ui.failUi
|
||||
import nl.komponents.kovenant.ui.successUi
|
||||
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
||||
@ -24,8 +26,8 @@ import org.thoughtcrime.securesms.loki.signAndSendDeviceLinkMessage
|
||||
import org.thoughtcrime.securesms.sms.MessageSender
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||
import org.whispersystems.signalservice.loki.api.DeviceLink
|
||||
import org.whispersystems.signalservice.loki.api.LokiAPI
|
||||
import org.whispersystems.signalservice.loki.api.LokiFileServerAPI
|
||||
import org.whispersystems.signalservice.loki.utilities.createContext
|
||||
import java.util.*
|
||||
import kotlin.concurrent.schedule
|
||||
|
||||
@ -145,7 +147,11 @@ class LinkedDevicesActivity : PassphraseRequiredActionBarActivity, LoaderManager
|
||||
}
|
||||
|
||||
override fun onDeviceLinkRequestAuthorized(deviceLink: DeviceLink) {
|
||||
LokiFileServerAPI.shared.addDeviceLink(deviceLink).bind(LokiAPI.sharedWorkContext) {
|
||||
val context = Kovenant.createContext("Multi-device")
|
||||
LokiFileServerAPI.shared.addDeviceLink(deviceLink)
|
||||
task(context) {
|
||||
1
|
||||
}.bind {
|
||||
signAndSendDeviceLinkMessage(this, deviceLink)
|
||||
}.successUi {
|
||||
LoaderManager.getInstance(this).restartLoader(0, null, this)
|
||||
|
@ -1,39 +0,0 @@
|
||||
package org.thoughtcrime.securesms.service;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
import org.thoughtcrime.securesms.ApplicationContext;
|
||||
import org.thoughtcrime.securesms.jobs.DirectoryRefreshJob;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class DirectoryRefreshListener extends PersistentAlarmManagerListener {
|
||||
|
||||
private static final long INTERVAL = TimeUnit.HOURS.toMillis(12);
|
||||
|
||||
@Override
|
||||
protected long getNextScheduledExecutionTime(Context context) {
|
||||
return TextSecurePreferences.getDirectoryRefreshTime(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected long onAlarm(Context context, long scheduledTime) {
|
||||
if (scheduledTime != 0 && TextSecurePreferences.isPushRegistered(context)) {
|
||||
ApplicationContext.getInstance(context)
|
||||
.getJobManager()
|
||||
.add(new DirectoryRefreshJob(true));
|
||||
}
|
||||
|
||||
long newTime = System.currentTimeMillis() + INTERVAL;
|
||||
TextSecurePreferences.setDirectoryRefreshTime(context, newTime);
|
||||
|
||||
return newTime;
|
||||
}
|
||||
|
||||
public static void schedule(Context context) {
|
||||
new DirectoryRefreshListener().onReceive(context, new Intent());
|
||||
}
|
||||
}
|
@ -1,520 +0,0 @@
|
||||
package org.thoughtcrime.securesms.util;
|
||||
|
||||
import android.Manifest;
|
||||
import android.accounts.Account;
|
||||
import android.accounts.AccountManager;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.OperationApplicationException;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.RemoteException;
|
||||
import android.provider.ContactsContract;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.text.TextUtils;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
|
||||
import com.annimon.stream.Collectors;
|
||||
import com.annimon.stream.Stream;
|
||||
|
||||
import org.thoughtcrime.securesms.ApplicationContext;
|
||||
import network.loki.messenger.BuildConfig;
|
||||
import network.loki.messenger.R;
|
||||
import org.thoughtcrime.securesms.contacts.ContactAccessor;
|
||||
import org.thoughtcrime.securesms.crypto.SessionUtil;
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.MessagingDatabase.InsertResult;
|
||||
import org.thoughtcrime.securesms.database.RecipientDatabase;
|
||||
import org.thoughtcrime.securesms.database.RecipientDatabase.RegisteredState;
|
||||
import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob;
|
||||
import org.thoughtcrime.securesms.notifications.MessageNotifier;
|
||||
import org.thoughtcrime.securesms.notifications.NotificationChannels;
|
||||
import org.thoughtcrime.securesms.permissions.Permissions;
|
||||
import org.thoughtcrime.securesms.push.AccountManagerFactory;
|
||||
import org.thoughtcrime.securesms.push.IasTrustStore;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.sms.IncomingJoinedMessage;
|
||||
import org.thoughtcrime.securesms.util.concurrent.SignalExecutors;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
|
||||
import org.whispersystems.signalservice.api.push.ContactTokenDetails;
|
||||
import org.whispersystems.signalservice.api.push.TrustStore;
|
||||
import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException;
|
||||
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
|
||||
import org.whispersystems.signalservice.internal.contacts.crypto.Quote;
|
||||
import org.whispersystems.signalservice.internal.contacts.crypto.UnauthenticatedQuoteException;
|
||||
import org.whispersystems.signalservice.internal.contacts.crypto.UnauthenticatedResponseException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.KeyStore;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SignatureException;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
public class DirectoryHelper {
|
||||
|
||||
private static final String TAG = DirectoryHelper.class.getSimpleName();
|
||||
|
||||
private static final int CONTACT_DISCOVERY_BATCH_SIZE = 2048;
|
||||
|
||||
public static void refreshDirectory(@NonNull Context context, boolean notifyOfNewUsers)
|
||||
throws IOException
|
||||
{
|
||||
if (TextUtils.isEmpty(TextSecurePreferences.getLocalNumber(context))) return;
|
||||
if (!Permissions.hasAll(context, Manifest.permission.WRITE_CONTACTS)) return;
|
||||
|
||||
List<Address> newlyActiveUsers = refreshDirectory(context, AccountManagerFactory.createManager(context));
|
||||
|
||||
if (TextSecurePreferences.isMultiDevice(context)) {
|
||||
ApplicationContext.getInstance(context)
|
||||
.getJobManager()
|
||||
.add(new MultiDeviceContactUpdateJob(context));
|
||||
}
|
||||
|
||||
if (notifyOfNewUsers) notifyNewUsers(context, newlyActiveUsers);
|
||||
}
|
||||
|
||||
@SuppressLint("CheckResult")
|
||||
private static @NonNull List<Address> refreshDirectory(@NonNull Context context, @NonNull SignalServiceAccountManager accountManager)
|
||||
throws IOException
|
||||
{
|
||||
if (TextUtils.isEmpty(TextSecurePreferences.getLocalNumber(context))) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
if (!Permissions.hasAll(context, Manifest.permission.WRITE_CONTACTS)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
RecipientDatabase recipientDatabase = DatabaseFactory.getRecipientDatabase(context);
|
||||
Stream<String> eligibleRecipientDatabaseContactNumbers = Stream.of(recipientDatabase.getAllAddresses()).filter(Address::isPhone).map(Address::toPhoneString);
|
||||
Stream<String> eligibleSystemDatabaseContactNumbers = Stream.of(ContactAccessor.getInstance().getAllContactsWithNumbers(context)).map(Address::serialize);
|
||||
Set<String> eligibleContactNumbers = Stream.concat(eligibleRecipientDatabaseContactNumbers, eligibleSystemDatabaseContactNumbers).collect(Collectors.toSet());
|
||||
|
||||
Future<DirectoryResult> legacyRequest = getLegacyDirectoryResult(context, accountManager, recipientDatabase, eligibleContactNumbers);
|
||||
List<Future<Set<String>>> contactServiceRequest = getContactServiceDirectoryResult(context, accountManager, eligibleContactNumbers);
|
||||
|
||||
try {
|
||||
DirectoryResult legacyResult = legacyRequest.get();
|
||||
Optional<Set<String>> contactServiceResult = executeAndMergeContactDiscoveryRequests(accountManager, contactServiceRequest);
|
||||
|
||||
if (!contactServiceResult.isPresent()) {
|
||||
Log.i(TAG, "[Batch] New contact discovery service failed, so we're skipping the comparison.");
|
||||
return legacyResult.getNewlyActiveAddresses();
|
||||
}
|
||||
|
||||
if (legacyResult.getNumbers().size() == contactServiceResult.get().size() && legacyResult.getNumbers().containsAll(contactServiceResult.get())) {
|
||||
Log.i(TAG, "[Batch] New contact discovery service request matched existing results.");
|
||||
accountManager.reportContactDiscoveryServiceMatch();
|
||||
} else {
|
||||
Log.w(TAG, "[Batch] New contact discovery service request did NOT match existing results.");
|
||||
accountManager.reportContactDiscoveryServiceMismatch();
|
||||
}
|
||||
|
||||
return legacyResult.getNewlyActiveAddresses();
|
||||
|
||||
} catch (InterruptedException e) {
|
||||
throw new IOException("[Batch] Operation was interrupted.", e);
|
||||
} catch (ExecutionException e) {
|
||||
if (e.getCause() instanceof IOException) {
|
||||
throw (IOException) e.getCause();
|
||||
} else {
|
||||
Log.e(TAG, "[Batch] Experienced an unexpected exception.", e);
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static RegisteredState refreshDirectoryFor(@NonNull Context context,
|
||||
@NonNull Recipient recipient)
|
||||
throws IOException
|
||||
{
|
||||
RecipientDatabase recipientDatabase = DatabaseFactory.getRecipientDatabase(context);
|
||||
SignalServiceAccountManager accountManager = AccountManagerFactory.createManager(context);
|
||||
|
||||
Future<RegisteredState> legacyRequest = getLegacyRegisteredState(context, accountManager, recipientDatabase, recipient);
|
||||
List<Future<Set<String>>> contactServiceRequest = getContactServiceDirectoryResult(context, accountManager, Collections.singleton(recipient.getAddress().serialize()));
|
||||
|
||||
try {
|
||||
RegisteredState legacyState = legacyRequest.get();
|
||||
Optional<Set<String>> contactServiceResult = executeAndMergeContactDiscoveryRequests(accountManager, contactServiceRequest);
|
||||
|
||||
if (!contactServiceResult.isPresent()) {
|
||||
Log.i(TAG, "[Singular] New contact discovery service failed, so we're skipping the comparison.");
|
||||
return legacyState;
|
||||
}
|
||||
|
||||
RegisteredState contactServiceState = contactServiceResult.get().size() == 1 ? RegisteredState.REGISTERED : RegisteredState.NOT_REGISTERED;
|
||||
|
||||
if (legacyState == contactServiceState) {
|
||||
Log.i(TAG, "[Singular] New contact discovery service request matched existing results.");
|
||||
accountManager.reportContactDiscoveryServiceMatch();
|
||||
} else {
|
||||
Log.w(TAG, "[Singular] New contact discovery service request did NOT match existing results.");
|
||||
accountManager.reportContactDiscoveryServiceMismatch();
|
||||
}
|
||||
|
||||
return legacyState;
|
||||
|
||||
} catch (InterruptedException e) {
|
||||
throw new IOException("[Singular] Operation was interrupted.", e);
|
||||
} catch (ExecutionException e) {
|
||||
if (e.getCause() instanceof IOException) {
|
||||
throw (IOException) e.getCause();
|
||||
} else {
|
||||
Log.e(TAG, "[Singular] Experienced an unexpected exception.", e);
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void updateContactsDatabase(@NonNull Context context, @NonNull List<Address> activeAddresses, boolean removeMissing) {
|
||||
Optional<AccountHolder> account = getOrCreateAccount(context);
|
||||
|
||||
if (account.isPresent()) {
|
||||
try {
|
||||
DatabaseFactory.getContactsDatabase(context).removeDeletedRawContacts(account.get().getAccount());
|
||||
DatabaseFactory.getContactsDatabase(context).setRegisteredUsers(account.get().getAccount(), activeAddresses, removeMissing);
|
||||
|
||||
Cursor cursor = ContactAccessor.getInstance().getAllSystemContacts(context);
|
||||
RecipientDatabase.BulkOperationsHandle handle = DatabaseFactory.getRecipientDatabase(context).resetAllSystemContactInfo();
|
||||
|
||||
try {
|
||||
while (cursor != null && cursor.moveToNext()) {
|
||||
String number = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.NUMBER));
|
||||
|
||||
if (!TextUtils.isEmpty(number)) {
|
||||
Address address = Address.fromExternal(context, number);
|
||||
String displayName = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
|
||||
String contactPhotoUri = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.PHOTO_URI));
|
||||
String contactLabel = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.LABEL));
|
||||
Uri contactUri = ContactsContract.Contacts.getLookupUri(cursor.getLong(cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone._ID)),
|
||||
cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.LOOKUP_KEY)));
|
||||
|
||||
handle.setSystemContactInfo(address, displayName, contactPhotoUri, contactLabel, contactUri.toString());
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
handle.finish();
|
||||
}
|
||||
|
||||
if (NotificationChannels.supported()) {
|
||||
try (RecipientDatabase.RecipientReader recipients = DatabaseFactory.getRecipientDatabase(context).getRecipientsWithNotificationChannels()) {
|
||||
Recipient recipient;
|
||||
while ((recipient = recipients.getNext()) != null) {
|
||||
NotificationChannels.updateContactChannelName(context, recipient);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (RemoteException | OperationApplicationException e) {
|
||||
Log.w(TAG, "Failed to update contacts.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void notifyNewUsers(@NonNull Context context,
|
||||
@NonNull List<Address> newUsers)
|
||||
{
|
||||
if (!TextSecurePreferences.isNewContactsNotificationEnabled(context)) return;
|
||||
|
||||
for (Address newUser: newUsers) {
|
||||
if (!SessionUtil.hasSession(context, newUser) && !Util.isOwnNumber(context, newUser)) {
|
||||
IncomingJoinedMessage message = new IncomingJoinedMessage(newUser);
|
||||
Optional<InsertResult> insertResult = DatabaseFactory.getSmsDatabase(context).insertMessageInbox(message);
|
||||
|
||||
if (insertResult.isPresent()) {
|
||||
int hour = Calendar.getInstance().get(Calendar.HOUR_OF_DAY);
|
||||
if (hour >= 9 && hour < 23) {
|
||||
MessageNotifier.updateNotification(context, insertResult.get().getThreadId(), true);
|
||||
} else {
|
||||
MessageNotifier.updateNotification(context, insertResult.get().getThreadId(), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Optional<AccountHolder> getOrCreateAccount(Context context) {
|
||||
AccountManager accountManager = AccountManager.get(context);
|
||||
Account[] accounts = accountManager.getAccountsByType("org.thoughtcrime.securesms");
|
||||
|
||||
Optional<AccountHolder> account;
|
||||
|
||||
if (accounts.length == 0) account = createAccount(context);
|
||||
else account = Optional.of(new AccountHolder(accounts[0], false));
|
||||
|
||||
if (account.isPresent() && !ContentResolver.getSyncAutomatically(account.get().getAccount(), ContactsContract.AUTHORITY)) {
|
||||
ContentResolver.setSyncAutomatically(account.get().getAccount(), ContactsContract.AUTHORITY, true);
|
||||
}
|
||||
|
||||
return account;
|
||||
}
|
||||
|
||||
private static Optional<AccountHolder> createAccount(Context context) {
|
||||
AccountManager accountManager = AccountManager.get(context);
|
||||
Account account = new Account(context.getString(R.string.app_name), "org.thoughtcrime.securesms");
|
||||
|
||||
if (accountManager.addAccountExplicitly(account, null, null)) {
|
||||
Log.i(TAG, "Created new account...");
|
||||
ContentResolver.setIsSyncable(account, ContactsContract.AUTHORITY, 1);
|
||||
return Optional.of(new AccountHolder(account, true));
|
||||
} else {
|
||||
Log.w(TAG, "Failed to create account!");
|
||||
return Optional.absent();
|
||||
}
|
||||
}
|
||||
|
||||
private static Future<DirectoryResult> getLegacyDirectoryResult(@NonNull Context context,
|
||||
@NonNull SignalServiceAccountManager accountManager,
|
||||
@NonNull RecipientDatabase recipientDatabase,
|
||||
@NonNull Set<String> eligibleContactNumbers)
|
||||
{
|
||||
return SignalExecutors.UNBOUNDED.submit(() -> {
|
||||
List<ContactTokenDetails> activeTokens = accountManager.getContacts(eligibleContactNumbers);
|
||||
|
||||
if (activeTokens != null) {
|
||||
List<Address> activeAddresses = new LinkedList<>();
|
||||
List<Address> inactiveAddresses = new LinkedList<>();
|
||||
|
||||
Set<String> inactiveContactNumbers = new HashSet<>(eligibleContactNumbers);
|
||||
|
||||
for (ContactTokenDetails activeToken : activeTokens) {
|
||||
activeAddresses.add(Address.fromSerialized(activeToken.getNumber()));
|
||||
inactiveContactNumbers.remove(activeToken.getNumber());
|
||||
}
|
||||
|
||||
for (String inactiveContactNumber : inactiveContactNumbers) {
|
||||
inactiveAddresses.add(Address.fromSerialized(inactiveContactNumber));
|
||||
}
|
||||
|
||||
Set<Address> currentActiveAddresses = new HashSet<>(recipientDatabase.getRegistered());
|
||||
Set<Address> contactAddresses = new HashSet<>(recipientDatabase.getSystemContacts());
|
||||
List<Address> newlyActiveAddresses = Stream.of(activeAddresses)
|
||||
.filter(address -> !currentActiveAddresses.contains(address))
|
||||
.filter(contactAddresses::contains)
|
||||
.toList();
|
||||
|
||||
recipientDatabase.setRegistered(activeAddresses, inactiveAddresses);
|
||||
updateContactsDatabase(context, activeAddresses, true);
|
||||
|
||||
Set<String> activeContactNumbers = Stream.of(activeAddresses).map(Address::serialize).collect(Collectors.toSet());
|
||||
|
||||
if (TextSecurePreferences.hasSuccessfullyRetrievedDirectory(context)) {
|
||||
return new DirectoryResult(activeContactNumbers, newlyActiveAddresses);
|
||||
} else {
|
||||
TextSecurePreferences.setHasSuccessfullyRetrievedDirectory(context, true);
|
||||
return new DirectoryResult(activeContactNumbers);
|
||||
}
|
||||
}
|
||||
return new DirectoryResult(Collections.emptySet(), Collections.emptyList());
|
||||
});
|
||||
}
|
||||
|
||||
private static Future<RegisteredState> getLegacyRegisteredState(@NonNull Context context,
|
||||
@NonNull SignalServiceAccountManager accountManager,
|
||||
@NonNull RecipientDatabase recipientDatabase,
|
||||
@NonNull Recipient recipient)
|
||||
{
|
||||
return SignalExecutors.UNBOUNDED.submit(() -> {
|
||||
boolean activeUser = recipient.resolve().getRegistered() == RegisteredState.REGISTERED;
|
||||
boolean systemContact = recipient.isSystemContact();
|
||||
String number = recipient.getAddress().serialize();
|
||||
Optional<ContactTokenDetails> details = accountManager.getContact(number);
|
||||
|
||||
if (details.isPresent()) {
|
||||
recipientDatabase.setRegistered(recipient, RegisteredState.REGISTERED);
|
||||
|
||||
if (Permissions.hasAll(context, Manifest.permission.WRITE_CONTACTS)) {
|
||||
updateContactsDatabase(context, Util.asList(recipient.getAddress()), false);
|
||||
}
|
||||
|
||||
if (!activeUser && TextSecurePreferences.isMultiDevice(context)) {
|
||||
ApplicationContext.getInstance(context).getJobManager().add(new MultiDeviceContactUpdateJob(context));
|
||||
}
|
||||
|
||||
if (!activeUser && systemContact && !TextSecurePreferences.getNeedsSqlCipherMigration(context)) {
|
||||
notifyNewUsers(context, Collections.singletonList(recipient.getAddress()));
|
||||
}
|
||||
|
||||
return RegisteredState.REGISTERED;
|
||||
} else {
|
||||
recipientDatabase.setRegistered(recipient, RegisteredState.NOT_REGISTERED);
|
||||
return RegisteredState.NOT_REGISTERED;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static List<Future<Set<String>>> getContactServiceDirectoryResult(@NonNull Context context,
|
||||
@NonNull SignalServiceAccountManager accountManager,
|
||||
@NonNull Set<String> eligibleContactNumbers)
|
||||
{
|
||||
Set<String> sanitizedNumbers = sanitizeNumbers(eligibleContactNumbers);
|
||||
List<Set<String>> batches = splitIntoBatches(sanitizedNumbers, CONTACT_DISCOVERY_BATCH_SIZE);
|
||||
List<Future<Set<String>>> futures = new ArrayList<>(batches.size());
|
||||
KeyStore iasKeyStore = getIasKeyStore(context);
|
||||
|
||||
for (Set<String> batch : batches) {
|
||||
Future<Set<String>> future = SignalExecutors.UNBOUNDED.submit(() -> {
|
||||
return new HashSet<>(accountManager.getRegisteredUsers(iasKeyStore, batch, BuildConfig.MRENCLAVE));
|
||||
});
|
||||
futures.add(future);
|
||||
}
|
||||
return futures;
|
||||
}
|
||||
|
||||
private static Set<String> sanitizeNumbers(@NonNull Set<String> numbers) {
|
||||
return Stream.of(numbers).filter(number -> {
|
||||
try {
|
||||
return number.startsWith("+") && number.length() > 1 && Long.parseLong(number.substring(1)) > 0;
|
||||
} catch (NumberFormatException e) {
|
||||
return false;
|
||||
}
|
||||
}).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
private static List<Set<String>> splitIntoBatches(@NonNull Set<String> numbers, int batchSize) {
|
||||
List<String> numberList = new ArrayList<>(numbers);
|
||||
List<Set<String>> batches = new LinkedList<>();
|
||||
|
||||
for (int i = 0; i < numberList.size(); i += batchSize) {
|
||||
List<String> batch = numberList.subList(i, Math.min(numberList.size(), i + batchSize));
|
||||
batches.add(new HashSet<>(batch));
|
||||
}
|
||||
|
||||
return batches;
|
||||
}
|
||||
|
||||
private static Optional<Set<String>> executeAndMergeContactDiscoveryRequests(@NonNull SignalServiceAccountManager accountManager, @NonNull List<Future<Set<String>>> futures) {
|
||||
Set<String> results = new HashSet<>();
|
||||
try {
|
||||
for (Future<Set<String>> future : futures) {
|
||||
results.addAll(future.get());
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
Log.w(TAG, "Contact discovery batch was interrupted.", e);
|
||||
accountManager.reportContactDiscoveryServiceUnexpectedError(buildErrorReason(e));
|
||||
return Optional.absent();
|
||||
} catch (ExecutionException e) {
|
||||
if (isAttestationError(e.getCause())) {
|
||||
Log.w(TAG, "Failed during attestation.", e);
|
||||
accountManager.reportContactDiscoveryServiceAttestationError(buildErrorReason(e.getCause()));
|
||||
return Optional.absent();
|
||||
} else if (e.getCause() instanceof PushNetworkException) {
|
||||
Log.w(TAG, "Failed due to poor network.", e);
|
||||
return Optional.absent();
|
||||
} else if (e.getCause() instanceof NonSuccessfulResponseCodeException) {
|
||||
Log.w(TAG, "Failed due to non successful response code.", e);
|
||||
return Optional.absent();
|
||||
} else {
|
||||
Log.w(TAG, "Failed for an unknown reason.", e);
|
||||
accountManager.reportContactDiscoveryServiceUnexpectedError(buildErrorReason(e.getCause()));
|
||||
return Optional.absent();
|
||||
}
|
||||
}
|
||||
|
||||
return Optional.of(results);
|
||||
}
|
||||
|
||||
private static boolean isAttestationError(Throwable e) {
|
||||
return e instanceof CertificateException ||
|
||||
e instanceof SignatureException ||
|
||||
e instanceof UnauthenticatedQuoteException ||
|
||||
e instanceof UnauthenticatedResponseException ||
|
||||
e instanceof Quote.InvalidQuoteFormatException;
|
||||
}
|
||||
|
||||
private static KeyStore getIasKeyStore(@NonNull Context context) {
|
||||
try {
|
||||
TrustStore contactTrustStore = new IasTrustStore(context);
|
||||
|
||||
KeyStore keyStore = KeyStore.getInstance("BKS");
|
||||
keyStore.load(contactTrustStore.getKeyStoreInputStream(), contactTrustStore.getKeyStorePassword().toCharArray());
|
||||
|
||||
return keyStore;
|
||||
} catch (KeyStoreException | CertificateException | IOException | NoSuchAlgorithmException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static String buildErrorReason(@Nullable Throwable t) {
|
||||
if (t == null) {
|
||||
return "null";
|
||||
}
|
||||
|
||||
String rawString = android.util.Log.getStackTraceString(t);
|
||||
List<String> lines = Arrays.asList(rawString.split("\\n"));
|
||||
|
||||
String errorString;
|
||||
|
||||
if (lines.size() > 1) {
|
||||
errorString = t.getClass().getName() + "\n" + Util.join(lines.subList(1, lines.size()), "\n");
|
||||
} else {
|
||||
errorString = t.getClass().getName();
|
||||
}
|
||||
|
||||
if (errorString.length() > 1000) {
|
||||
return errorString.substring(0, 1000);
|
||||
} else {
|
||||
return errorString;
|
||||
}
|
||||
}
|
||||
|
||||
private static class DirectoryResult {
|
||||
|
||||
private final Set<String> numbers;
|
||||
private final List<Address> newlyActiveAddresses;
|
||||
|
||||
DirectoryResult(@NonNull Set<String> numbers) {
|
||||
this(numbers, Collections.emptyList());
|
||||
}
|
||||
|
||||
DirectoryResult(@NonNull Set<String> numbers, @NonNull List<Address> newlyActiveAddresses) {
|
||||
this.numbers = numbers;
|
||||
this.newlyActiveAddresses = newlyActiveAddresses;
|
||||
}
|
||||
|
||||
Set<String> getNumbers() {
|
||||
return numbers;
|
||||
}
|
||||
|
||||
List<Address> getNewlyActiveAddresses() {
|
||||
return newlyActiveAddresses;
|
||||
}
|
||||
}
|
||||
|
||||
private static class AccountHolder {
|
||||
|
||||
private final boolean fresh;
|
||||
private final Account account;
|
||||
|
||||
private AccountHolder(Account account, boolean fresh) {
|
||||
this.fresh = fresh;
|
||||
this.account = account;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public boolean isFresh() {
|
||||
return fresh;
|
||||
}
|
||||
|
||||
public Account getAccount() {
|
||||
return account;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user