diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a939785036..298775a112 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -523,6 +523,11 @@ + + diff --git a/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java b/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java index fbb8a4e804..b1a13a1eb7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java +++ b/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java @@ -198,7 +198,7 @@ public class ApplicationContext extends MultiDexApplication implements DefaultLi public void checkBuildExpiration() { if (Util.getTimeUntilBuildExpiry() <= 0 && !SignalStore.misc().isClientDeprecated()) { Log.w(TAG, "Build expired!"); - SignalStore.misc().markDeprecated(); + SignalStore.misc().markClientDeprecated(); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/reminder/ExpiredBuildReminder.java b/app/src/main/java/org/thoughtcrime/securesms/components/reminder/ExpiredBuildReminder.java index 5a3b911d8e..b5089cf4b5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/reminder/ExpiredBuildReminder.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/reminder/ExpiredBuildReminder.java @@ -2,16 +2,25 @@ package org.thoughtcrime.securesms.components.reminder; import android.content.Context; +import androidx.annotation.NonNull; + import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.keyvalue.SignalStore; import org.thoughtcrime.securesms.util.PlayStoreUtil; +import java.util.List; + +/** + * Showed when a build has fully expired (either via the compile-time constant, or remote + * deprecation). + */ public class ExpiredBuildReminder extends Reminder { public ExpiredBuildReminder(final Context context) { - super(context.getString(R.string.reminder_header_expired_build), - context.getString(R.string.reminder_header_expired_build_details)); + super(null, context.getString(R.string.ExpiredBuildReminder_this_version_of_signal_has_expired)); + setOkListener(v -> PlayStoreUtil.openPlayStoreOrOurApkDownloadPage(context)); + addAction(new Action(context.getString(R.string.ExpiredBuildReminder_update_now), R.id.reminder_action_update_now)); } @Override @@ -19,8 +28,17 @@ public class ExpiredBuildReminder extends Reminder { return false; } + @Override + public List getActions() { + return super.getActions(); + } + + @Override + public @NonNull Importance getImportance() { + return Importance.TERMINAL; + } + public static boolean isEligible() { return SignalStore.misc().isClientDeprecated(); } - } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/reminder/OutdatedBuildReminder.java b/app/src/main/java/org/thoughtcrime/securesms/components/reminder/OutdatedBuildReminder.java index 3bcbecb5a5..93d4853d45 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/reminder/OutdatedBuildReminder.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/reminder/OutdatedBuildReminder.java @@ -8,20 +8,22 @@ import org.thoughtcrime.securesms.util.Util; import java.util.concurrent.TimeUnit; +/** + * Reminder that is shown when a build is getting close to expiry (either because of the + * compile-time constant, or remote deprecation). + */ public class OutdatedBuildReminder extends Reminder { public OutdatedBuildReminder(final Context context) { - super(context.getString(R.string.reminder_header_outdated_build), - getPluralsText(context)); + super(null, getPluralsText(context)); + setOkListener(v -> PlayStoreUtil.openPlayStoreOrOurApkDownloadPage(context)); + addAction(new Action(context.getString(R.string.OutdatedBuildReminder_update_now), R.id.reminder_action_update_now)); } private static CharSequence getPluralsText(final Context context) { int days = getDaysUntilExpiry() - 1; - if (days == 0) { - return context.getString(R.string.reminder_header_outdated_build_details_today); - } - return context.getResources().getQuantityString(R.plurals.reminder_header_outdated_build_details, days, days); + return context.getResources().getQuantityString(R.plurals.OutdatedBuildReminder_your_version_of_signal_will_expire_in_n_days, days, days); } @Override diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/reminder/Reminder.java b/app/src/main/java/org/thoughtcrime/securesms/components/reminder/Reminder.java index 109d9f4497..df3c946fcf 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/reminder/Reminder.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/reminder/Reminder.java @@ -58,7 +58,7 @@ public abstract class Reminder { return Importance.NORMAL; } - public void addAction(@NonNull Action action) { + protected void addAction(@NonNull Action action) { actions.add(action); } @@ -71,7 +71,7 @@ public abstract class Reminder { } public enum Importance { - NORMAL, ERROR + NORMAL, ERROR, TERMINAL } public final class Action { diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/reminder/ReminderView.java b/app/src/main/java/org/thoughtcrime/securesms/components/reminder/ReminderView.java index 2ea65728d8..a87d499854 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/reminder/ReminderView.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/reminder/ReminderView.java @@ -1,8 +1,6 @@ package org.thoughtcrime.securesms.components.reminder; -import android.annotation.TargetApi; import android.content.Context; -import android.os.Build.VERSION_CODES; import android.text.TextUtils; import android.util.AttributeSet; import android.view.LayoutInflater; @@ -19,7 +17,6 @@ import androidx.annotation.Nullable; import androidx.recyclerview.widget.RecyclerView; import org.thoughtcrime.securesms.R; -import org.thoughtcrime.securesms.util.ViewUtil; import java.util.List; @@ -48,7 +45,6 @@ public final class ReminderView extends FrameLayout { initialize(); } - @TargetApi(VERSION_CODES.HONEYCOMB) public ReminderView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initialize(); @@ -56,14 +52,14 @@ public final class ReminderView extends FrameLayout { private void initialize() { LayoutInflater.from(getContext()).inflate(R.layout.reminder_header, this, true); - progressBar = ViewUtil.findById(this, R.id.reminder_progress); - progressText = ViewUtil.findById(this, R.id.reminder_progress_text); - container = ViewUtil.findById(this, R.id.container); - closeButton = ViewUtil.findById(this, R.id.cancel); - title = ViewUtil.findById(this, R.id.reminder_title); - text = ViewUtil.findById(this, R.id.reminder_text); - space = ViewUtil.findById(this, R.id.reminder_space); - actionsRecycler = ViewUtil.findById(this, R.id.reminder_actions); + progressBar = findViewById(R.id.reminder_progress); + progressText = findViewById(R.id.reminder_progress_text); + container = findViewById(R.id.container); + closeButton = findViewById(R.id.cancel); + title = findViewById(R.id.reminder_title); + text = findViewById(R.id.reminder_text); + space = findViewById(R.id.reminder_space); + actionsRecycler = findViewById(R.id.reminder_actions); } public void showReminder(final Reminder reminder) { @@ -76,9 +72,26 @@ public final class ReminderView extends FrameLayout { title.setVisibility(GONE); space.setVisibility(VISIBLE); } + + if (!reminder.isDismissable()) { + space.setVisibility(GONE); + } + text.setText(reminder.getText()); - container.setBackgroundResource(reminder.getImportance() == Reminder.Importance.ERROR ? R.drawable.reminder_background_error - : R.drawable.reminder_background_normal); + + switch (reminder.getImportance()) { + case NORMAL: + container.setBackgroundResource(R.drawable.reminder_background_normal); + break; + case ERROR: + container.setBackgroundResource(R.drawable.reminder_background_error); + break; + case TERMINAL: + container.setBackgroundResource(R.drawable.reminder_background_terminal); + break; + default: + throw new IllegalStateException(); + } setOnClickListener(reminder.getOkListener()); diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java index b33b619edd..f59a7aa03a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java @@ -240,6 +240,7 @@ import org.thoughtcrime.securesms.util.FeatureFlags; import org.thoughtcrime.securesms.util.IdentityUtil; import org.thoughtcrime.securesms.util.MediaUtil; import org.thoughtcrime.securesms.util.MessageUtil; +import org.thoughtcrime.securesms.util.PlayStoreUtil; import org.thoughtcrime.securesms.util.ServiceUtil; import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.TextSecurePreferences.MediaKeyboardMode; @@ -1685,6 +1686,7 @@ public class ConversationActivity extends PassphraseRequiredActivity reminderView.get().showReminder(new UnauthorizedReminder(this)); } else if (ExpiredBuildReminder.isEligible()) { reminderView.get().showReminder(new ExpiredBuildReminder(this)); + reminderView.get().setOnActionClickListener(this::handleReminderAction); } else if (ServiceOutageReminder.isEligible(this)) { ApplicationDependencies.getJobManager().add(new ServiceOutageDetectionJob()); reminderView.get().showReminder(new ServiceOutageReminder(this)); @@ -1710,6 +1712,9 @@ public class ConversationActivity extends PassphraseRequiredActivity case R.id.reminder_action_view_insights: InsightsLauncher.showInsightsDashboard(getSupportFragmentManager()); break; + case R.id.reminder_action_update_now: + PlayStoreUtil.openPlayStoreOrOurApkDownloadPage(this); + break; default: throw new IllegalArgumentException("Unknown ID: " + reminderActionId); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java index e8021604f5..b200b30557 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java @@ -56,6 +56,7 @@ import androidx.appcompat.widget.TooltipCompat; import androidx.core.content.res.ResourcesCompat; import androidx.core.view.ViewCompat; import androidx.lifecycle.DefaultLifecycleObserver; +import androidx.lifecycle.LifecycleObserver; import androidx.lifecycle.LifecycleOwner; import androidx.lifecycle.ProcessLifecycleOwner; import androidx.lifecycle.ViewModelProviders; @@ -117,6 +118,7 @@ import org.thoughtcrime.securesms.service.KeyCachingService; import org.thoughtcrime.securesms.sms.MessageSender; import org.thoughtcrime.securesms.storage.StorageSyncHelper; import org.thoughtcrime.securesms.util.AvatarUtil; +import org.thoughtcrime.securesms.util.PlayStoreUtil; import org.thoughtcrime.securesms.util.ServiceUtil; import org.thoughtcrime.securesms.util.SnapToTopDataObserver; import org.thoughtcrime.securesms.util.StickyHeaderDecoration; @@ -176,6 +178,7 @@ public class ConversationListFragment extends MainFragment implements ActionMode private ViewGroup megaphoneContainer; private SnapToTopDataObserver snapToTopDataObserver; private Drawable archiveDrawable; + private LifecycleObserver visibilityLifecycleObserver; public static ConversationListFragment newInstance() { return new ConversationListFragment(); @@ -214,6 +217,7 @@ public class ConversationListFragment extends MainFragment implements ActionMode cameraFab.show(); reminderView.setOnDismissListener(this::updateReminders); + reminderView.setOnActionClickListener(this::onReminderAction); list.setLayoutManager(new LinearLayoutManager(requireActivity())); list.setItemAnimator(new DeleteItemAnimator()); @@ -272,6 +276,7 @@ public class ConversationListFragment extends MainFragment implements ActionMode public void onStart() { super.onStart(); ConversationFragment.prepare(requireContext()); + ProcessLifecycleOwner.get().getLifecycle().addObserver(visibilityLifecycleObserver); } @Override @@ -283,6 +288,12 @@ public class ConversationListFragment extends MainFragment implements ActionMode EventBus.getDefault().unregister(this); } + @Override + public void onStop() { + super.onStop(); + ProcessLifecycleOwner.get().getLifecycle().removeObserver(visibilityLifecycleObserver); + } + @Override public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) { menu.clear(); @@ -412,6 +423,12 @@ public class ConversationListFragment extends MainFragment implements ActionMode viewModel.onMegaphoneCompleted(event); } + private void onReminderAction(@IdRes int reminderActionId) { + if (reminderActionId == R.id.reminder_action_update_now) { + PlayStoreUtil.openPlayStoreOrOurApkDownloadPage(requireContext()); + } + } + private void hideKeyboard() { InputMethodManager imm = ServiceUtil.getInputMethodManager(requireContext()); imm.hideSoftInputFromWindow(requireView().getWindowToken(), 0); @@ -508,12 +525,12 @@ public class ConversationListFragment extends MainFragment implements ActionMode viewModel.getConversationList().observe(getViewLifecycleOwner(), this::onSubmitList); viewModel.hasNoConversations().observe(getViewLifecycleOwner(), this::updateEmptyState); - ProcessLifecycleOwner.get().getLifecycle().addObserver(new DefaultLifecycleObserver() { + visibilityLifecycleObserver = new DefaultLifecycleObserver() { @Override public void onStart(@NonNull LifecycleOwner owner) { viewModel.onVisible(); } - }); + }; } private void onSearchResultChanged(@Nullable SearchResult result) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/MiscellaneousValues.java b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/MiscellaneousValues.java index 975a114ada..1a49d01f62 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/MiscellaneousValues.java +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/MiscellaneousValues.java @@ -51,7 +51,11 @@ public final class MiscellaneousValues extends SignalStoreValues { return getBoolean(CLIENT_DEPRECATED, false); } - public void markDeprecated() { + public void markClientDeprecated() { putBoolean(CLIENT_DEPRECATED, true); } + + public void clearClientDeprecated() { + putBoolean(CLIENT_DEPRECATED, false); + } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/megaphone/ClientDeprecatedActivity.java b/app/src/main/java/org/thoughtcrime/securesms/megaphone/ClientDeprecatedActivity.java new file mode 100644 index 0000000000..7ab4bd66d5 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/megaphone/ClientDeprecatedActivity.java @@ -0,0 +1,62 @@ +package org.thoughtcrime.securesms.megaphone; + +import android.os.Bundle; + +import androidx.appcompat.app.AlertDialog; + +import org.thoughtcrime.securesms.PassphraseRequiredActivity; +import org.thoughtcrime.securesms.R; +import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; +import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme; +import org.thoughtcrime.securesms.util.DynamicTheme; +import org.thoughtcrime.securesms.util.PlayStoreUtil; +import org.thoughtcrime.securesms.util.Util; + +/** + * Shown when a users build fully expires. Controlled by {@link Megaphones.Event#CLIENT_DEPRECATED}. + */ +public class ClientDeprecatedActivity extends PassphraseRequiredActivity { + + private final DynamicTheme theme = new DynamicNoActionBarTheme(); + + @Override + protected void onCreate(Bundle savedInstanceState, boolean ready) { + setContentView(R.layout.client_deprecated_activity); + + findViewById(R.id.client_deprecated_update_button).setOnClickListener(v -> onUpdateClicked()); + findViewById(R.id.client_deprecated_dont_update_button).setOnClickListener(v -> onDontUpdateClicked()); + } + + @Override + protected void onPreCreate() { + theme.onCreate(this); + } + + @Override + protected void onResume() { + super.onResume(); + theme.onResume(this); + } + + @Override + public void onBackPressed() { + // Disabled + } + + private void onUpdateClicked() { + PlayStoreUtil.openPlayStoreOrOurApkDownloadPage(this); + } + + private void onDontUpdateClicked() { + new AlertDialog.Builder(this) + .setTitle(R.string.ClientDeprecatedActivity_warning) + .setMessage(R.string.ClientDeprecatedActivity_your_version_of_signal_has_expired_you_can_view_your_message_history) + .setPositiveButton(R.string.ClientDeprecatedActivity_dont_update, (dialog, which) -> { + ApplicationDependencies.getMegaphoneRepository().markFinished(Megaphones.Event.CLIENT_DEPRECATED, () -> { + Util.runOnMain(this::finish); + }); + }) + .setNegativeButton(android.R.string.cancel, (dialog, which) -> dialog.dismiss()) + .show(); + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/megaphone/Megaphone.java b/app/src/main/java/org/thoughtcrime/securesms/megaphone/Megaphone.java index 1b6094229a..a001cb3e36 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/megaphone/Megaphone.java +++ b/app/src/main/java/org/thoughtcrime/securesms/megaphone/Megaphone.java @@ -20,7 +20,7 @@ public class Megaphone { private final Event event; private final Style style; - private final boolean mandatory; + private final Priority priority; private final boolean canSnooze; private final int titleRes; private final int bodyRes; @@ -33,7 +33,7 @@ public class Megaphone { private Megaphone(@NonNull Builder builder) { this.event = builder.event; this.style = builder.style; - this.mandatory = builder.mandatory; + this.priority = builder.priority; this.canSnooze = builder.canSnooze; this.titleRes = builder.titleRes; this.bodyRes = builder.bodyRes; @@ -48,8 +48,8 @@ public class Megaphone { return event; } - public boolean isMandatory() { - return mandatory; + public @NonNull Priority getPriority() { + return priority; } public boolean canSnooze() { @@ -97,7 +97,7 @@ public class Megaphone { private final Event event; private final Style style; - private boolean mandatory; + private Priority priority; private boolean canSnooze; private int titleRes; private int bodyRes; @@ -111,13 +111,14 @@ public class Megaphone { public Builder(@NonNull Event event, @NonNull Style style) { this.event = event; this.style = style; + this.priority = Priority.DEFAULT; } /** * Prioritizes this megaphone over others that do not set this flag. */ - public @NonNull Builder setMandatory(boolean mandatory) { - this.mandatory = mandatory; + public @NonNull Builder setPriority(@NonNull Priority priority) { + this.priority = priority; return this; } @@ -192,6 +193,20 @@ public class Megaphone { POPUP } + enum Priority { + DEFAULT(0), HIGH(1), CLIENT_EXPIRATION(1000); + + int priorityValue; + + Priority(int priorityValue) { + this.priorityValue = priorityValue; + } + + public int getPriorityValue() { + return priorityValue; + } + } + public interface EventListener { void onEvent(@NonNull Megaphone megaphone, @NonNull MegaphoneActionController listener); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/megaphone/MegaphoneRepository.java b/app/src/main/java/org/thoughtcrime/securesms/megaphone/MegaphoneRepository.java index 95ff61eba0..a0d4c73c8a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/megaphone/MegaphoneRepository.java +++ b/app/src/main/java/org/thoughtcrime/securesms/megaphone/MegaphoneRepository.java @@ -5,6 +5,7 @@ import android.content.Context; import androidx.annotation.AnyThread; import androidx.annotation.MainThread; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.annotation.WorkerThread; import com.annimon.stream.Collectors; @@ -100,6 +101,11 @@ public class MegaphoneRepository { @AnyThread public void markFinished(@NonNull Event event) { + markFinished(event, null); + } + + @AnyThread + public void markFinished(@NonNull Event event, @Nullable Runnable onComplete) { executor.execute(() -> { MegaphoneRecord record = databaseCache.get(event); if (record != null && record.isFinished()) { @@ -108,6 +114,10 @@ public class MegaphoneRepository { database.markFinished(event); resetDatabaseCache(); + + if (onComplete != null) { + onComplete.run(); + } }); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/megaphone/Megaphones.java b/app/src/main/java/org/thoughtcrime/securesms/megaphone/Megaphones.java index 0721ae3b08..4f5766a62c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/megaphone/Megaphones.java +++ b/app/src/main/java/org/thoughtcrime/securesms/megaphone/Megaphones.java @@ -34,12 +34,12 @@ import java.util.Objects; * Creating a new megaphone: * - Add an enum to {@link Event} * - Return a megaphone in {@link #forRecord(Context, MegaphoneRecord)} - * - Include the event in {@link #buildDisplayOrder()} + * - Include the event in {@link #buildDisplayOrder(Context)} * * Common patterns: * - For events that have a snooze-able recurring display schedule, use a {@link RecurringSchedule}. * - For events guarded by feature flags, set a {@link ForeverSchedule} with false in - * {@link #buildDisplayOrder()}. + * {@link #buildDisplayOrder(Context)}. * - For events that change, return different megaphones in {@link #forRecord(Context, MegaphoneRecord)} * based on whatever properties you're interested in. */ @@ -65,15 +65,9 @@ public final class Megaphones { .map(Map.Entry::getKey) .map(records::get) .map(record -> Megaphones.forRecord(context, record)) + .sortBy(m -> -m.getPriority().getPriorityValue()) .toList(); - boolean hasOptional = Stream.of(megaphones).anyMatch(m -> !m.isMandatory()); - boolean hasMandatory = Stream.of(megaphones).anyMatch(Megaphone::isMandatory); - - if (hasOptional && hasMandatory) { - megaphones = Stream.of(megaphones).filter(Megaphone::isMandatory).toList(); - } - if (megaphones.size() > 0) { return megaphones.get(0); } else { @@ -93,6 +87,7 @@ public final class Megaphones { put(Event.MESSAGE_REQUESTS, shouldShowMessageRequestsMegaphone() ? ALWAYS : NEVER); put(Event.MENTIONS, shouldShowMentionsMegaphone() ? ALWAYS : NEVER); put(Event.LINK_PREVIEWS, shouldShowLinkPreviewsMegaphone(context) ? ALWAYS : NEVER); + put(Event.CLIENT_DEPRECATED, SignalStore.misc().isClientDeprecated() ? ALWAYS : NEVER); }}; } @@ -110,6 +105,8 @@ public final class Megaphones { return buildMentionsMegaphone(); case LINK_PREVIEWS: return buildLinkPreviewsMegaphone(); + case CLIENT_DEPRECATED: + return buildClientDeprecatedMegaphone(context); default: throw new IllegalArgumentException("Event not handled!"); } @@ -117,14 +114,14 @@ public final class Megaphones { private static @NonNull Megaphone buildReactionsMegaphone() { return new Megaphone.Builder(Event.REACTIONS, Megaphone.Style.REACTIONS) - .setMandatory(false) + .setPriority(Megaphone.Priority.DEFAULT) .build(); } private static @NonNull Megaphone buildPinsForAllMegaphone(@NonNull MegaphoneRecord record) { if (PinsForAllSchedule.shouldDisplayFullScreen(record.getFirstVisible(), System.currentTimeMillis())) { return new Megaphone.Builder(Event.PINS_FOR_ALL, Megaphone.Style.FULLSCREEN) - .setMandatory(true) + .setPriority(Megaphone.Priority.HIGH) .enableSnooze(null) .setOnVisibleListener((megaphone, listener) -> { if (new NetworkConstraint.Factory(ApplicationDependencies.getApplication()).create().isMet()) { @@ -134,7 +131,7 @@ public final class Megaphones { .build(); } else { return new Megaphone.Builder(Event.PINS_FOR_ALL, Megaphone.Style.BASIC) - .setMandatory(true) + .setPriority(Megaphone.Priority.HIGH) .setImage(R.drawable.kbs_pin_megaphone) .setTitle(R.string.KbsMegaphone__create_a_pin) .setBody(R.string.KbsMegaphone__pins_keep_information_thats_stored_with_signal_encrytped) @@ -184,7 +181,7 @@ public final class Megaphones { private static @NonNull Megaphone buildMessageRequestsMegaphone(@NonNull Context context) { return new Megaphone.Builder(Event.MESSAGE_REQUESTS, Megaphone.Style.FULLSCREEN) .disableSnooze() - .setMandatory(true) + .setPriority(Megaphone.Priority.HIGH) .setOnVisibleListener(((megaphone, listener) -> { listener.onMegaphoneNavigationRequested(new Intent(context, MessageRequestMegaphoneActivity.class), ConversationListFragment.MESSAGE_REQUESTS_REQUEST_CODE_CREATE_NAME); @@ -202,7 +199,17 @@ public final class Megaphones { private static @NonNull Megaphone buildLinkPreviewsMegaphone() { return new Megaphone.Builder(Event.LINK_PREVIEWS, Megaphone.Style.LINK_PREVIEWS) - .setMandatory(true) + .setPriority(Megaphone.Priority.HIGH) + .build(); + } + + private static @NonNull Megaphone buildClientDeprecatedMegaphone(@NonNull Context context) { + return new Megaphone.Builder(Event.CLIENT_DEPRECATED, Megaphone.Style.FULLSCREEN) + .disableSnooze() + .setPriority(Megaphone.Priority.HIGH) + .setOnVisibleListener((megaphone, listener) -> { + listener.onMegaphoneNavigationRequested(new Intent(context, ClientDeprecatedActivity.class)); + }) .build(); } @@ -224,7 +231,8 @@ public final class Megaphones { PIN_REMINDER("pin_reminder"), MESSAGE_REQUESTS("message_requests"), MENTIONS("mentions"), - LINK_PREVIEWS("link_previews"); + LINK_PREVIEWS("link_previews"), + CLIENT_DEPRECATED("client_deprecated"); private final String key; diff --git a/app/src/main/java/org/thoughtcrime/securesms/migrations/ApplicationMigrations.java b/app/src/main/java/org/thoughtcrime/securesms/migrations/ApplicationMigrations.java index 47df39184c..5f8ab87036 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/migrations/ApplicationMigrations.java +++ b/app/src/main/java/org/thoughtcrime/securesms/migrations/ApplicationMigrations.java @@ -75,6 +75,7 @@ public class ApplicationMigrations { if (!isUpdate(context)) { Log.d(TAG, "Not an update. Skipping."); + VersionTracker.updateLastSeenVersion(context); return; } diff --git a/app/src/main/java/org/thoughtcrime/securesms/net/RemoteDeprecationDetectorInterceptor.java b/app/src/main/java/org/thoughtcrime/securesms/net/RemoteDeprecationDetectorInterceptor.java index 80258eb819..ff5b542ba5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/net/RemoteDeprecationDetectorInterceptor.java +++ b/app/src/main/java/org/thoughtcrime/securesms/net/RemoteDeprecationDetectorInterceptor.java @@ -23,7 +23,7 @@ public final class RemoteDeprecationDetectorInterceptor implements Interceptor { if (response.code() == 499 && !SignalStore.misc().isClientDeprecated()) { Log.w(TAG, "Received 499. Client version is deprecated."); - SignalStore.misc().markDeprecated(); + SignalStore.misc().markClientDeprecated(); } return response; diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/VersionTracker.java b/app/src/main/java/org/thoughtcrime/securesms/util/VersionTracker.java index 4c9a6ba97a..e94710c021 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/VersionTracker.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/VersionTracker.java @@ -3,10 +3,15 @@ package org.thoughtcrime.securesms.util; import android.content.Context; import androidx.annotation.NonNull; +import org.thoughtcrime.securesms.keyvalue.SignalStore; +import org.thoughtcrime.securesms.logging.Log; + import java.io.IOException; public class VersionTracker { + private static final String TAG = Log.tag(VersionTracker.class); + public static int getLastSeenVersion(@NonNull Context context) { return TextSecurePreferences.getLastVersionCode(context); } @@ -14,7 +19,13 @@ public class VersionTracker { public static void updateLastSeenVersion(@NonNull Context context) { try { int currentVersionCode = Util.getCanonicalVersionCode(); - TextSecurePreferences.setLastVersionCode(context, currentVersionCode); + int lastVersionCode = TextSecurePreferences.getLastVersionCode(context); + + if (currentVersionCode != lastVersionCode) { + Log.i(TAG, "Upgraded from " + lastVersionCode + " to " + currentVersionCode); + SignalStore.misc().clearClientDeprecated(); + TextSecurePreferences.setLastVersionCode(context, currentVersionCode); + } } catch (IOException ioe) { throw new AssertionError(ioe); } diff --git a/app/src/main/res/drawable/ic_signal_logo_large.xml b/app/src/main/res/drawable/ic_signal_logo_large.xml new file mode 100644 index 0000000000..cab7595e13 --- /dev/null +++ b/app/src/main/res/drawable/ic_signal_logo_large.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/reminder_background_terminal.xml b/app/src/main/res/drawable/reminder_background_terminal.xml new file mode 100644 index 0000000000..795dc3c25c --- /dev/null +++ b/app/src/main/res/drawable/reminder_background_terminal.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/app/src/main/res/layout/client_deprecated_activity.xml b/app/src/main/res/layout/client_deprecated_activity.xml new file mode 100644 index 0000000000..31dbbba4f0 --- /dev/null +++ b/app/src/main/res/layout/client_deprecated_activity.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + +