Skip to content

Commit

Permalink
Add support for remote feature flags.
Browse files Browse the repository at this point in the history
  • Loading branch information
greyson-signal committed Jan 27, 2020
1 parent b8602ee commit 55e9f87
Show file tree
Hide file tree
Showing 33 changed files with 403 additions and 58 deletions.
Expand Up @@ -49,6 +49,7 @@
import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob;
import org.thoughtcrime.securesms.jobs.PushNotificationReceiveJob;
import org.thoughtcrime.securesms.jobs.StickerPackDownloadJob;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.logging.AndroidLogger;
import org.thoughtcrime.securesms.logging.CustomSignalProtocolLogger;
import org.thoughtcrime.securesms.logging.Log;
Expand All @@ -71,6 +72,7 @@
import org.thoughtcrime.securesms.service.RotateSignedPreKeyListener;
import org.thoughtcrime.securesms.service.UpdateApkRefreshListener;
import org.thoughtcrime.securesms.stickers.BlessedPacks;
import org.thoughtcrime.securesms.util.FeatureFlags;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.concurrent.SignalExecutors;
Expand Down Expand Up @@ -133,6 +135,7 @@ public void onCreate() {
initializeBlobProvider();
initializeCleanup();
initializeCameraX();
FeatureFlags.init();
NotificationChannels.create(this);
ProcessLifecycleOwner.get().getLifecycle().addObserver(this);

Expand Down
Expand Up @@ -42,7 +42,6 @@
import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.FeatureFlags;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.ThemeUtil;

Expand Down
Expand Up @@ -113,7 +113,7 @@ public String[] getRecipientStrings() {
}

private String getRecipientName(Recipient recipient) {
if (FeatureFlags.PROFILE_DISPLAY) return recipient.getDisplayName(context);
if (FeatureFlags.profileDisplay()) return recipient.getDisplayName(context);

String name = recipient.toShortString(context);

Expand Down
Expand Up @@ -434,7 +434,7 @@ private void setSummaries(Recipient recipient) {
colorPreference.setColors(MaterialColors.CONVERSATION_PALETTE.asConversationColorArray(requireActivity()));
colorPreference.setColor(recipient.getColor().toActionBarColor(requireActivity()));

if (FeatureFlags.PROFILE_DISPLAY) {
if (FeatureFlags.profileDisplay()) {
aboutPreference.setTitle(recipient.getDisplayName(requireContext()));
aboutPreference.setSummary(recipient.resolve().getE164().or(""));
} else {
Expand Down
Expand Up @@ -275,7 +275,7 @@ public void onCreate(Bundle bundle) {
byte[] localId;
byte[] remoteId;

if (FeatureFlags.UUIDS && recipient.resolve().getUuid().isPresent()) {
if (FeatureFlags.uuids() && recipient.resolve().getUuid().isPresent()) {
Log.i(TAG, "Using UUID (version 2).");
version = 2;
localId = UuidUtil.toByteArray(TextSecurePreferences.getLocalUuid(requireContext()));
Expand Down
Expand Up @@ -61,7 +61,7 @@ public void setText(Recipient recipient, boolean read, @Nullable String suffix)

if (recipient.isLocalNumber()) {
builder.append(getContext().getString(R.string.note_to_self));
} else if (!FeatureFlags.PROFILE_DISPLAY && recipient.getName(getContext()) == null && !recipient.getProfileName().isEmpty()) {
} else if (!FeatureFlags.profileDisplay() && recipient.getName(getContext()) == null && !recipient.getProfileName().isEmpty()) {
SpannableString profileName = new SpannableString(" (~" + recipient.getProfileName().toString() + ") ");
profileName.setSpan(new CenterAlignedRelativeSizeSpan(0.75f), 0, profileName.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
profileName.setSpan(new TypefaceSpan("sans-serif-light"), 0, profileName.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
Expand Down
Expand Up @@ -370,7 +370,7 @@ private void setPersonInfo(final @NonNull Recipient recipient) {
.diskCacheStrategy(DiskCacheStrategy.ALL)
.into(this.photo);

if (FeatureFlags.PROFILE_DISPLAY) {
if (FeatureFlags.profileDisplay()) {
this.name.setText(recipient.getDisplayName(getContext()));

if (recipient.getE164().isPresent()) {
Expand Down
Expand Up @@ -145,15 +145,15 @@ private List<Cursor> getFilteredResults() {
cursorList.addAll(getContactsCursors());
}

if (FeatureFlags.USERNAMES && NumberUtil.isVisuallyValidNumberOrEmail(filter)) {
if (FeatureFlags.usernames() && NumberUtil.isVisuallyValidNumberOrEmail(filter)) {
cursorList.add(getPhoneNumberSearchHeaderCursor());
cursorList.add(getNewNumberCursor());
} else if (!FeatureFlags.USERNAMES && NumberUtil.isValidSmsOrEmail(filter)){
} else if (!FeatureFlags.usernames() && NumberUtil.isValidSmsOrEmail(filter)){
cursorList.add(getContactsHeaderCursor());
cursorList.add(getNewNumberCursor());
}

if (FeatureFlags.USERNAMES && UsernameUtil.isValidUsernameForSearch(filter)) {
if (FeatureFlags.usernames() && UsernameUtil.isValidUsernameForSearch(filter)) {
cursorList.add(getUsernameSearchHeaderCursor());
cursorList.add(getUsernameSearchCursor());
}
Expand Down
Expand Up @@ -17,14 +17,14 @@ public class DirectoryHelper {

@WorkerThread
public static void refreshDirectory(@NonNull Context context, boolean notifyOfNewUsers) throws IOException {
if (FeatureFlags.UUIDS) {
if (FeatureFlags.uuids()) {
// TODO [greyson] Create a DirectoryHelperV2 when appropriate.
DirectoryHelperV1.refreshDirectory(context, notifyOfNewUsers);
} else {
DirectoryHelperV1.refreshDirectory(context, notifyOfNewUsers);
}

if (FeatureFlags.STORAGE_SERVICE) {
if (FeatureFlags.storageService()) {
ApplicationDependencies.getJobManager().add(new StorageSyncJob());
}
}
Expand All @@ -34,14 +34,14 @@ public static RegisteredState refreshDirectoryFor(@NonNull Context context, @Non
RegisteredState originalRegisteredState = recipient.resolve().getRegistered();
RegisteredState newRegisteredState = null;

if (FeatureFlags.UUIDS) {
if (FeatureFlags.uuids()) {
// TODO [greyson] Create a DirectoryHelperV2 when appropriate.
newRegisteredState = DirectoryHelperV1.refreshDirectoryFor(context, recipient, notifyOfNewUsers);
} else {
newRegisteredState = DirectoryHelperV1.refreshDirectoryFor(context, recipient, notifyOfNewUsers);
}

if (FeatureFlags.STORAGE_SERVICE && newRegisteredState != originalRegisteredState) {
if (FeatureFlags.storageService() && newRegisteredState != originalRegisteredState) {
ApplicationDependencies.getJobManager().add(new StorageSyncJob());
}

Expand Down
Expand Up @@ -2014,7 +2014,7 @@ private void setBlockedUserState(Recipient recipient, boolean isSecureText, bool
}

private void setGroupShareProfileReminder(@NonNull Recipient recipient) {
if (!FeatureFlags.MESSAGE_REQUESTS && recipient.isPushGroup() && !recipient.isProfileSharing()) {
if (!FeatureFlags.messageRequests() && recipient.isPushGroup() && !recipient.isProfileSharing()) {
groupShareProfileView.get().setRecipient(recipient);
groupShareProfileView.get().setVisibility(View.VISIBLE);
} else if (groupShareProfileView.resolved()) {
Expand Down
Expand Up @@ -713,7 +713,7 @@ public void onLoadFinished(@NonNull Loader<Cursor> cursorLoader, Cursor cursor)
setLastSeen(loader.getLastSeen());
}

if (FeatureFlags.MESSAGE_REQUESTS) {
if (FeatureFlags.messageRequests()) {
if (!loader.hasSent() && !recipient.get().isSystemContact() && !recipient.get().isProfileSharing() && !recipient.get().isBlocked() && recipient.get().isRegistered()) {
listener.onMessageRequest();
} else {
Expand Down Expand Up @@ -994,8 +994,8 @@ public void onItemLongClick(View maskTarget, MessageRecord messageRecord) {

if (actionMode != null) return;

if (FeatureFlags.REACTION_SENDING &&
messageRecord.isSecure() &&
if (FeatureFlags.reactionSending() &&
messageRecord.isSecure() &&
((ConversationAdapter) list.getAdapter()).getSelectedItems().isEmpty())
{
isReacting = true;
Expand Down
Expand Up @@ -960,7 +960,7 @@ private boolean shouldInterceptClicks(MessageRecord messageRecord) {
private void setGroupMessageStatus(MessageRecord messageRecord, Recipient recipient) {
if (groupThread && !messageRecord.isOutgoing()) {

if (FeatureFlags.PROFILE_DISPLAY) {
if (FeatureFlags.profileDisplay()) {
this.groupSender.setText(recipient.getDisplayName(getContext()));
this.groupSenderProfileName.setVisibility(View.GONE);
} else {
Expand Down
Expand Up @@ -126,7 +126,7 @@ private void setComposeTitle() {
}

private void setRecipientTitle(Recipient recipient) {
if (FeatureFlags.PROFILE_DISPLAY) {
if (FeatureFlags.profileDisplay()) {
if (recipient.isGroup()) setGroupRecipientTitle(recipient);
else if (recipient.isLocalNumber()) setSelfTitle();
else setIndividualRecipientTitle(recipient);
Expand Down Expand Up @@ -166,7 +166,7 @@ private void setContactRecipientTitle(Recipient recipient) {
private void setGroupRecipientTitle(Recipient recipient) {
String localNumber = TextSecurePreferences.getLocalNumber(getContext());

if (FeatureFlags.PROFILE_DISPLAY) {
if (FeatureFlags.profileDisplay()) {
this.title.setText(recipient.getDisplayName(getContext()));
} else {
this.title.setText(recipient.getName(getContext()));
Expand Down
Expand Up @@ -1209,7 +1209,7 @@ public void clearDirtyState(@NonNull List<RecipientId> recipients) {
}

void markDirty(@NonNull RecipientId recipientId, @NonNull DirtyState dirtyState) {
if (!FeatureFlags.STORAGE_SERVICE) return;
if (!FeatureFlags.storageService()) return;

ContentValues contentValues = new ContentValues(1);
contentValues.put(DIRTY, dirtyState.getId());
Expand Down
Expand Up @@ -69,7 +69,7 @@ public static synchronized void init(@NonNull Application application, @NonNull
}

public static synchronized @NonNull KeyBackupService getKeyBackupService() {
if (!FeatureFlags.KBS) throw new AssertionError();
if (!FeatureFlags.kbs()) throw new AssertionError();
return getSignalServiceAccountManager().getKeyBackupService(IasKeyStore.getIasKeyStore(application),
BuildConfig.KEY_BACKUP_ENCLAVE_NAME,
BuildConfig.KEY_BACKUP_MRENCLAVE,
Expand Down
Expand Up @@ -75,6 +75,7 @@ public static Map<String, Job.Factory> getJobFactories(@NonNull Application appl
put(RefreshAttributesJob.KEY, new RefreshAttributesJob.Factory());
put(RefreshOwnProfileJob.KEY, new RefreshOwnProfileJob.Factory());
put(RefreshPreKeysJob.KEY, new RefreshPreKeysJob.Factory());
put(RemoteConfigRefreshJob.KEY, new RemoteConfigRefreshJob.Factory());
put(RequestGroupInfoJob.KEY, new RequestGroupInfoJob.Factory());
put(RetrieveProfileAvatarJob.KEY, new RetrieveProfileAvatarJob.Factory());
put(RetrieveProfileJob.KEY, new RetrieveProfileJob.Factory());
Expand Down
@@ -0,0 +1,66 @@
package org.thoughtcrime.securesms.jobs;

import androidx.annotation.NonNull;

import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.jobmanager.Data;
import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.util.FeatureFlags;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

public class RemoteConfigRefreshJob extends BaseJob {

public static final String KEY = "RemoteConfigRefreshJob";

public RemoteConfigRefreshJob() {
this(new Job.Parameters.Builder()
.setQueue("RemoteConfigRefreshJob")
.setMaxInstances(1)
.setMaxAttempts(Parameters.UNLIMITED)
.setLifespan(TimeUnit.DAYS.toMillis(1))
.build());
}

private RemoteConfigRefreshJob(@NonNull Parameters parameters) {
super(parameters);
}

@Override
public @NonNull Data serialize() {
return Data.EMPTY;
}

@Override
public @NonNull String getFactoryKey() {
return KEY;
}

@Override
protected void onRun() throws Exception {
Map<String, Boolean> config = ApplicationDependencies.getSignalServiceAccountManager().getRemoteConfig();
FeatureFlags.updateDiskCache(config);
SignalStore.setRemoteConfigLastFetchTime(System.currentTimeMillis());
}

@Override
protected boolean onShouldRetry(@NonNull Exception e) {
return e instanceof PushNetworkException;
}

@Override
public void onFailure() {
}

public static final class Factory implements Job.Factory<RemoteConfigRefreshJob> {
@Override
public @NonNull RemoteConfigRefreshJob create(@NonNull Parameters parameters, @NonNull Data data) {
return new RemoteConfigRefreshJob(parameters);
}
}
}
Expand Up @@ -102,7 +102,7 @@ private void handlePhoneNumberRecipient(Recipient recipient) throws IOException

setProfileName(recipient, profile.getName());
setProfileAvatar(recipient, profile.getAvatar());
if (FeatureFlags.USERNAMES) setUsername(recipient, profile.getUsername());
if (FeatureFlags.usernames()) setUsername(recipient, profile.getUsername());
setProfileCapabilities(recipient, profile.getCapabilities());
setIdentityKey(recipient, profile.getIdentityKey());
setUnidentifiedAccessMode(recipient, profile.getUnidentifiedAccess(), profile.isUnrestrictedUnidentifiedAccess());
Expand Down
Expand Up @@ -67,7 +67,7 @@ private StorageForcePushJob(@NonNull Parameters parameters) {

@Override
protected void onRun() throws IOException, RetryLaterException {
if (!FeatureFlags.STORAGE_SERVICE) throw new AssertionError();
if (!FeatureFlags.storageService()) throw new AssertionError();

MasterKey kbsMasterKey = SignalStore.kbsValues().getPinBackedMasterKey();

Expand Down
Expand Up @@ -79,7 +79,7 @@ private StorageSyncJob(@NonNull Parameters parameters) {

@Override
protected void onRun() throws IOException, RetryLaterException {
if (!FeatureFlags.STORAGE_SERVICE) throw new AssertionError();
if (!FeatureFlags.storageService()) throw new AssertionError();

try {
boolean needsMultiDeviceSync = performSync();
Expand Down
@@ -1,5 +1,7 @@
package org.thoughtcrime.securesms.keyvalue;

import android.content.Context;

import androidx.annotation.NonNull;

import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
Expand All @@ -10,12 +12,32 @@
*/
public final class SignalStore {

private static final String REMOTE_CONFIG = "remote_config";
private static final String REMOTE_CONFIG_LAST_FETCH_TIME = "remote_config_last_fetch_time";

private SignalStore() {}

public static KbsValues kbsValues() {
return new KbsValues(getStore());
}

public static String getRemoteConfig() {
return getStore().getString(REMOTE_CONFIG, null);
}

public static void setRemoteConfig(String value) {
putString(REMOTE_CONFIG, value);
}

public static long getRemoteConfigLastFetchTime() {
return getStore().getLong(REMOTE_CONFIG_LAST_FETCH_TIME, 0);
}

public static void setRemoteConfigLastFetchTime(long time) {
putLong(REMOTE_CONFIG_LAST_FETCH_TIME, time);
}


/**
* Ensures any pending writes are finished. Only intended to be called by
* {@link SignalUncaughtExceptionHandler}.
Expand Down
Expand Up @@ -126,7 +126,7 @@ private static TextWatcher getV1PinWatcher(@NonNull Context context, AlertDialog
dialog.dismiss();
RegistrationLockReminders.scheduleReminder(context, true);

if (FeatureFlags.KBS) {
if (FeatureFlags.kbs()) {
Log.i(TAG, "Pin V1 successfully remembered, scheduling a migration to V2");
ApplicationDependencies.getJobManager().add(new RegistrationPinV2MigrationJob());
}
Expand Down Expand Up @@ -201,7 +201,7 @@ protected void onPreExecute() {
@Override
protected Boolean doInBackground(Void... voids) {
try {
if (!FeatureFlags.KBS) {
if (!FeatureFlags.kbs()) {
Log.i(TAG, "Setting V1 pin");
SignalServiceAccountManager accountManager = ApplicationDependencies.getSignalServiceAccountManager();
accountManager.setPin(pinValue);
Expand Down Expand Up @@ -282,7 +282,7 @@ protected void onPreExecute() {
@Override
protected Boolean doInBackground(Void... voids) {
try {
if (!FeatureFlags.KBS) {
if (!FeatureFlags.kbs()) {
Log.i(TAG, "Removing v1 registration lock pin from server");
ApplicationDependencies.getSignalServiceAccountManager().removeV1Pin();
} else {
Expand Down

0 comments on commit 55e9f87

Please sign in to comment.