Move capabilities into a single column.
This commit is contained in:
parent
ead64d92a5
commit
3357475fc4
9 changed files with 276 additions and 60 deletions
|
@ -31,6 +31,7 @@ import org.thoughtcrime.securesms.groups.v2.ProfileKeySet;
|
||||||
import org.thoughtcrime.securesms.groups.v2.processing.GroupsV2StateProcessor;
|
import org.thoughtcrime.securesms.groups.v2.processing.GroupsV2StateProcessor;
|
||||||
import org.thoughtcrime.securesms.jobs.RefreshAttributesJob;
|
import org.thoughtcrime.securesms.jobs.RefreshAttributesJob;
|
||||||
import org.thoughtcrime.securesms.jobs.RequestGroupV2InfoJob;
|
import org.thoughtcrime.securesms.jobs.RequestGroupV2InfoJob;
|
||||||
|
import org.thoughtcrime.securesms.jobs.RetrieveProfileJob;
|
||||||
import org.thoughtcrime.securesms.logging.Log;
|
import org.thoughtcrime.securesms.logging.Log;
|
||||||
import org.thoughtcrime.securesms.profiles.AvatarHelper;
|
import org.thoughtcrime.securesms.profiles.AvatarHelper;
|
||||||
import org.thoughtcrime.securesms.profiles.ProfileName;
|
import org.thoughtcrime.securesms.profiles.ProfileName;
|
||||||
|
@ -40,6 +41,7 @@ import org.thoughtcrime.securesms.storage.StorageSyncHelper;
|
||||||
import org.thoughtcrime.securesms.storage.StorageSyncHelper.RecordUpdate;
|
import org.thoughtcrime.securesms.storage.StorageSyncHelper.RecordUpdate;
|
||||||
import org.thoughtcrime.securesms.storage.StorageSyncModels;
|
import org.thoughtcrime.securesms.storage.StorageSyncModels;
|
||||||
import org.thoughtcrime.securesms.util.Base64;
|
import org.thoughtcrime.securesms.util.Base64;
|
||||||
|
import org.thoughtcrime.securesms.util.Bitmask;
|
||||||
import org.thoughtcrime.securesms.util.CursorUtil;
|
import org.thoughtcrime.securesms.util.CursorUtil;
|
||||||
import org.thoughtcrime.securesms.util.GroupUtil;
|
import org.thoughtcrime.securesms.util.GroupUtil;
|
||||||
import org.thoughtcrime.securesms.util.IdentityUtil;
|
import org.thoughtcrime.securesms.util.IdentityUtil;
|
||||||
|
@ -114,8 +116,7 @@ public class RecipientDatabase extends Database {
|
||||||
private static final String LAST_PROFILE_FETCH = "last_profile_fetch";
|
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 CAPABILITIES = "capabilities";
|
||||||
private static final String GROUPS_V2_CAPABILITY = "gv2_capability";
|
|
||||||
private static final String STORAGE_SERVICE_ID = "storage_service_key";
|
private static final String STORAGE_SERVICE_ID = "storage_service_key";
|
||||||
private static final String DIRTY = "dirty";
|
private static final String DIRTY = "dirty";
|
||||||
private static final String PROFILE_GIVEN_NAME = "signal_profile_name";
|
private static final String PROFILE_GIVEN_NAME = "signal_profile_name";
|
||||||
|
@ -129,6 +130,11 @@ public class RecipientDatabase extends Database {
|
||||||
private static final String IDENTITY_STATUS = "identity_status";
|
private static final String IDENTITY_STATUS = "identity_status";
|
||||||
private static final String IDENTITY_KEY = "identity_key";
|
private static final String IDENTITY_KEY = "identity_key";
|
||||||
|
|
||||||
|
private static final class Capabilities {
|
||||||
|
static final int BIT_LENGTH = 2;
|
||||||
|
static final int GROUPS_V2 = 0;
|
||||||
|
}
|
||||||
|
|
||||||
private static final String[] RECIPIENT_PROJECTION = new String[] {
|
private static final String[] RECIPIENT_PROJECTION = new String[] {
|
||||||
ID, UUID, USERNAME, PHONE, EMAIL, GROUP_ID, GROUP_TYPE,
|
ID, UUID, USERNAME, PHONE, EMAIL, GROUP_ID, GROUP_TYPE,
|
||||||
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,
|
||||||
|
@ -138,7 +144,7 @@ public class RecipientDatabase extends Database {
|
||||||
NOTIFICATION_CHANNEL,
|
NOTIFICATION_CHANNEL,
|
||||||
UNIDENTIFIED_ACCESS_MODE,
|
UNIDENTIFIED_ACCESS_MODE,
|
||||||
FORCE_SMS_SELECTION,
|
FORCE_SMS_SELECTION,
|
||||||
UUID_CAPABILITY, GROUPS_V2_CAPABILITY,
|
CAPABILITIES,
|
||||||
STORAGE_SERVICE_ID, DIRTY,
|
STORAGE_SERVICE_ID, DIRTY,
|
||||||
MENTION_SETTING
|
MENTION_SETTING
|
||||||
};
|
};
|
||||||
|
@ -329,12 +335,11 @@ public class RecipientDatabase extends Database {
|
||||||
LAST_PROFILE_FETCH + " 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() + ", " +
|
|
||||||
GROUPS_V2_CAPABILITY + " INTEGER DEFAULT " + Recipient.Capability.UNKNOWN.serialize() + ", " +
|
|
||||||
STORAGE_SERVICE_ID + " TEXT UNIQUE DEFAULT NULL, " +
|
STORAGE_SERVICE_ID + " TEXT UNIQUE DEFAULT NULL, " +
|
||||||
DIRTY + " INTEGER DEFAULT " + DirtyState.CLEAN.getId() + ", " +
|
DIRTY + " INTEGER DEFAULT " + DirtyState.CLEAN.getId() + ", " +
|
||||||
MENTION_SETTING + " INTEGER DEFAULT " + MentionSetting.ALWAYS_NOTIFY.getId() + ", " +
|
MENTION_SETTING + " INTEGER DEFAULT " + MentionSetting.ALWAYS_NOTIFY.getId() + ", " +
|
||||||
STORAGE_PROTO + " TEXT DEFAULT NULL);";
|
STORAGE_PROTO + " TEXT DEFAULT NULL, " +
|
||||||
|
CAPABILITIES + " INTEGER DEFAULT 0);";
|
||||||
|
|
||||||
private static final String INSIGHTS_INVITEE_LIST = "SELECT " + TABLE_NAME + "." + ID +
|
private static final String INSIGHTS_INVITEE_LIST = "SELECT " + TABLE_NAME + "." + ID +
|
||||||
" FROM " + TABLE_NAME +
|
" FROM " + TABLE_NAME +
|
||||||
|
@ -503,6 +508,7 @@ public class RecipientDatabase extends Database {
|
||||||
if (transactionSuccessful) {
|
if (transactionSuccessful) {
|
||||||
if (recipientNeedingRefresh != null) {
|
if (recipientNeedingRefresh != null) {
|
||||||
Recipient.live(recipientNeedingRefresh).refresh();
|
Recipient.live(recipientNeedingRefresh).refresh();
|
||||||
|
RetrieveProfileJob.enqueue(recipientNeedingRefresh);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (remapped != null) {
|
if (remapped != null) {
|
||||||
|
@ -1173,8 +1179,7 @@ public class RecipientDatabase extends Database {
|
||||||
String notificationChannel = CursorUtil.requireString(cursor, NOTIFICATION_CHANNEL);
|
String notificationChannel = CursorUtil.requireString(cursor, NOTIFICATION_CHANNEL);
|
||||||
int unidentifiedAccessMode = CursorUtil.requireInt(cursor, UNIDENTIFIED_ACCESS_MODE);
|
int unidentifiedAccessMode = CursorUtil.requireInt(cursor, UNIDENTIFIED_ACCESS_MODE);
|
||||||
boolean forceSmsSelection = CursorUtil.requireBoolean(cursor, FORCE_SMS_SELECTION);
|
boolean forceSmsSelection = CursorUtil.requireBoolean(cursor, FORCE_SMS_SELECTION);
|
||||||
int uuidCapabilityValue = CursorUtil.requireInt(cursor, UUID_CAPABILITY);
|
long capabilities = CursorUtil.requireLong(cursor, CAPABILITIES);
|
||||||
int groupsV2CapabilityValue = CursorUtil.requireInt(cursor, GROUPS_V2_CAPABILITY);
|
|
||||||
String storageKeyRaw = CursorUtil.requireString(cursor, STORAGE_SERVICE_ID);
|
String storageKeyRaw = CursorUtil.requireString(cursor, STORAGE_SERVICE_ID);
|
||||||
int mentionSettingId = CursorUtil.requireInt(cursor, MENTION_SETTING);
|
int mentionSettingId = CursorUtil.requireInt(cursor, MENTION_SETTING);
|
||||||
|
|
||||||
|
@ -1240,8 +1245,7 @@ public class RecipientDatabase extends Database {
|
||||||
notificationChannel,
|
notificationChannel,
|
||||||
UnidentifiedAccessMode.fromMode(unidentifiedAccessMode),
|
UnidentifiedAccessMode.fromMode(unidentifiedAccessMode),
|
||||||
forceSmsSelection,
|
forceSmsSelection,
|
||||||
Recipient.Capability.deserialize(uuidCapabilityValue),
|
capabilities,
|
||||||
Recipient.Capability.deserialize(groupsV2CapabilityValue),
|
|
||||||
InsightsBannerTier.fromId(insightsBannerTier),
|
InsightsBannerTier.fromId(insightsBannerTier),
|
||||||
storageKey,
|
storageKey,
|
||||||
MentionSetting.fromId(mentionSettingId),
|
MentionSetting.fromId(mentionSettingId),
|
||||||
|
@ -1404,9 +1408,13 @@ public class RecipientDatabase extends Database {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setCapabilities(@NonNull RecipientId id, @NonNull SignalServiceProfile.Capabilities capabilities) {
|
public void setCapabilities(@NonNull RecipientId id, @NonNull SignalServiceProfile.Capabilities capabilities) {
|
||||||
ContentValues values = new ContentValues(2);
|
long value = 0;
|
||||||
values.put(UUID_CAPABILITY, Recipient.Capability.fromBoolean(capabilities.isUuid()).serialize());
|
|
||||||
values.put(GROUPS_V2_CAPABILITY, Recipient.Capability.fromBoolean(capabilities.isGv2()).serialize());
|
value = Bitmask.update(value, Capabilities.GROUPS_V2, Capabilities.BIT_LENGTH, Recipient.Capability.fromBoolean(capabilities.isGv2()).serialize());
|
||||||
|
|
||||||
|
ContentValues values = new ContentValues(1);
|
||||||
|
values.put(CAPABILITIES, value);
|
||||||
|
|
||||||
if (update(id, values)) {
|
if (update(id, values)) {
|
||||||
Recipient.live(id).refresh();
|
Recipient.live(id).refresh();
|
||||||
}
|
}
|
||||||
|
@ -2372,7 +2380,7 @@ public class RecipientDatabase extends Database {
|
||||||
uuidValues.put(SYSTEM_PHONE_LABEL, e164Settings.getSystemPhoneLabel());
|
uuidValues.put(SYSTEM_PHONE_LABEL, e164Settings.getSystemPhoneLabel());
|
||||||
uuidValues.put(SYSTEM_CONTACT_URI, e164Settings.getSystemContactUri());
|
uuidValues.put(SYSTEM_CONTACT_URI, e164Settings.getSystemContactUri());
|
||||||
uuidValues.put(PROFILE_SHARING, uuidSettings.isProfileSharing() || e164Settings.isProfileSharing());
|
uuidValues.put(PROFILE_SHARING, uuidSettings.isProfileSharing() || e164Settings.isProfileSharing());
|
||||||
uuidValues.put(GROUPS_V2_CAPABILITY, uuidSettings.getGroupsV2Capability() != Recipient.Capability.UNKNOWN ? uuidSettings.getGroupsV2Capability().serialize() : e164Settings.getGroupsV2Capability().serialize());
|
uuidValues.put(CAPABILITIES, Math.max(uuidSettings.getCapabilities(), e164Settings.getCapabilities()));
|
||||||
uuidValues.put(MENTION_SETTING, uuidSettings.getMentionSetting() != MentionSetting.ALWAYS_NOTIFY ? uuidSettings.getMentionSetting().getId() : e164Settings.getMentionSetting().getId());
|
uuidValues.put(MENTION_SETTING, uuidSettings.getMentionSetting() != MentionSetting.ALWAYS_NOTIFY ? uuidSettings.getMentionSetting().getId() : e164Settings.getMentionSetting().getId());
|
||||||
if (uuidSettings.getProfileKey() != null) {
|
if (uuidSettings.getProfileKey() != null) {
|
||||||
updateProfileValuesForMerge(uuidValues, uuidSettings);
|
updateProfileValuesForMerge(uuidValues, uuidSettings);
|
||||||
|
@ -2589,7 +2597,7 @@ public class RecipientDatabase extends Database {
|
||||||
private final String notificationChannel;
|
private final String notificationChannel;
|
||||||
private final UnidentifiedAccessMode unidentifiedAccessMode;
|
private final UnidentifiedAccessMode unidentifiedAccessMode;
|
||||||
private final boolean forceSmsSelection;
|
private final boolean forceSmsSelection;
|
||||||
private final Recipient.Capability uuidCapability;
|
private final long capabilities;
|
||||||
private final Recipient.Capability groupsV2Capability;
|
private final Recipient.Capability groupsV2Capability;
|
||||||
private final InsightsBannerTier insightsBannerTier;
|
private final InsightsBannerTier insightsBannerTier;
|
||||||
private final byte[] storageId;
|
private final byte[] storageId;
|
||||||
|
@ -2627,8 +2635,7 @@ public class RecipientDatabase extends Database {
|
||||||
@Nullable String notificationChannel,
|
@Nullable String notificationChannel,
|
||||||
@NonNull UnidentifiedAccessMode unidentifiedAccessMode,
|
@NonNull UnidentifiedAccessMode unidentifiedAccessMode,
|
||||||
boolean forceSmsSelection,
|
boolean forceSmsSelection,
|
||||||
Recipient.Capability uuidCapability,
|
long capabilities,
|
||||||
Recipient.Capability groupsV2Capability,
|
|
||||||
@NonNull InsightsBannerTier insightsBannerTier,
|
@NonNull InsightsBannerTier insightsBannerTier,
|
||||||
@Nullable byte[] storageId,
|
@Nullable byte[] storageId,
|
||||||
@NonNull MentionSetting mentionSetting,
|
@NonNull MentionSetting mentionSetting,
|
||||||
|
@ -2665,8 +2672,8 @@ public class RecipientDatabase extends Database {
|
||||||
this.notificationChannel = notificationChannel;
|
this.notificationChannel = notificationChannel;
|
||||||
this.unidentifiedAccessMode = unidentifiedAccessMode;
|
this.unidentifiedAccessMode = unidentifiedAccessMode;
|
||||||
this.forceSmsSelection = forceSmsSelection;
|
this.forceSmsSelection = forceSmsSelection;
|
||||||
this.uuidCapability = uuidCapability;
|
this.capabilities = capabilities;
|
||||||
this.groupsV2Capability = groupsV2Capability;
|
this.groupsV2Capability = Recipient.Capability.deserialize((int) Bitmask.read(capabilities, Capabilities.GROUPS_V2, Capabilities.BIT_LENGTH));
|
||||||
this.insightsBannerTier = insightsBannerTier;
|
this.insightsBannerTier = insightsBannerTier;
|
||||||
this.storageId = storageId;
|
this.storageId = storageId;
|
||||||
this.mentionSetting = mentionSetting;
|
this.mentionSetting = mentionSetting;
|
||||||
|
@ -2801,10 +2808,6 @@ public class RecipientDatabase extends Database {
|
||||||
return forceSmsSelection;
|
return forceSmsSelection;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Recipient.Capability getUuidCapability() {
|
|
||||||
return uuidCapability;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Recipient.Capability getGroupsV2Capability() {
|
public Recipient.Capability getGroupsV2Capability() {
|
||||||
return groupsV2Capability;
|
return groupsV2Capability;
|
||||||
}
|
}
|
||||||
|
@ -2821,6 +2824,10 @@ public class RecipientDatabase extends Database {
|
||||||
return syncExtras;
|
return syncExtras;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
long getCapabilities() {
|
||||||
|
return capabilities;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A bundle of data that's only necessary when syncing to storage service, not for a
|
* A bundle of data that's only necessary when syncing to storage service, not for a
|
||||||
* {@link Recipient}.
|
* {@link Recipient}.
|
||||||
|
|
|
@ -157,8 +157,9 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
|
||||||
private static final int MENTION_CLEANUP = 76;
|
private static final int MENTION_CLEANUP = 76;
|
||||||
private static final int MENTION_CLEANUP_V2 = 77;
|
private static final int MENTION_CLEANUP_V2 = 77;
|
||||||
private static final int REACTION_CLEANUP = 78;
|
private static final int REACTION_CLEANUP = 78;
|
||||||
|
private static final int CAPABILITIES_REFACTOR = 79;
|
||||||
|
|
||||||
private static final int DATABASE_VERSION = 78;
|
private static final int DATABASE_VERSION = 79;
|
||||||
private static final String DATABASE_NAME = "signal.db";
|
private static final String DATABASE_NAME = "signal.db";
|
||||||
|
|
||||||
private final Context context;
|
private final Context context;
|
||||||
|
@ -1131,6 +1132,13 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
|
||||||
db.update("sms", values, "remote_deleted = ?", new String[] { "1" });
|
db.update("sms", values, "remote_deleted = ?", new String[] { "1" });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (oldVersion < CAPABILITIES_REFACTOR) {
|
||||||
|
db.execSQL("ALTER TABLE recipient ADD COLUMN capabilities INTEGER DEFAULT 0");
|
||||||
|
|
||||||
|
db.execSQL("UPDATE recipient SET capabilities = 1 WHERE gv2_capability = 1");
|
||||||
|
db.execSQL("UPDATE recipient SET capabilities = 2 WHERE gv2_capability = -1");
|
||||||
|
}
|
||||||
|
|
||||||
db.setTransactionSuccessful();
|
db.setTransactionSuccessful();
|
||||||
} finally {
|
} finally {
|
||||||
db.endTransaction();
|
db.endTransaction();
|
||||||
|
|
|
@ -30,9 +30,7 @@ public final class LogSectionCapabilities implements LogSection {
|
||||||
|
|
||||||
AccountAttributes.Capabilities capabilities = AppCapabilities.getCapabilities(false);
|
AccountAttributes.Capabilities capabilities = AppCapabilities.getCapabilities(false);
|
||||||
|
|
||||||
return new StringBuilder().append("Local device UUID : ").append(capabilities.isUuid()).append("\n")
|
return new StringBuilder().append("Local device GV2: ").append(capabilities.isGv2()).append("\n")
|
||||||
.append("Global UUID : ").append(self.getUuidCapability()).append("\n")
|
|
||||||
.append("Local device GV2 : ").append(capabilities.isGv2()).append("\n")
|
|
||||||
.append("Global GV2 : ").append(self.getGroupsV2Capability()).append("\n");
|
.append("Global GV2 : ").append(self.getGroupsV2Capability()).append("\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -97,7 +97,6 @@ public class Recipient {
|
||||||
private final String notificationChannel;
|
private final String notificationChannel;
|
||||||
private final UnidentifiedAccessMode unidentifiedAccessMode;
|
private final UnidentifiedAccessMode unidentifiedAccessMode;
|
||||||
private final boolean forceSmsSelection;
|
private final boolean forceSmsSelection;
|
||||||
private final Capability uuidCapability;
|
|
||||||
private final Capability groupsV2Capability;
|
private final Capability groupsV2Capability;
|
||||||
private final InsightsBannerTier insightsBannerTier;
|
private final InsightsBannerTier insightsBannerTier;
|
||||||
private final byte[] storageId;
|
private final byte[] storageId;
|
||||||
|
@ -311,7 +310,6 @@ public class Recipient {
|
||||||
this.notificationChannel = null;
|
this.notificationChannel = null;
|
||||||
this.unidentifiedAccessMode = UnidentifiedAccessMode.DISABLED;
|
this.unidentifiedAccessMode = UnidentifiedAccessMode.DISABLED;
|
||||||
this.forceSmsSelection = false;
|
this.forceSmsSelection = false;
|
||||||
this.uuidCapability = Capability.UNKNOWN;
|
|
||||||
this.groupsV2Capability = Capability.UNKNOWN;
|
this.groupsV2Capability = Capability.UNKNOWN;
|
||||||
this.storageId = null;
|
this.storageId = null;
|
||||||
this.mentionSetting = MentionSetting.ALWAYS_NOTIFY;
|
this.mentionSetting = MentionSetting.ALWAYS_NOTIFY;
|
||||||
|
@ -353,7 +351,6 @@ public class Recipient {
|
||||||
this.notificationChannel = details.notificationChannel;
|
this.notificationChannel = details.notificationChannel;
|
||||||
this.unidentifiedAccessMode = details.unidentifiedAccessMode;
|
this.unidentifiedAccessMode = details.unidentifiedAccessMode;
|
||||||
this.forceSmsSelection = details.forceSmsSelection;
|
this.forceSmsSelection = details.forceSmsSelection;
|
||||||
this.uuidCapability = details.uuidCapability;
|
|
||||||
this.groupsV2Capability = details.groupsV2Capability;
|
this.groupsV2Capability = details.groupsV2Capability;
|
||||||
this.storageId = details.storageId;
|
this.storageId = details.storageId;
|
||||||
this.mentionSetting = details.mentionSetting;
|
this.mentionSetting = details.mentionSetting;
|
||||||
|
@ -740,25 +737,10 @@ public class Recipient {
|
||||||
return forceSmsSelection;
|
return forceSmsSelection;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return True if this recipient can support receiving UUID-only messages, otherwise false.
|
|
||||||
*/
|
|
||||||
public boolean isUuidSupported() {
|
|
||||||
if (FeatureFlags.usernames()) {
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return uuidCapability == Capability.SUPPORTED;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Capability getGroupsV2Capability() {
|
public Capability getGroupsV2Capability() {
|
||||||
return groupsV2Capability;
|
return groupsV2Capability;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Capability getUuidCapability() {
|
|
||||||
return uuidCapability;
|
|
||||||
}
|
|
||||||
|
|
||||||
public @Nullable byte[] getProfileKey() {
|
public @Nullable byte[] getProfileKey() {
|
||||||
return profileKey;
|
return profileKey;
|
||||||
}
|
}
|
||||||
|
@ -825,7 +807,7 @@ public class Recipient {
|
||||||
public enum Capability {
|
public enum Capability {
|
||||||
UNKNOWN(0),
|
UNKNOWN(0),
|
||||||
SUPPORTED(1),
|
SUPPORTED(1),
|
||||||
NOT_SUPPORTED(-1);
|
NOT_SUPPORTED(2);
|
||||||
|
|
||||||
private final int value;
|
private final int value;
|
||||||
|
|
||||||
|
@ -839,9 +821,10 @@ public class Recipient {
|
||||||
|
|
||||||
public static Capability deserialize(int value) {
|
public static Capability deserialize(int value) {
|
||||||
switch (value) {
|
switch (value) {
|
||||||
case 1 : return SUPPORTED;
|
case 0: return UNKNOWN;
|
||||||
case -1 : return NOT_SUPPORTED;
|
case 1: return SUPPORTED;
|
||||||
default : return UNKNOWN;
|
case 2: return NOT_SUPPORTED;
|
||||||
|
default: throw new IllegalArgumentException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -59,7 +59,6 @@ public class RecipientDetails {
|
||||||
final String notificationChannel;
|
final String notificationChannel;
|
||||||
final UnidentifiedAccessMode unidentifiedAccessMode;
|
final UnidentifiedAccessMode unidentifiedAccessMode;
|
||||||
final boolean forceSmsSelection;
|
final boolean forceSmsSelection;
|
||||||
final Recipient.Capability uuidCapability;
|
|
||||||
final Recipient.Capability groupsV2Capability;
|
final Recipient.Capability groupsV2Capability;
|
||||||
final InsightsBannerTier insightsBannerTier;
|
final InsightsBannerTier insightsBannerTier;
|
||||||
final byte[] storageId;
|
final byte[] storageId;
|
||||||
|
@ -104,7 +103,6 @@ public class RecipientDetails {
|
||||||
this.notificationChannel = settings.getNotificationChannel();
|
this.notificationChannel = settings.getNotificationChannel();
|
||||||
this.unidentifiedAccessMode = settings.getUnidentifiedAccessMode();
|
this.unidentifiedAccessMode = settings.getUnidentifiedAccessMode();
|
||||||
this.forceSmsSelection = settings.isForceSmsSelection();
|
this.forceSmsSelection = settings.isForceSmsSelection();
|
||||||
this.uuidCapability = settings.getUuidCapability();
|
|
||||||
this.groupsV2Capability = settings.getGroupsV2Capability();
|
this.groupsV2Capability = settings.getGroupsV2Capability();
|
||||||
this.insightsBannerTier = settings.getInsightsBannerTier();
|
this.insightsBannerTier = settings.getInsightsBannerTier();
|
||||||
this.storageId = settings.getStorageId();
|
this.storageId = settings.getStorageId();
|
||||||
|
@ -152,7 +150,6 @@ public class RecipientDetails {
|
||||||
this.unidentifiedAccessMode = UnidentifiedAccessMode.UNKNOWN;
|
this.unidentifiedAccessMode = UnidentifiedAccessMode.UNKNOWN;
|
||||||
this.forceSmsSelection = false;
|
this.forceSmsSelection = false;
|
||||||
this.name = null;
|
this.name = null;
|
||||||
this.uuidCapability = Recipient.Capability.UNKNOWN;
|
|
||||||
this.groupsV2Capability = Recipient.Capability.UNKNOWN;
|
this.groupsV2Capability = Recipient.Capability.UNKNOWN;
|
||||||
this.storageId = null;
|
this.storageId = null;
|
||||||
this.mentionSetting = MentionSetting.ALWAYS_NOTIFY;
|
this.mentionSetting = MentionSetting.ALWAYS_NOTIFY;
|
||||||
|
|
|
@ -0,0 +1,74 @@
|
||||||
|
package org.thoughtcrime.securesms.util;
|
||||||
|
|
||||||
|
import org.whispersystems.libsignal.util.guava.Preconditions;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A set of utilities to make working with Bitmasks easier.
|
||||||
|
*/
|
||||||
|
public final class Bitmask {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads a bitmasked boolean from a long at the requested position.
|
||||||
|
*/
|
||||||
|
public static boolean read(long value, int position) {
|
||||||
|
return read(value, position, 1) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads a bitmasked value from a long at the requested position.
|
||||||
|
*
|
||||||
|
* @param value The value your are reading state from
|
||||||
|
* @param position The position you'd like to read from
|
||||||
|
* @param flagBitSize How many bits are in each flag
|
||||||
|
* @return The value at the requested position
|
||||||
|
*/
|
||||||
|
public static long read(long value, int position, int flagBitSize) {
|
||||||
|
Preconditions.checkArgument(flagBitSize >= 0, "Must have a positive bit size! size: " + flagBitSize);
|
||||||
|
|
||||||
|
int bitsToShift = position * flagBitSize;
|
||||||
|
Preconditions.checkArgument(bitsToShift + flagBitSize <= 64 && position >= 0, String.format(Locale.US, "Your position is out of bounds! position: %d, flagBitSize: %d", position, flagBitSize));
|
||||||
|
|
||||||
|
long shifted = value >>> bitsToShift;
|
||||||
|
long mask = twoToThe(flagBitSize) - 1;
|
||||||
|
|
||||||
|
return shifted & mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the value at the specified position in a single-bit bitmasked long.
|
||||||
|
*/
|
||||||
|
public static long update(long existing, int position, boolean value) {
|
||||||
|
return update(existing, position, 1, value ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the value in a bitmasked long.
|
||||||
|
*
|
||||||
|
* @param existing The existing state of the bitmask
|
||||||
|
* @param position The position you'd like to update
|
||||||
|
* @param flagBitSize How many bits are in each flag
|
||||||
|
* @param value The value you'd like to set at the specified position
|
||||||
|
* @return The updated bitmask
|
||||||
|
*/
|
||||||
|
public static long update(long existing, int position, int flagBitSize, long value) {
|
||||||
|
Preconditions.checkArgument(flagBitSize >= 0, "Must have a positive bit size! size: " + flagBitSize);
|
||||||
|
Preconditions.checkArgument(value >= 0, "Value must be positive! value: " + value);
|
||||||
|
Preconditions.checkArgument(value < twoToThe(flagBitSize), String.format(Locale.US, "Value is larger than you can hold for the given bitsize! value: %d, flagBitSize: %d", value, flagBitSize));
|
||||||
|
|
||||||
|
int bitsToShift = position * flagBitSize;
|
||||||
|
Preconditions.checkArgument(bitsToShift + flagBitSize <= 64 && position >= 0, String.format(Locale.US, "Your position is out of bounds! position: %d, flagBitSize: %d", position, flagBitSize));
|
||||||
|
|
||||||
|
long clearMask = ~((twoToThe(flagBitSize) - 1) << bitsToShift);
|
||||||
|
long cleared = existing & clearMask;
|
||||||
|
long shiftedValue = value << bitsToShift;
|
||||||
|
|
||||||
|
return cleared | shiftedValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Simple method to do 2^n. Giving it a name just so it's clear what's happening. */
|
||||||
|
private static long twoToThe(long n) {
|
||||||
|
return 1 << n;
|
||||||
|
}
|
||||||
|
}
|
|
@ -30,6 +30,14 @@ public final class CursorUtil {
|
||||||
return cursor.getBlob(cursor.getColumnIndexOrThrow(column));
|
return cursor.getBlob(cursor.getColumnIndexOrThrow(column));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean requireMaskedBoolean(@NonNull Cursor cursor, @NonNull String column, int position) {
|
||||||
|
return Bitmask.read(requireLong(cursor, column), position);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int requireMaskedInt(@NonNull Cursor cursor, @NonNull String column, int position, int flagBitSize) {
|
||||||
|
return Util.toIntExact(Bitmask.read(requireLong(cursor, column), position, flagBitSize));
|
||||||
|
}
|
||||||
|
|
||||||
public static Optional<String> getString(@NonNull Cursor cursor, @NonNull String column) {
|
public static Optional<String> getString(@NonNull Cursor cursor, @NonNull String column) {
|
||||||
if (cursor.getColumnIndex(column) < 0) {
|
if (cursor.getColumnIndex(column) < 0) {
|
||||||
return Optional.absent();
|
return Optional.absent();
|
||||||
|
|
|
@ -0,0 +1,148 @@
|
||||||
|
package org.thoughtcrime.securesms.util;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
public class BitmaskTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void read_singleBit() {
|
||||||
|
assertFalse(Bitmask.read(0b00000000, 0));
|
||||||
|
assertFalse(Bitmask.read(0b11111101, 1));
|
||||||
|
assertFalse(Bitmask.read(0b11111011, 2));
|
||||||
|
assertFalse(Bitmask.read(0b01111111_11111111_11111111_11111111_11111111_11111111_11111111_11111111L, 63));
|
||||||
|
|
||||||
|
assertTrue(Bitmask.read(0b00000001, 0));
|
||||||
|
assertTrue(Bitmask.read(0b00000010, 1));
|
||||||
|
assertTrue(Bitmask.read(0b00000100, 2));
|
||||||
|
assertTrue(Bitmask.read(0b10000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000L, 63));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void read_twoBits() {
|
||||||
|
assertEquals(0, Bitmask.read(0b11111100, 0, 2));
|
||||||
|
assertEquals(1, Bitmask.read(0b11111101, 0, 2));
|
||||||
|
assertEquals(2, Bitmask.read(0b11111110, 0, 2));
|
||||||
|
assertEquals(3, Bitmask.read(0b11111111, 0, 2));
|
||||||
|
|
||||||
|
assertEquals(0, Bitmask.read(0b11110011, 1, 2));
|
||||||
|
assertEquals(1, Bitmask.read(0b11110111, 1, 2));
|
||||||
|
assertEquals(2, Bitmask.read(0b11111011, 1, 2));
|
||||||
|
assertEquals(3, Bitmask.read(0b11111111, 1, 2));
|
||||||
|
|
||||||
|
assertEquals(0, Bitmask.read(0b00000000, 2, 2));
|
||||||
|
assertEquals(1, Bitmask.read(0b00010000, 2, 2));
|
||||||
|
assertEquals(2, Bitmask.read(0b00100000, 2, 2));
|
||||||
|
assertEquals(3, Bitmask.read(0b00110000, 2, 2));
|
||||||
|
|
||||||
|
assertEquals(0, Bitmask.read(0b00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000L, 31, 2));
|
||||||
|
assertEquals(1, Bitmask.read(0b01000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000L, 31, 2));
|
||||||
|
assertEquals(2, Bitmask.read(0b10000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000L, 31, 2));
|
||||||
|
assertEquals(3, Bitmask.read(0b11000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000L, 31, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void read_fourBits() {
|
||||||
|
assertEquals(0, Bitmask.read(0b00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000L, 15, 4));
|
||||||
|
assertEquals(4, Bitmask.read(0b01000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000L, 15, 4));
|
||||||
|
assertEquals(8, Bitmask.read(0b10000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000L, 15, 4));
|
||||||
|
assertEquals(15, Bitmask.read(0b11110000_00000000_00000000_00000000_00000000_00000000_00000000_00000000L, 15, 4));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void read_error_negativeIndex() {
|
||||||
|
Bitmask.read(0b0000000, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void read_error_indexTooLarge_singleBit() {
|
||||||
|
Bitmask.read(0b0000000, 64);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void read_error_indexTooLarge_twoBits() {
|
||||||
|
Bitmask.read(0b0000000, 32, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void update_singleBit() {
|
||||||
|
assertEquals(0b00000001, Bitmask.update(0b00000000, 0, true));
|
||||||
|
assertEquals(0b00000010, Bitmask.update(0b00000000, 1, true));
|
||||||
|
assertEquals(0b00000100, Bitmask.update(0b00000000, 2, true));
|
||||||
|
assertEquals(0b10000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000L,
|
||||||
|
Bitmask.update(0b00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000L, 63, true));
|
||||||
|
|
||||||
|
assertEquals(0b11111110, Bitmask.update(0b11111111, 0, false));
|
||||||
|
assertEquals(0b11111101, Bitmask.update(0b11111111, 1, false));
|
||||||
|
assertEquals(0b11111011, Bitmask.update(0b11111111, 2, false));
|
||||||
|
assertEquals(0b01111111_11111111_11111111_11111111_11111111_11111111_11111111_11111111L,
|
||||||
|
Bitmask.update(0b11111111_11111111_11111111_11111111_11111111_11111111_11111111_11111111L, 63, false));
|
||||||
|
|
||||||
|
assertEquals(0b11111111, Bitmask.update(0b11111111, 0, true));
|
||||||
|
assertEquals(0b11111111, Bitmask.update(0b11111111, 1, true));
|
||||||
|
assertEquals(0b11111111, Bitmask.update(0b11111111, 2, true));
|
||||||
|
assertEquals(0b11111111_11111111_11111111_11111111_11111111_11111111_11111111_11111111L,
|
||||||
|
Bitmask.update(0b11111111_11111111_11111111_11111111_11111111_11111111_11111111_11111111L, 63, true));
|
||||||
|
|
||||||
|
assertEquals(0b00000000, Bitmask.update(0b00000000, 0, false));
|
||||||
|
assertEquals(0b00000000, Bitmask.update(0b00000000, 1, false));
|
||||||
|
assertEquals(0b00000000, Bitmask.update(0b00000000, 2, false));
|
||||||
|
assertEquals(0b00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000L,
|
||||||
|
Bitmask.update(0b00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000L, 63, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void update_twoBits() {
|
||||||
|
assertEquals(0b00000000, Bitmask.update(0b00000000, 0, 2, 0));
|
||||||
|
assertEquals(0b00000001, Bitmask.update(0b00000000, 0, 2, 1));
|
||||||
|
assertEquals(0b00000010, Bitmask.update(0b00000000, 0, 2, 2));
|
||||||
|
assertEquals(0b00000011, Bitmask.update(0b00000000, 0, 2, 3));
|
||||||
|
|
||||||
|
assertEquals(0b00000000, Bitmask.update(0b00000000, 1, 2, 0));
|
||||||
|
assertEquals(0b00000100, Bitmask.update(0b00000000, 1, 2, 1));
|
||||||
|
assertEquals(0b00001000, Bitmask.update(0b00000000, 1, 2, 2));
|
||||||
|
assertEquals(0b00001100, Bitmask.update(0b00000000, 1, 2, 3));
|
||||||
|
|
||||||
|
assertEquals(0b11111100, Bitmask.update(0b11111111, 0, 2, 0));
|
||||||
|
assertEquals(0b11111101, Bitmask.update(0b11111111, 0, 2, 1));
|
||||||
|
assertEquals(0b11111110, Bitmask.update(0b11111111, 0, 2, 2));
|
||||||
|
assertEquals(0b11111111, Bitmask.update(0b11111111, 0, 2, 3));
|
||||||
|
|
||||||
|
assertEquals(0b00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000L,
|
||||||
|
Bitmask.update(0b00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000L, 31, 2, 0));
|
||||||
|
assertEquals(0b01000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000L,
|
||||||
|
Bitmask.update(0b00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000L, 31, 2, 1));
|
||||||
|
assertEquals(0b10000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000L,
|
||||||
|
Bitmask.update(0b00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000L, 31, 2, 2));
|
||||||
|
assertEquals(0b11000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000L,
|
||||||
|
Bitmask.update(0b00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000L, 31, 2, 3));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void update_error_negativeIndex() {
|
||||||
|
Bitmask.update(0b0000000, -1, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void update_error_indexTooLarge_singleBit() {
|
||||||
|
Bitmask.update(0b0000000, 64, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void update_error_indexTooLarge_twoBits() {
|
||||||
|
Bitmask.update(0b0000000, 32, 2, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void update_error_negativeValue() {
|
||||||
|
Bitmask.update(0b0000000, 0, 2, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void update_error_valueTooLarge() {
|
||||||
|
Bitmask.update(0b0000000, 0, 2, 4);
|
||||||
|
}
|
||||||
|
}
|
|
@ -91,9 +91,6 @@ public class SignalServiceProfile {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Capabilities {
|
public static class Capabilities {
|
||||||
@JsonProperty
|
|
||||||
private boolean uuid;
|
|
||||||
|
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
private boolean gv2;
|
private boolean gv2;
|
||||||
|
|
||||||
|
@ -103,10 +100,6 @@ public class SignalServiceProfile {
|
||||||
@JsonCreator
|
@JsonCreator
|
||||||
public Capabilities() {}
|
public Capabilities() {}
|
||||||
|
|
||||||
public boolean isUuid() {
|
|
||||||
return uuid;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isGv2() {
|
public boolean isGv2() {
|
||||||
return gv2;
|
return gv2;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue