Optimistically fetch profiles.
This commit is contained in:
parent
f5626f678d
commit
ce940235b0
11 changed files with 169 additions and 29 deletions
|
@ -45,6 +45,7 @@ import org.thoughtcrime.securesms.jobs.FcmRefreshJob;
|
||||||
import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob;
|
import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob;
|
||||||
import org.thoughtcrime.securesms.jobs.PushNotificationReceiveJob;
|
import org.thoughtcrime.securesms.jobs.PushNotificationReceiveJob;
|
||||||
import org.thoughtcrime.securesms.jobs.RefreshPreKeysJob;
|
import org.thoughtcrime.securesms.jobs.RefreshPreKeysJob;
|
||||||
|
import org.thoughtcrime.securesms.jobs.RetrieveProfileJob;
|
||||||
import org.thoughtcrime.securesms.logging.AndroidLogger;
|
import org.thoughtcrime.securesms.logging.AndroidLogger;
|
||||||
import org.thoughtcrime.securesms.logging.CustomSignalProtocolLogger;
|
import org.thoughtcrime.securesms.logging.CustomSignalProtocolLogger;
|
||||||
import org.thoughtcrime.securesms.logging.Log;
|
import org.thoughtcrime.securesms.logging.Log;
|
||||||
|
@ -133,6 +134,7 @@ public class ApplicationContext extends MultiDexApplication implements DefaultLi
|
||||||
NotificationChannels.create(this);
|
NotificationChannels.create(this);
|
||||||
RefreshPreKeysJob.scheduleIfNecessary();
|
RefreshPreKeysJob.scheduleIfNecessary();
|
||||||
StorageSyncHelper.scheduleRoutineSync();
|
StorageSyncHelper.scheduleRoutineSync();
|
||||||
|
RetrieveProfileJob.enqueueRoutineFetchIfNeccessary(this);
|
||||||
RegistrationUtil.markRegistrationPossiblyComplete();
|
RegistrationUtil.markRegistrationPossiblyComplete();
|
||||||
ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
|
ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@ import org.thoughtcrime.securesms.contacts.avatars.ContactColors;
|
||||||
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil;
|
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil;
|
||||||
import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord;
|
import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord;
|
||||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
|
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
|
||||||
|
import org.thoughtcrime.securesms.database.model.ThreadRecord;
|
||||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||||
import org.thoughtcrime.securesms.groups.GroupId;
|
import org.thoughtcrime.securesms.groups.GroupId;
|
||||||
import org.thoughtcrime.securesms.groups.v2.ProfileKeySet;
|
import org.thoughtcrime.securesms.groups.v2.ProfileKeySet;
|
||||||
|
@ -60,6 +61,7 @@ import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
@ -102,6 +104,7 @@ public class RecipientDatabase extends Database {
|
||||||
private static final String PROFILE_KEY_CREDENTIAL = "profile_key_credential";
|
private static final String PROFILE_KEY_CREDENTIAL = "profile_key_credential";
|
||||||
private static final String SIGNAL_PROFILE_AVATAR = "signal_profile_avatar";
|
private static final String SIGNAL_PROFILE_AVATAR = "signal_profile_avatar";
|
||||||
private static final String PROFILE_SHARING = "profile_sharing";
|
private static final String PROFILE_SHARING = "profile_sharing";
|
||||||
|
private static final String LAST_PROFILE_FETCH = "last_profile_fetch";
|
||||||
private static final String UNIDENTIFIED_ACCESS_MODE = "unidentified_access_mode";
|
private static final String UNIDENTIFIED_ACCESS_MODE = "unidentified_access_mode";
|
||||||
private static final String FORCE_SMS_SELECTION = "force_sms_selection";
|
private static final String FORCE_SMS_SELECTION = "force_sms_selection";
|
||||||
private static final String UUID_CAPABILITY = "uuid_supported";
|
private static final String UUID_CAPABILITY = "uuid_supported";
|
||||||
|
@ -123,7 +126,8 @@ public class RecipientDatabase extends Database {
|
||||||
BLOCKED, MESSAGE_RINGTONE, CALL_RINGTONE, MESSAGE_VIBRATE, CALL_VIBRATE, MUTE_UNTIL, COLOR, SEEN_INVITE_REMINDER, DEFAULT_SUBSCRIPTION_ID, MESSAGE_EXPIRATION_TIME, REGISTERED,
|
BLOCKED, MESSAGE_RINGTONE, CALL_RINGTONE, MESSAGE_VIBRATE, CALL_VIBRATE, MUTE_UNTIL, COLOR, SEEN_INVITE_REMINDER, DEFAULT_SUBSCRIPTION_ID, MESSAGE_EXPIRATION_TIME, REGISTERED,
|
||||||
PROFILE_KEY, PROFILE_KEY_CREDENTIAL,
|
PROFILE_KEY, PROFILE_KEY_CREDENTIAL,
|
||||||
SYSTEM_DISPLAY_NAME, SYSTEM_PHOTO_URI, SYSTEM_PHONE_LABEL, SYSTEM_PHONE_TYPE, SYSTEM_CONTACT_URI,
|
SYSTEM_DISPLAY_NAME, SYSTEM_PHOTO_URI, SYSTEM_PHONE_LABEL, SYSTEM_PHONE_TYPE, SYSTEM_CONTACT_URI,
|
||||||
PROFILE_GIVEN_NAME, PROFILE_FAMILY_NAME, SIGNAL_PROFILE_AVATAR, PROFILE_SHARING, NOTIFICATION_CHANNEL,
|
PROFILE_GIVEN_NAME, PROFILE_FAMILY_NAME, SIGNAL_PROFILE_AVATAR, PROFILE_SHARING, LAST_PROFILE_FETCH,
|
||||||
|
NOTIFICATION_CHANNEL,
|
||||||
UNIDENTIFIED_ACCESS_MODE,
|
UNIDENTIFIED_ACCESS_MODE,
|
||||||
FORCE_SMS_SELECTION,
|
FORCE_SMS_SELECTION,
|
||||||
UUID_CAPABILITY, GROUPS_V2_CAPABILITY,
|
UUID_CAPABILITY, GROUPS_V2_CAPABILITY,
|
||||||
|
@ -295,6 +299,7 @@ public class RecipientDatabase extends Database {
|
||||||
PROFILE_JOINED_NAME + " TEXT DEFAULT NULL, " +
|
PROFILE_JOINED_NAME + " TEXT DEFAULT NULL, " +
|
||||||
SIGNAL_PROFILE_AVATAR + " TEXT DEFAULT NULL, " +
|
SIGNAL_PROFILE_AVATAR + " TEXT DEFAULT NULL, " +
|
||||||
PROFILE_SHARING + " INTEGER DEFAULT 0, " +
|
PROFILE_SHARING + " INTEGER DEFAULT 0, " +
|
||||||
|
LAST_PROFILE_FETCH + " INTEGER DEFAULT 0, " +
|
||||||
UNIDENTIFIED_ACCESS_MODE + " INTEGER DEFAULT 0, " +
|
UNIDENTIFIED_ACCESS_MODE + " INTEGER DEFAULT 0, " +
|
||||||
FORCE_SMS_SELECTION + " INTEGER DEFAULT 0, " +
|
FORCE_SMS_SELECTION + " INTEGER DEFAULT 0, " +
|
||||||
UUID_CAPABILITY + " INTEGER DEFAULT " + Recipient.Capability.UNKNOWN.serialize() + ", " +
|
UUID_CAPABILITY + " INTEGER DEFAULT " + Recipient.Capability.UNKNOWN.serialize() + ", " +
|
||||||
|
@ -842,6 +847,7 @@ public class RecipientDatabase extends Database {
|
||||||
String profileFamilyName = cursor.getString(cursor.getColumnIndexOrThrow(PROFILE_FAMILY_NAME));
|
String profileFamilyName = cursor.getString(cursor.getColumnIndexOrThrow(PROFILE_FAMILY_NAME));
|
||||||
String signalProfileAvatar = cursor.getString(cursor.getColumnIndexOrThrow(SIGNAL_PROFILE_AVATAR));
|
String signalProfileAvatar = cursor.getString(cursor.getColumnIndexOrThrow(SIGNAL_PROFILE_AVATAR));
|
||||||
boolean profileSharing = cursor.getInt(cursor.getColumnIndexOrThrow(PROFILE_SHARING)) == 1;
|
boolean profileSharing = cursor.getInt(cursor.getColumnIndexOrThrow(PROFILE_SHARING)) == 1;
|
||||||
|
long lastProfileFetch = cursor.getLong(cursor.getColumnIndexOrThrow(LAST_PROFILE_FETCH));
|
||||||
String notificationChannel = cursor.getString(cursor.getColumnIndexOrThrow(NOTIFICATION_CHANNEL));
|
String notificationChannel = cursor.getString(cursor.getColumnIndexOrThrow(NOTIFICATION_CHANNEL));
|
||||||
int unidentifiedAccessMode = cursor.getInt(cursor.getColumnIndexOrThrow(UNIDENTIFIED_ACCESS_MODE));
|
int unidentifiedAccessMode = cursor.getInt(cursor.getColumnIndexOrThrow(UNIDENTIFIED_ACCESS_MODE));
|
||||||
boolean forceSmsSelection = cursor.getInt(cursor.getColumnIndexOrThrow(FORCE_SMS_SELECTION)) == 1;
|
boolean forceSmsSelection = cursor.getInt(cursor.getColumnIndexOrThrow(FORCE_SMS_SELECTION)) == 1;
|
||||||
|
@ -908,7 +914,7 @@ public class RecipientDatabase extends Database {
|
||||||
systemDisplayName, systemContactPhoto,
|
systemDisplayName, systemContactPhoto,
|
||||||
systemPhoneLabel, systemContactUri,
|
systemPhoneLabel, systemContactUri,
|
||||||
ProfileName.fromParts(profileGivenName, profileFamilyName), signalProfileAvatar,
|
ProfileName.fromParts(profileGivenName, profileFamilyName), signalProfileAvatar,
|
||||||
AvatarHelper.hasAvatar(context, RecipientId.from(id)), profileSharing,
|
AvatarHelper.hasAvatar(context, RecipientId.from(id)), profileSharing, lastProfileFetch,
|
||||||
notificationChannel, UnidentifiedAccessMode.fromMode(unidentifiedAccessMode),
|
notificationChannel, UnidentifiedAccessMode.fromMode(unidentifiedAccessMode),
|
||||||
forceSmsSelection,
|
forceSmsSelection,
|
||||||
Recipient.Capability.deserialize(uuidCapabilityValue),
|
Recipient.Capability.deserialize(uuidCapabilityValue),
|
||||||
|
@ -1587,6 +1593,53 @@ public class RecipientDatabase extends Database {
|
||||||
return recipients;
|
return recipients;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param lastInteractionThreshold Only include contacts that have been interacted with since this time.
|
||||||
|
* @param lastProfileFetchThreshold Only include contacts that haven't their profile fetched after this time.
|
||||||
|
* @param limit Only return at most this many contact.
|
||||||
|
*/
|
||||||
|
public List<RecipientId> getRecipientsForRoutineProfileFetch(long lastInteractionThreshold, long lastProfileFetchThreshold, int limit) {
|
||||||
|
ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(context);
|
||||||
|
Set<Recipient> recipientsWithinInteractionThreshold = new LinkedHashSet<>();
|
||||||
|
|
||||||
|
try (ThreadDatabase.Reader reader = threadDatabase.readerFor(threadDatabase.getRecentPushConversationList(-1, false))) {
|
||||||
|
ThreadRecord record;
|
||||||
|
|
||||||
|
while ((record = reader.getNext()) != null && record.getDate() > lastInteractionThreshold) {
|
||||||
|
Recipient recipient = Recipient.resolved(record.getRecipient().getId());
|
||||||
|
|
||||||
|
if (recipient.isGroup()) {
|
||||||
|
recipientsWithinInteractionThreshold.addAll(recipient.getParticipants());
|
||||||
|
} else {
|
||||||
|
recipientsWithinInteractionThreshold.add(recipient);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Stream.of(recipientsWithinInteractionThreshold)
|
||||||
|
.filterNot(Recipient::isLocalNumber)
|
||||||
|
.filter(r -> r.getLastProfileFetchTime() < lastProfileFetchThreshold)
|
||||||
|
.limit(limit)
|
||||||
|
.map(Recipient::getId)
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void markProfilesFetched(@NonNull Collection<RecipientId> ids, long time) {
|
||||||
|
SQLiteDatabase db = databaseHelper.getWritableDatabase();
|
||||||
|
db.beginTransaction();
|
||||||
|
try {
|
||||||
|
ContentValues values = new ContentValues(1);
|
||||||
|
values.put(LAST_PROFILE_FETCH, time);
|
||||||
|
|
||||||
|
for (RecipientId id : ids) {
|
||||||
|
db.update(TABLE_NAME, values, ID_WHERE, new String[] { id.serialize() });
|
||||||
|
}
|
||||||
|
db.setTransactionSuccessful();
|
||||||
|
} finally {
|
||||||
|
db.endTransaction();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void applyBlockedUpdate(@NonNull List<SignalServiceAddress> blocked, List<byte[]> groupIds) {
|
public void applyBlockedUpdate(@NonNull List<SignalServiceAddress> blocked, List<byte[]> groupIds) {
|
||||||
List<String> blockedE164 = Stream.of(blocked)
|
List<String> blockedE164 = Stream.of(blocked)
|
||||||
.filter(b -> b.getNumber().isPresent())
|
.filter(b -> b.getNumber().isPresent())
|
||||||
|
@ -1885,6 +1938,7 @@ public class RecipientDatabase extends Database {
|
||||||
private final String signalProfileAvatar;
|
private final String signalProfileAvatar;
|
||||||
private final boolean hasProfileImage;
|
private final boolean hasProfileImage;
|
||||||
private final boolean profileSharing;
|
private final boolean profileSharing;
|
||||||
|
private final long lastProfileFetch;
|
||||||
private final String notificationChannel;
|
private final String notificationChannel;
|
||||||
private final UnidentifiedAccessMode unidentifiedAccessMode;
|
private final UnidentifiedAccessMode unidentifiedAccessMode;
|
||||||
private final boolean forceSmsSelection;
|
private final boolean forceSmsSelection;
|
||||||
|
@ -1923,6 +1977,7 @@ public class RecipientDatabase extends Database {
|
||||||
@Nullable String signalProfileAvatar,
|
@Nullable String signalProfileAvatar,
|
||||||
boolean hasProfileImage,
|
boolean hasProfileImage,
|
||||||
boolean profileSharing,
|
boolean profileSharing,
|
||||||
|
long lastProfileFetch,
|
||||||
@Nullable String notificationChannel,
|
@Nullable String notificationChannel,
|
||||||
@NonNull UnidentifiedAccessMode unidentifiedAccessMode,
|
@NonNull UnidentifiedAccessMode unidentifiedAccessMode,
|
||||||
boolean forceSmsSelection,
|
boolean forceSmsSelection,
|
||||||
|
@ -1961,6 +2016,7 @@ public class RecipientDatabase extends Database {
|
||||||
this.signalProfileAvatar = signalProfileAvatar;
|
this.signalProfileAvatar = signalProfileAvatar;
|
||||||
this.hasProfileImage = hasProfileImage;
|
this.hasProfileImage = hasProfileImage;
|
||||||
this.profileSharing = profileSharing;
|
this.profileSharing = profileSharing;
|
||||||
|
this.lastProfileFetch = lastProfileFetch;
|
||||||
this.notificationChannel = notificationChannel;
|
this.notificationChannel = notificationChannel;
|
||||||
this.unidentifiedAccessMode = unidentifiedAccessMode;
|
this.unidentifiedAccessMode = unidentifiedAccessMode;
|
||||||
this.forceSmsSelection = forceSmsSelection;
|
this.forceSmsSelection = forceSmsSelection;
|
||||||
|
@ -2091,6 +2147,10 @@ public class RecipientDatabase extends Database {
|
||||||
return profileSharing;
|
return profileSharing;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long getLastProfileFetch() {
|
||||||
|
return lastProfileFetch;
|
||||||
|
}
|
||||||
|
|
||||||
public @Nullable String getNotificationChannel() {
|
public @Nullable String getNotificationChannel() {
|
||||||
return notificationChannel;
|
return notificationChannel;
|
||||||
}
|
}
|
||||||
|
|
|
@ -135,8 +135,9 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
|
||||||
private static final int REMOTE_DELETE = 60;
|
private static final int REMOTE_DELETE = 60;
|
||||||
private static final int COLOR_MIGRATION = 61;
|
private static final int COLOR_MIGRATION = 61;
|
||||||
private static final int LAST_SCROLLED = 62;
|
private static final int LAST_SCROLLED = 62;
|
||||||
|
private static final int LAST_PROFILE_FETCH = 63;
|
||||||
|
|
||||||
private static final int DATABASE_VERSION = 62;
|
private static final int DATABASE_VERSION = 63;
|
||||||
private static final String DATABASE_NAME = "signal.db";
|
private static final String DATABASE_NAME = "signal.db";
|
||||||
|
|
||||||
private final Context context;
|
private final Context context;
|
||||||
|
@ -911,6 +912,10 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
|
||||||
db.execSQL("ALTER TABLE thread ADD COLUMN last_scrolled INTEGER DEFAULT 0");
|
db.execSQL("ALTER TABLE thread ADD COLUMN last_scrolled INTEGER DEFAULT 0");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (oldVersion < LAST_PROFILE_FETCH) {
|
||||||
|
db.execSQL("ALTER TABLE recipient ADD COLUMN last_profile_fetch INTEGER DEFAULT 0");
|
||||||
|
}
|
||||||
|
|
||||||
db.setTransactionSuccessful();
|
db.setTransactionSuccessful();
|
||||||
} finally {
|
} finally {
|
||||||
db.endTransaction();
|
db.endTransaction();
|
||||||
|
|
|
@ -45,7 +45,7 @@ public class RefreshPreKeysJob extends BaseJob {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void scheduleIfNecessary() {
|
public static void scheduleIfNecessary() {
|
||||||
long timeSinceLastRefresh = System.currentTimeMillis() - SignalStore.getLastPrekeyRefreshTime();
|
long timeSinceLastRefresh = System.currentTimeMillis() - SignalStore.misc().getLastPrekeyRefreshTime();
|
||||||
|
|
||||||
if (timeSinceLastRefresh > REFRESH_INTERVAL) {
|
if (timeSinceLastRefresh > REFRESH_INTERVAL) {
|
||||||
Log.i(TAG, "Scheduling a prekey refresh. Time since last schedule: " + timeSinceLastRefresh + " ms");
|
Log.i(TAG, "Scheduling a prekey refresh. Time since last schedule: " + timeSinceLastRefresh + " ms");
|
||||||
|
@ -82,7 +82,7 @@ public class RefreshPreKeysJob extends BaseJob {
|
||||||
|
|
||||||
if (availableKeys >= PREKEY_MINIMUM && TextSecurePreferences.isSignedPreKeyRegistered(context)) {
|
if (availableKeys >= PREKEY_MINIMUM && TextSecurePreferences.isSignedPreKeyRegistered(context)) {
|
||||||
Log.i(TAG, "Available keys sufficient.");
|
Log.i(TAG, "Available keys sufficient.");
|
||||||
SignalStore.setLastPrekeyRefreshTime(System.currentTimeMillis());
|
SignalStore.misc().setLastPrekeyRefreshTime(System.currentTimeMillis());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,7 +98,7 @@ public class RefreshPreKeysJob extends BaseJob {
|
||||||
TextSecurePreferences.setSignedPreKeyRegistered(context, true);
|
TextSecurePreferences.setSignedPreKeyRegistered(context, true);
|
||||||
|
|
||||||
ApplicationDependencies.getJobManager().add(new CleanPreKeysJob());
|
ApplicationDependencies.getJobManager().add(new CleanPreKeysJob());
|
||||||
SignalStore.setLastPrekeyRefreshTime(System.currentTimeMillis());
|
SignalStore.misc().setLastPrekeyRefreshTime(System.currentTimeMillis());
|
||||||
Log.i(TAG, "Successfully refreshed prekeys.");
|
Log.i(TAG, "Successfully refreshed prekeys.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package org.thoughtcrime.securesms.jobs;
|
package org.thoughtcrime.securesms.jobs;
|
||||||
|
|
||||||
|
|
||||||
|
import android.app.Application;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
|
||||||
|
@ -22,7 +23,7 @@ import org.thoughtcrime.securesms.jobmanager.Data;
|
||||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
import org.thoughtcrime.securesms.jobmanager.Job;
|
||||||
import org.thoughtcrime.securesms.jobmanager.JobManager;
|
import org.thoughtcrime.securesms.jobmanager.JobManager;
|
||||||
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
|
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
|
||||||
import org.thoughtcrime.securesms.linkpreview.Link;
|
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||||
import org.thoughtcrime.securesms.logging.Log;
|
import org.thoughtcrime.securesms.logging.Log;
|
||||||
import org.thoughtcrime.securesms.profiles.ProfileName;
|
import org.thoughtcrime.securesms.profiles.ProfileName;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
|
@ -32,6 +33,7 @@ import org.thoughtcrime.securesms.util.Base64;
|
||||||
import org.thoughtcrime.securesms.util.FeatureFlags;
|
import org.thoughtcrime.securesms.util.FeatureFlags;
|
||||||
import org.thoughtcrime.securesms.util.IdentityUtil;
|
import org.thoughtcrime.securesms.util.IdentityUtil;
|
||||||
import org.thoughtcrime.securesms.util.ProfileUtil;
|
import org.thoughtcrime.securesms.util.ProfileUtil;
|
||||||
|
import org.thoughtcrime.securesms.util.SetUtil;
|
||||||
import org.thoughtcrime.securesms.util.Stopwatch;
|
import org.thoughtcrime.securesms.util.Stopwatch;
|
||||||
import org.thoughtcrime.securesms.util.Util;
|
import org.thoughtcrime.securesms.util.Util;
|
||||||
import org.thoughtcrime.securesms.util.concurrent.SignalExecutors;
|
import org.thoughtcrime.securesms.util.concurrent.SignalExecutors;
|
||||||
|
@ -48,7 +50,6 @@ import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException
|
||||||
import org.whispersystems.signalservice.internal.util.concurrent.ListenableFuture;
|
import org.whispersystems.signalservice.internal.util.concurrent.ListenableFuture;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
@ -128,6 +129,36 @@ public class RetrieveProfileJob extends BaseJob {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will fetch some profiles to ensure we're decently up-to-date if we haven't done so within a
|
||||||
|
* certain time period.
|
||||||
|
*/
|
||||||
|
public static void enqueueRoutineFetchIfNeccessary(Application application) {
|
||||||
|
long timeSinceRefresh = System.currentTimeMillis() - SignalStore.misc().getLastProfileRefreshTime();
|
||||||
|
if (timeSinceRefresh < TimeUnit.HOURS.toMillis(12)) {
|
||||||
|
Log.i(TAG, "Too soon to refresh. Did the last refresh " + timeSinceRefresh + " ms ago.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SignalExecutors.BOUNDED.execute(() -> {
|
||||||
|
RecipientDatabase db = DatabaseFactory.getRecipientDatabase(application);
|
||||||
|
long current = System.currentTimeMillis();
|
||||||
|
|
||||||
|
List<RecipientId> ids = db.getRecipientsForRoutineProfileFetch(current - TimeUnit.DAYS.toMillis(30),
|
||||||
|
current - TimeUnit.DAYS.toMillis(1),
|
||||||
|
50);
|
||||||
|
|
||||||
|
if (ids.size() > 0) {
|
||||||
|
Log.i(TAG, "Optimistically refreshing " + ids.size() + " eligible recipient(s).");
|
||||||
|
enqueue(ids);
|
||||||
|
} else {
|
||||||
|
Log.i(TAG, "No recipients to refresh.");
|
||||||
|
}
|
||||||
|
|
||||||
|
SignalStore.misc().setLastProfileRefreshTime(System.currentTimeMillis());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private RetrieveProfileJob(@NonNull List<RecipientId> recipientIds) {
|
private RetrieveProfileJob(@NonNull List<RecipientId> recipientIds) {
|
||||||
this(new Job.Parameters.Builder()
|
this(new Job.Parameters.Builder()
|
||||||
.addConstraint(NetworkConstraint.KEY)
|
.addConstraint(NetworkConstraint.KEY)
|
||||||
|
@ -197,6 +228,9 @@ public class RetrieveProfileJob extends BaseJob {
|
||||||
process(profile.first(), profile.second());
|
process(profile.first(), profile.second());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Set<RecipientId> success = SetUtil.difference(recipientIds, retries);
|
||||||
|
DatabaseFactory.getRecipientDatabase(context).markProfilesFetched(success, System.currentTimeMillis());
|
||||||
|
|
||||||
stopwatch.split("process");
|
stopwatch.split("process");
|
||||||
|
|
||||||
long keyCount = Stream.of(profiles).map(Pair::first).map(Recipient::getProfileKey).withoutNulls().count();
|
long keyCount = Stream.of(profiles).map(Pair::first).map(Recipient::getProfileKey).withoutNulls().count();
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
package org.thoughtcrime.securesms.keyvalue;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
public final class MiscellaneousValues extends SignalStoreValues {
|
||||||
|
|
||||||
|
private static final String LAST_PREKEY_REFRESH_TIME = "last_prekey_refresh_time";
|
||||||
|
private static final String MESSAGE_REQUEST_ENABLE_TIME = "message_request_enable_time";
|
||||||
|
private static final String LAST_PROFILE_REFRESH_TIME = "misc.last_profile_refresh_time";
|
||||||
|
|
||||||
|
MiscellaneousValues(@NonNull KeyValueStore store) {
|
||||||
|
super(store);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void onFirstEverAppLaunch() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getLastPrekeyRefreshTime() {
|
||||||
|
return getLong(LAST_PREKEY_REFRESH_TIME, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLastPrekeyRefreshTime(long time) {
|
||||||
|
putLong(LAST_PREKEY_REFRESH_TIME, time);
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getMessageRequestEnableTime() {
|
||||||
|
return getLong(MESSAGE_REQUEST_ENABLE_TIME, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMessageRequestEnableTime(long time) {
|
||||||
|
putLong(MESSAGE_REQUEST_ENABLE_TIME, time);
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getLastProfileRefreshTime() {
|
||||||
|
return getLong(LAST_PROFILE_REFRESH_TIME, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLastProfileRefreshTime(long time) {
|
||||||
|
putLong(LAST_PROFILE_REFRESH_TIME, time);
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,9 +12,6 @@ import org.thoughtcrime.securesms.logging.SignalUncaughtExceptionHandler;
|
||||||
*/
|
*/
|
||||||
public final class SignalStore {
|
public final class SignalStore {
|
||||||
|
|
||||||
private static final String LAST_PREKEY_REFRESH_TIME = "last_prekey_refresh_time";
|
|
||||||
private static final String MESSAGE_REQUEST_ENABLE_TIME = "message_request_enable_time";
|
|
||||||
|
|
||||||
private static final SignalStore INSTANCE = new SignalStore();
|
private static final SignalStore INSTANCE = new SignalStore();
|
||||||
|
|
||||||
private final KeyValueStore store;
|
private final KeyValueStore store;
|
||||||
|
@ -25,6 +22,7 @@ public final class SignalStore {
|
||||||
private final StorageServiceValues storageServiceValues;
|
private final StorageServiceValues storageServiceValues;
|
||||||
private final UiHints uiHints;
|
private final UiHints uiHints;
|
||||||
private final TooltipValues tooltipValues;
|
private final TooltipValues tooltipValues;
|
||||||
|
private final MiscellaneousValues misc;
|
||||||
|
|
||||||
private SignalStore() {
|
private SignalStore() {
|
||||||
this.store = ApplicationDependencies.getKeyValueStore();
|
this.store = ApplicationDependencies.getKeyValueStore();
|
||||||
|
@ -35,6 +33,7 @@ public final class SignalStore {
|
||||||
this.storageServiceValues = new StorageServiceValues(store);
|
this.storageServiceValues = new StorageServiceValues(store);
|
||||||
this.uiHints = new UiHints(store);
|
this.uiHints = new UiHints(store);
|
||||||
this.tooltipValues = new TooltipValues(store);
|
this.tooltipValues = new TooltipValues(store);
|
||||||
|
this.misc = new MiscellaneousValues(store);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void onFirstEverAppLaunch() {
|
public static void onFirstEverAppLaunch() {
|
||||||
|
@ -71,26 +70,14 @@ public final class SignalStore {
|
||||||
return INSTANCE.tooltipValues;
|
return INSTANCE.tooltipValues;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static @NonNull MiscellaneousValues misc() {
|
||||||
|
return INSTANCE.misc;
|
||||||
|
}
|
||||||
|
|
||||||
public static @NonNull GroupsV2AuthorizationSignalStoreCache groupsV2AuthorizationCache() {
|
public static @NonNull GroupsV2AuthorizationSignalStoreCache groupsV2AuthorizationCache() {
|
||||||
return new GroupsV2AuthorizationSignalStoreCache(getStore());
|
return new GroupsV2AuthorizationSignalStoreCache(getStore());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static long getLastPrekeyRefreshTime() {
|
|
||||||
return getStore().getLong(LAST_PREKEY_REFRESH_TIME, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void setLastPrekeyRefreshTime(long time) {
|
|
||||||
putLong(LAST_PREKEY_REFRESH_TIME, time);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static long getMessageRequestEnableTime() {
|
|
||||||
return getStore().getLong(MESSAGE_REQUEST_ENABLE_TIME, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void setMessageRequestEnableTime(long time) {
|
|
||||||
putLong(MESSAGE_REQUEST_ENABLE_TIME, time);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static @NonNull PreferenceDataStore getPreferenceDataStore() {
|
public static @NonNull PreferenceDataStore getPreferenceDataStore() {
|
||||||
return new SignalPreferenceDataStore(getStore());
|
return new SignalPreferenceDataStore(getStore());
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,6 +89,7 @@ public class Recipient {
|
||||||
private final String profileAvatar;
|
private final String profileAvatar;
|
||||||
private final boolean hasProfileImage;
|
private final boolean hasProfileImage;
|
||||||
private final boolean profileSharing;
|
private final boolean profileSharing;
|
||||||
|
private final long lastProfileFetch;
|
||||||
private final String notificationChannel;
|
private final String notificationChannel;
|
||||||
private final UnidentifiedAccessMode unidentifiedAccessMode;
|
private final UnidentifiedAccessMode unidentifiedAccessMode;
|
||||||
private final boolean forceSmsSelection;
|
private final boolean forceSmsSelection;
|
||||||
|
@ -332,6 +333,7 @@ public class Recipient {
|
||||||
this.profileAvatar = null;
|
this.profileAvatar = null;
|
||||||
this.hasProfileImage = false;
|
this.hasProfileImage = false;
|
||||||
this.profileSharing = false;
|
this.profileSharing = false;
|
||||||
|
this.lastProfileFetch = 0;
|
||||||
this.notificationChannel = null;
|
this.notificationChannel = null;
|
||||||
this.unidentifiedAccessMode = UnidentifiedAccessMode.DISABLED;
|
this.unidentifiedAccessMode = UnidentifiedAccessMode.DISABLED;
|
||||||
this.forceSmsSelection = false;
|
this.forceSmsSelection = false;
|
||||||
|
@ -374,6 +376,7 @@ public class Recipient {
|
||||||
this.profileAvatar = details.profileAvatar;
|
this.profileAvatar = details.profileAvatar;
|
||||||
this.hasProfileImage = details.hasProfileImage;
|
this.hasProfileImage = details.hasProfileImage;
|
||||||
this.profileSharing = details.profileSharing;
|
this.profileSharing = details.profileSharing;
|
||||||
|
this.lastProfileFetch = details.lastProfileFetch;
|
||||||
this.notificationChannel = details.notificationChannel;
|
this.notificationChannel = details.notificationChannel;
|
||||||
this.unidentifiedAccessMode = details.unidentifiedAccessMode;
|
this.unidentifiedAccessMode = details.unidentifiedAccessMode;
|
||||||
this.forceSmsSelection = details.forceSmsSelection;
|
this.forceSmsSelection = details.forceSmsSelection;
|
||||||
|
@ -610,6 +613,10 @@ public class Recipient {
|
||||||
return profileSharing;
|
return profileSharing;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long getLastProfileFetchTime() {
|
||||||
|
return lastProfileFetch;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isGroup() {
|
public boolean isGroup() {
|
||||||
return resolve().groupId != null;
|
return resolve().groupId != null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,6 +53,7 @@ public class RecipientDetails {
|
||||||
final String profileAvatar;
|
final String profileAvatar;
|
||||||
final boolean hasProfileImage;
|
final boolean hasProfileImage;
|
||||||
final boolean profileSharing;
|
final boolean profileSharing;
|
||||||
|
final long lastProfileFetch;
|
||||||
final boolean systemContact;
|
final boolean systemContact;
|
||||||
final boolean isLocalNumber;
|
final boolean isLocalNumber;
|
||||||
final String notificationChannel;
|
final String notificationChannel;
|
||||||
|
@ -99,6 +100,7 @@ public class RecipientDetails {
|
||||||
this.profileAvatar = settings.getProfileAvatar();
|
this.profileAvatar = settings.getProfileAvatar();
|
||||||
this.hasProfileImage = settings.hasProfileImage();
|
this.hasProfileImage = settings.hasProfileImage();
|
||||||
this.profileSharing = settings.isProfileSharing();
|
this.profileSharing = settings.isProfileSharing();
|
||||||
|
this.lastProfileFetch = settings.getLastProfileFetch();
|
||||||
this.systemContact = systemContact;
|
this.systemContact = systemContact;
|
||||||
this.isLocalNumber = isLocalNumber;
|
this.isLocalNumber = isLocalNumber;
|
||||||
this.notificationChannel = settings.getNotificationChannel();
|
this.notificationChannel = settings.getNotificationChannel();
|
||||||
|
@ -146,6 +148,7 @@ public class RecipientDetails {
|
||||||
this.profileAvatar = null;
|
this.profileAvatar = null;
|
||||||
this.hasProfileImage = false;
|
this.hasProfileImage = false;
|
||||||
this.profileSharing = false;
|
this.profileSharing = false;
|
||||||
|
this.lastProfileFetch = 0;
|
||||||
this.systemContact = true;
|
this.systemContact = true;
|
||||||
this.isLocalNumber = false;
|
this.isLocalNumber = false;
|
||||||
this.notificationChannel = null;
|
this.notificationChannel = null;
|
||||||
|
|
|
@ -163,7 +163,7 @@ public class RecipientUtil {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
long beforeTime = SignalStore.getMessageRequestEnableTime();
|
long beforeTime = SignalStore.misc().getMessageRequestEnableTime();
|
||||||
return DatabaseFactory.getMmsSmsDatabase(context).getConversationCount(threadId, beforeTime) > 0;
|
return DatabaseFactory.getMmsSmsDatabase(context).getConversationCount(threadId, beforeTime) > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -130,7 +130,7 @@ public final class FeatureFlags {
|
||||||
* desired test state.
|
* desired test state.
|
||||||
*/
|
*/
|
||||||
private static final Map<String, OnFlagChange> FLAG_CHANGE_LISTENERS = new HashMap<String, OnFlagChange>() {{
|
private static final Map<String, OnFlagChange> FLAG_CHANGE_LISTENERS = new HashMap<String, OnFlagChange>() {{
|
||||||
put(MESSAGE_REQUESTS, (change) -> SignalStore.setMessageRequestEnableTime(change == Change.ENABLED ? System.currentTimeMillis() : 0));
|
put(MESSAGE_REQUESTS, (change) -> SignalStore.misc().setMessageRequestEnableTime(change == Change.ENABLED ? System.currentTimeMillis() : 0));
|
||||||
put(VERSIONED_PROFILES, (change) -> {
|
put(VERSIONED_PROFILES, (change) -> {
|
||||||
if (change == Change.ENABLED) {
|
if (change == Change.ENABLED) {
|
||||||
ApplicationDependencies.getJobManager().add(new ProfileUploadJob());
|
ApplicationDependencies.getJobManager().add(new ProfileUploadJob());
|
||||||
|
|
Loading…
Add table
Reference in a new issue