Reimplement Phase 0 for SMS removal.

This commit is contained in:
Cody Henthorne
2022-10-20 15:50:38 -04:00
committed by Greyson Parrelli
parent 690e1e60ba
commit a535b4f97c
21 changed files with 107 additions and 42 deletions

View File

@@ -70,7 +70,7 @@ public abstract class ContactSelectionActivity extends PassphraseRequiredActivit
@Override
protected void onCreate(Bundle icicle, boolean ready) {
if (!getIntent().hasExtra(ContactSelectionListFragment.DISPLAY_MODE)) {
boolean includeSms = Util.isDefaultSmsProvider(this) && SignalStore.misc().getSmsExportPhase().isSmsSupported();
boolean includeSms = SignalStore.misc().getSmsExportPhase().allowSmsFeatures();
int displayMode = includeSms ? DisplayMode.FLAG_ALL : DisplayMode.FLAG_PUSH | DisplayMode.FLAG_ACTIVE_GROUPS | DisplayMode.FLAG_INACTIVE_GROUPS | DisplayMode.FLAG_SELF;
getIntent().putExtra(ContactSelectionListFragment.DISPLAY_MODE, displayMode);
}

View File

@@ -106,7 +106,7 @@ public class NewConversationActivity extends ContactSelectionActivity
@Override
public void onBeforeContactSelected(@NonNull Optional<RecipientId> recipientId, String number, @NonNull Consumer<Boolean> callback) {
boolean smsSupported = Util.isDefaultSmsProvider(this) && SignalStore.misc().getSmsExportPhase().isSmsSupported();
boolean smsSupported = SignalStore.misc().getSmsExportPhase().allowSmsFeatures();
if (recipientId.isPresent()) {
launch(Recipient.resolved(recipientId.get()));
@@ -283,7 +283,7 @@ public class NewConversationActivity extends ContactSelectionActivity
return null;
}
if (recipient.isRegistered() || (Util.isDefaultSmsProvider(this) && SignalStore.misc().getSmsExportPhase().isSmsSupported())) {
if (recipient.isRegistered() || (SignalStore.misc().getSmsExportPhase().allowSmsFeatures())) {
return new ActionItem(
R.drawable.ic_phone_right_24,
getString(R.string.NewConversationActivity__audio_call),

View File

@@ -13,7 +13,6 @@ import org.thoughtcrime.securesms.components.menu.ActionItem
import org.thoughtcrime.securesms.components.menu.SignalContextMenu
import org.thoughtcrime.securesms.conversation.MessageSendType
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.util.Util
import org.thoughtcrime.securesms.util.ViewUtil
import java.lang.AssertionError
import java.util.concurrent.CopyOnWriteArrayList
@@ -157,7 +156,7 @@ class SendButton(context: Context, attributeSet: AttributeSet?) : AppCompatImage
}
if (availableSendTypes.size == 1) {
return if (!Util.isDefaultSmsProvider(context) || !SignalStore.misc().smsExportPhase.isSmsSupported()) {
return if (!SignalStore.misc().smsExportPhase.allowSmsFeatures()) {
Snackbar.make(snackbarContainer, R.string.InputPanel__sms_messaging_is_no_longer_supported_in_signal, Snackbar.LENGTH_SHORT).show()
true
} else {

View File

@@ -14,6 +14,7 @@ import org.thoughtcrime.securesms.components.settings.app.chats.sms.SmsExportSta
import org.thoughtcrime.securesms.components.settings.configure
import org.thoughtcrime.securesms.exporter.flow.SmsExportActivity
import org.thoughtcrime.securesms.exporter.flow.SmsExportDialogs
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.util.adapter.mapping.MappingAdapter
import org.thoughtcrime.securesms.util.navigation.safeNavigate
@@ -45,7 +46,7 @@ class ChatsSettingsFragment : DSLSettingsFragment(R.string.preferences_chats__ch
private fun getConfiguration(state: ChatsSettingsState): DSLConfiguration {
return configure {
if (!state.useAsDefaultSmsApp) {
if (!state.useAsDefaultSmsApp && SignalStore.misc().smsExportPhase.isAtLeastPhase1()) {
when (state.smsExportState) {
SmsExportState.FETCHING -> Unit
SmsExportState.HAS_UNEXPORTED_MESSAGES -> {
@@ -73,9 +74,7 @@ class ChatsSettingsFragment : DSLSettingsFragment(R.string.preferences_chats__ch
SmsExportState.NO_SMS_MESSAGES_IN_DATABASE -> Unit
SmsExportState.NOT_AVAILABLE -> Unit
}
}
if (state.useAsDefaultSmsApp) {
} else {
clickPref(
title = DSLSettingsText.from(R.string.preferences__sms_mms),
onClick = {

View File

@@ -19,6 +19,8 @@ import org.thoughtcrime.securesms.components.settings.models.OutlinedLearnMore
import org.thoughtcrime.securesms.exporter.flow.SmsExportActivity
import org.thoughtcrime.securesms.exporter.flow.SmsExportDialogs
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.keyvalue.SmsExportPhase
import org.thoughtcrime.securesms.util.SmsUtil
import org.thoughtcrime.securesms.util.Util
import org.thoughtcrime.securesms.util.adapter.mapping.MappingAdapter
import org.thoughtcrime.securesms.util.navigation.safeNavigate
@@ -56,14 +58,16 @@ class SmsSettingsFragment : DSLSettingsFragment(R.string.preferences__sms_mms) {
SignalStore.settings().setDefaultSms(true)
} else {
SignalStore.settings().setDefaultSms(false)
findNavController().navigateUp()
if (SignalStore.misc().smsExportPhase.isAtLeastPhase1()) {
findNavController().navigateUp()
}
}
}
private fun getConfiguration(state: SmsSettingsState): DSLConfiguration {
return configure {
if (state.useAsDefaultSmsApp) {
if (state.useAsDefaultSmsApp && SignalStore.misc().smsExportPhase.isAtLeastPhase1()) {
customPref(
OutlinedLearnMore.Model(
summary = DSLSettingsText.from(R.string.SmsSettingsFragment__sms_support_will_be_removed_soon_to_focus_on_encrypted_messaging),
@@ -100,13 +104,17 @@ class SmsSettingsFragment : DSLSettingsFragment(R.string.preferences__sms_mms) {
SmsExportState.NOT_AVAILABLE -> Unit
}
if (state.useAsDefaultSmsApp) {
if (state.useAsDefaultSmsApp || SignalStore.misc().smsExportPhase == SmsExportPhase.PHASE_0) {
@Suppress("DEPRECATION")
clickPref(
title = DSLSettingsText.from(R.string.SmsSettingsFragment__use_as_default_sms_app),
summary = DSLSettingsText.from(R.string.arrays__enabled),
summary = DSLSettingsText.from(if (state.useAsDefaultSmsApp) R.string.arrays__enabled else R.string.arrays__disabled),
onClick = {
startDefaultAppSelectionIntent()
if (state.useAsDefaultSmsApp) {
startDefaultAppSelectionIntent()
} else {
startActivityForResult(SmsUtil.getSmsRoleIntent(requireContext()), SMS_REQUEST_CODE.toInt())
}
}
)
}

View File

@@ -18,7 +18,6 @@ import org.thoughtcrime.securesms.groups.SelectionLimits
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.recipients.RecipientId
import org.thoughtcrime.securesms.util.LifecycleDisposable
import org.thoughtcrime.securesms.util.Util
import org.thoughtcrime.securesms.util.ViewUtil
import org.thoughtcrime.securesms.util.views.CircularProgressMaterialButton
import java.util.Optional
@@ -106,7 +105,7 @@ class SelectRecipientsFragment : LoggingFragment(), ContactSelectionListFragment
ContactsCursorLoader.DisplayMode.FLAG_HIDE_RECENT_HEADER or
ContactsCursorLoader.DisplayMode.FLAG_GROUPS_AFTER_CONTACTS
if (Util.isDefaultSmsProvider(requireContext()) && SignalStore.misc().smsExportPhase.isSmsSupported()) {
if (SignalStore.misc().smsExportPhase.allowSmsFeatures()) {
mode = mode or ContactsCursorLoader.DisplayMode.FLAG_SMS
}

View File

@@ -16,7 +16,6 @@ import org.thoughtcrime.securesms.components.settings.conversation.preferences.L
import org.thoughtcrime.securesms.database.AttachmentDatabase
import org.thoughtcrime.securesms.database.RecipientDatabase
import org.thoughtcrime.securesms.database.model.StoryViewState
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import org.thoughtcrime.securesms.groups.GroupId
import org.thoughtcrime.securesms.groups.LiveGroup
import org.thoughtcrime.securesms.groups.v2.GroupAddMembersResult
@@ -26,7 +25,6 @@ import org.thoughtcrime.securesms.recipients.RecipientId
import org.thoughtcrime.securesms.recipients.RecipientUtil
import org.thoughtcrime.securesms.util.FeatureFlags
import org.thoughtcrime.securesms.util.SingleLiveEvent
import org.thoughtcrime.securesms.util.Util
import org.thoughtcrime.securesms.util.livedata.LiveDataUtil
import org.thoughtcrime.securesms.util.livedata.Store
import java.util.Optional
@@ -141,7 +139,7 @@ sealed class ConversationSettingsViewModel(
}
store.update(liveRecipient.liveData) { recipient, state ->
val isAudioAvailable = (recipient.isRegistered || (Util.isDefaultSmsProvider(ApplicationDependencies.getApplication()) && SignalStore.misc().smsExportPhase.isSmsSupported())) &&
val isAudioAvailable = (recipient.isRegistered || SignalStore.misc().smsExportPhase.allowSmsFeatures()) &&
!recipient.isGroup &&
!recipient.isBlocked &&
!recipient.isSelf &&

View File

@@ -929,7 +929,7 @@ public class ConversationParentFragment extends Fragment
if (isSingleConversation()) {
if (viewModel.isPushAvailable()) {
inflater.inflate(R.menu.conversation_callable_secure, menu);
} else if (!recipient.get().isReleaseNotes() && Util.isDefaultSmsProvider(requireContext()) && SignalStore.misc().getSmsExportPhase().isSmsSupported()) {
} else if (!recipient.get().isReleaseNotes() && SignalStore.misc().getSmsExportPhase().allowSmsFeatures()) {
inflater.inflate(R.menu.conversation_callable_insecure, menu);
}
} else if (isGroupConversation()) {
@@ -2742,7 +2742,14 @@ public class ConversationParentFragment extends Fragment
TextView message = smsExportStub.get().findViewById(R.id.export_sms_message);
MaterialButton actionButton = smsExportStub.get().findViewById(R.id.export_sms_button);
boolean isPhase1 = SignalStore.misc().getSmsExportPhase() == SmsExportPhase.PHASE_1;
if (conversationSecurityInfo.getHasUnexportedInsecureMessages()) {
if (SignalStore.misc().getSmsExportPhase() == SmsExportPhase.PHASE_0) {
message.setText(getString(R.string.NewConversationActivity__s_is_not_a_signal_user, recipient.getDisplayName(requireContext())));
actionButton.setText(R.string.conversation_activity__enable_signal_for_sms);
actionButton.setOnClickListener(v -> {
handleMakeDefaultSms();
});
} else if (conversationSecurityInfo.getHasUnexportedInsecureMessages()) {
message.setText(isPhase1 ? R.string.ConversationActivity__sms_messaging_is_currently_disabled_you_can_export_your_messages_to_another_app_on_your_phone
: R.string.ConversationActivity__sms_messaging_is_no_longer_supported_in_signal_you_can_export_your_messages_to_another_app_on_your_phone);
actionButton.setText(R.string.ConversationActivity__export_sms_messages);

View File

@@ -31,6 +31,7 @@ import org.thoughtcrime.securesms.database.DatabaseObserver;
import org.thoughtcrime.securesms.database.model.MessageId;
import org.thoughtcrime.securesms.database.model.StoryViewState;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.mediasend.Media;
import org.thoughtcrime.securesms.mediasend.MediaRepository;
import org.thoughtcrime.securesms.notifications.profiles.NotificationProfile;
@@ -443,7 +444,9 @@ public class ConversationViewModel extends ViewModel {
}
public void insertSmsExportUpdateEvent(@NonNull Recipient recipient) {
conversationRepository.insertSmsExportUpdateEvent(recipient);
if (SignalStore.misc().getSmsExportPhase().isAtLeastPhase1()) {
conversationRepository.insertSmsExportUpdateEvent(recipient);
}
}
enum Event {

View File

@@ -14,7 +14,6 @@ import org.thoughtcrime.securesms.util.CharacterCalculator
import org.thoughtcrime.securesms.util.MmsCharacterCalculator
import org.thoughtcrime.securesms.util.PushCharacterCalculator
import org.thoughtcrime.securesms.util.SmsCharacterCalculator
import org.thoughtcrime.securesms.util.Util
import org.thoughtcrime.securesms.util.dualsim.SubscriptionInfoCompat
import org.thoughtcrime.securesms.util.dualsim.SubscriptionManagerCompat
import java.lang.IllegalArgumentException
@@ -140,7 +139,7 @@ sealed class MessageSendType(
options += SignalMessageSendType
if (Util.isDefaultSmsProvider(context) && SignalStore.misc().smsExportPhase.isSmsSupported()) {
if (SignalStore.misc().smsExportPhase.allowSmsFeatures()) {
try {
val subscriptions: Collection<SubscriptionInfoCompat> = SubscriptionManagerCompat(context).activeAndReadySubscriptionInfos

View File

@@ -51,7 +51,7 @@ public class CreateGroupActivity extends ContactSelectionActivity {
intent.putExtra(ContactSelectionListFragment.REFRESHABLE, false);
intent.putExtra(ContactSelectionActivity.EXTRA_LAYOUT_RES_ID, R.layout.create_group_activity);
boolean smsEnabled = Util.isDefaultSmsProvider(context) && SignalStore.misc().getSmsExportPhase().isSmsSupported();
boolean smsEnabled = SignalStore.misc().getSmsExportPhase().allowSmsFeatures();
int displayMode = smsEnabled ? ContactsCursorLoader.DisplayMode.FLAG_SMS | ContactsCursorLoader.DisplayMode.FLAG_PUSH
: ContactsCursorLoader.DisplayMode.FLAG_PUSH;

View File

@@ -100,6 +100,7 @@ public class AddGroupDetailsFragment extends LoggingFragment {
GroupMemberListView members = view.findViewById(R.id.member_list);
ImageView avatar = view.findViewById(R.id.group_avatar);
View mmsWarning = view.findViewById(R.id.mms_warning);
TextView mmsWarningText = view.findViewById(R.id.mms_warning_text);
View addLater = view.findViewById(R.id.add_later);
TextView disappearingMessageValue = view.findViewById(R.id.group_disappearing_messages_value);
@@ -127,6 +128,8 @@ public class AddGroupDetailsFragment extends LoggingFragment {
viewModel.getIsMms().observe(getViewLifecycleOwner(), isMms -> {
disappearingMessagesRow.setVisibility(isMms ? View.GONE : View.VISIBLE);
mmsWarning.setVisibility(isMms ? View.VISIBLE : View.GONE);
mmsWarningText.setText(SignalStore.misc().getSmsExportPhase().isAtLeastPhase1() ? R.string.AddGroupDetailsFragment__youve_selected_a_contact_that_doesnt_support_signal_groups_mms_removal
: R.string.AddGroupDetailsFragment__youve_selected_a_contact_that_doesnt_support);
name.setHint(isMms ? R.string.AddGroupDetailsFragment__group_name_optional : R.string.AddGroupDetailsFragment__group_name_required);
toolbar.setTitle(isMms ? R.string.AddGroupDetailsFragment__create_group : R.string.AddGroupDetailsFragment__name_this_group);
});

View File

@@ -26,7 +26,8 @@ public final class MiscellaneousValues extends SignalStoreValues {
private static final String LAST_FCM_FOREGROUND_TIME = "misc.last_fcm_foreground_time";
private static final String LAST_FOREGROUND_TIME = "misc.last_foreground_time";
private static final String PNI_INITIALIZED_DEVICES = "misc.pni_initialized_devices";
private static final String SMS_EXPORT_TIME = "misc.sms_export_time";
private static final String SMS_PHASE_1_START_MS = "misc.sms_export.phase_1_start";
private static final String STORIES_FEATURE_AVAILABLE_MS = "misc.stories_feature_available_ms";
MiscellaneousValues(@NonNull KeyValueStore store) {
super(store);
@@ -40,7 +41,8 @@ public final class MiscellaneousValues extends SignalStoreValues {
@Override
@NonNull List<String> getKeysToIncludeInBackup() {
return Arrays.asList(
SMS_EXPORT_TIME
SMS_PHASE_1_START_MS,
STORIES_FEATURE_AVAILABLE_MS
);
}
@@ -188,14 +190,26 @@ public final class MiscellaneousValues extends SignalStoreValues {
putBoolean(PNI_INITIALIZED_DEVICES, value);
}
public void setHasSeenSmsExportMegaphone() {
if (!getStore().containsKey(SMS_EXPORT_TIME)) {
putLong(SMS_EXPORT_TIME, System.currentTimeMillis());
public void startSmsPhase1() {
if (!getStore().containsKey(SMS_PHASE_1_START_MS)) {
putLong(SMS_PHASE_1_START_MS, System.currentTimeMillis());
}
}
public long getStoriesFeatureAvailableTimestamp() {
return getLong(STORIES_FEATURE_AVAILABLE_MS, 0);
}
public void setStoriesFeatureAvailableTimestamp(long timestamp) {
putLong(STORIES_FEATURE_AVAILABLE_MS, timestamp);
}
public @NonNull SmsExportPhase getSmsExportPhase() {
if (getLong(SMS_PHASE_1_START_MS, 0) == 0) {
return SmsExportPhase.PHASE_0;
}
long now = System.currentTimeMillis();
return SmsExportPhase.getCurrentPhase(now - getLong(SMS_EXPORT_TIME, now));
return SmsExportPhase.getCurrentPhase(now - getLong(SMS_PHASE_1_START_MS, now));
}
}

View File

@@ -1,24 +1,35 @@
package org.thoughtcrime.securesms.keyvalue
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import org.thoughtcrime.securesms.util.Util
import kotlin.time.Duration.Companion.days
enum class SmsExportPhase(val duration: Long) {
PHASE_0(-1),
PHASE_1(0.days.inWholeMilliseconds),
PHASE_2(45.days.inWholeMilliseconds),
PHASE_3(105.days.inWholeMilliseconds);
fun allowSmsFeatures(): Boolean {
return this == PHASE_0 || (Util.isDefaultSmsProvider(ApplicationDependencies.getApplication()) && SignalStore.misc().smsExportPhase.isSmsSupported())
}
fun isSmsSupported(): Boolean {
return this != PHASE_3
}
fun isFullscreen(): Boolean {
return this != PHASE_1
return this.ordinal > PHASE_1.ordinal
}
fun isBlockingUi(): Boolean {
return this == PHASE_3
}
fun isAtLeastPhase1(): Boolean {
return this.ordinal >= PHASE_1.ordinal
}
companion object {
@JvmStatic
fun getCurrentPhase(duration: Long): SmsExportPhase {

View File

@@ -362,14 +362,16 @@ public final class Megaphones {
private static @NonNull Megaphone buildSmsExportMegaphone(@NonNull Context context) {
SmsExportPhase phase = SignalStore.misc().getSmsExportPhase();
if (phase == SmsExportPhase.PHASE_1) {
if (phase == SmsExportPhase.PHASE_0) {
throw new AssertionError("Should not be showing megaphone for Phase 0");
} else if (phase == SmsExportPhase.PHASE_1) {
return new Megaphone.Builder(Event.SMS_EXPORT, Megaphone.Style.BASIC)
.setTitle(R.string.SmsExportMegaphone__sms_support_going_away)
.setImage(R.drawable.sms_megaphone)
.setBody(R.string.SmsExportMegaphone__sms_support_will_be_removed_soon_to_focus_on_encrypted_messaging)
.setActionButton(R.string.SmsExportMegaphone__export_sms, (megaphone, controller) -> controller.onMegaphoneNavigationRequested(SmsExportActivity.createIntent(context), SmsExportMegaphoneActivity.REQUEST_CODE))
.setSecondaryButton(R.string.Megaphones_remind_me_later, (megaphone, controller) -> controller.onMegaphoneSnooze(Event.SMS_EXPORT))
.setOnVisibleListener((megaphone, controller) -> SignalStore.misc().setHasSeenSmsExportMegaphone())
.setOnVisibleListener((megaphone, controller) -> SignalStore.misc().startSmsPhase1())
.build();
} else {
Megaphone.Builder builder = new Megaphone.Builder(Event.SMS_EXPORT, Megaphone.Style.FULLSCREEN)

View File

@@ -2,9 +2,9 @@ package org.thoughtcrime.securesms.megaphone
import android.content.Context
import androidx.annotation.WorkerThread
import org.thoughtcrime.securesms.database.SignalDatabase.Companion.mmsSms
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.keyvalue.SmsExportPhase
import org.thoughtcrime.securesms.stories.Stories
import org.thoughtcrime.securesms.util.FeatureFlags
import org.thoughtcrime.securesms.util.Util
import kotlin.time.Duration.Companion.days
@@ -23,6 +23,7 @@ class SmsExportReminderSchedule(private val context: Context) : MegaphoneSchedul
override fun shouldDisplay(seenCount: Int, lastSeen: Long, firstVisible: Long, currentTime: Long): Boolean {
return if (shouldShowMegaphone()) {
when (SignalStore.misc().smsExportPhase) {
SmsExportPhase.PHASE_0 -> false
SmsExportPhase.PHASE_1 -> basicMegaphoneSchedule.shouldDisplay(seenCount, lastSeen, firstVisible, currentTime)
SmsExportPhase.PHASE_2 -> fullScreenSchedule.shouldDisplay(seenCount, lastSeen, firstVisible, currentTime)
SmsExportPhase.PHASE_3 -> showPhase3Megaphone
@@ -32,8 +33,17 @@ class SmsExportReminderSchedule(private val context: Context) : MegaphoneSchedul
}
}
@Suppress("UsePropertyAccessSyntax")
@WorkerThread
fun shouldShowMegaphone(): Boolean {
return FeatureFlags.smsExporter() && (Util.isDefaultSmsProvider(context) || mmsSms.unexportedInsecureMessagesCount > 0)
return if (Stories.isFeatureFlagEnabled() && SignalStore.misc().storiesFeatureAvailableTimestamp == 0L) {
SignalStore.misc().storiesFeatureAvailableTimestamp = System.currentTimeMillis()
false
} else if (System.currentTimeMillis() > (SignalStore.misc().storiesFeatureAvailableTimestamp + FeatureFlags.smsExportMegaphoneDelayDays().days.inWholeMilliseconds)) {
SignalStore.misc().startSmsPhase1()
FeatureFlags.smsExporter() && Util.isDefaultSmsProvider(context)
} else {
false
}
}
}

View File

@@ -222,7 +222,7 @@ public final class RecipientBottomSheetDialogFragment extends BottomSheetDialogF
unblockButton.setVisibility(View.GONE);
}
boolean isAudioAvailable = (recipient.isRegistered() || (Util.isDefaultSmsProvider(requireContext()) && SignalStore.misc().getSmsExportPhase().isSmsSupported())) &&
boolean isAudioAvailable = (recipient.isRegistered() || SignalStore.misc().getSmsExportPhase().allowSmsFeatures()) &&
!recipient.isGroup() &&
!recipient.isBlocked() &&
!recipient.isSelf() &&

View File

@@ -104,7 +104,8 @@ public final class FeatureFlags {
private static final String CDS_V2_COMPAT = "android.cdsV2Compat.4";
public static final String STORIES_LOCALE = "android.stories.locale";
private static final String HIDE_CONTACTS = "android.hide.contacts";
public static final String MEDIA_PREVIEW_V2 = "android.mediaPreviewV2";
private static final String MEDIA_PREVIEW_V2 = "android.mediaPreviewV2";
private static final String SMS_EXPORT_MEGAPHONE_DELAY_DAYS = "android.smsExport.megaphoneDelayDays";
/**
* We will only store remote values for flags in this set. If you want a flag to be controllable
@@ -161,7 +162,8 @@ public final class FeatureFlags {
CDS_V2_COMPAT,
STORIES_LOCALE,
HIDE_CONTACTS,
MEDIA_PREVIEW_V2
MEDIA_PREVIEW_V2,
SMS_EXPORT_MEGAPHONE_DELAY_DAYS
);
@VisibleForTesting
@@ -225,7 +227,8 @@ public final class FeatureFlags {
CDS_V2_LOAD_TEST,
CDS_V2_COMPAT,
STORIES,
MEDIA_PREVIEW_V2
MEDIA_PREVIEW_V2,
SMS_EXPORT_MEGAPHONE_DELAY_DAYS
);
/**
@@ -585,6 +588,13 @@ public final class FeatureFlags {
return getBoolean(MEDIA_PREVIEW_V2, false);
}
/**
* Number of days to postpone the sms export megaphone and Phase 1 start.
*/
public static int smsExportMegaphoneDelayDays() {
return getInteger(SMS_EXPORT_MEGAPHONE_DELAY_DAYS, 14);
}
/** Only for rendering debug info. */
public static synchronized @NonNull Map<String, Object> getMemoryValues() {
return new TreeMap<>(REMOTE_VALUES);

View File

@@ -30,7 +30,7 @@ public class Stub<T extends View> {
}
public void setVisibility(int visibility) {
if (resolved()) {
if (resolved() || visibility == View.VISIBLE) {
get().setVisibility(visibility);
}
}