Delete the reminders system.

This commit is contained in:
Nicholas Tinsley 2024-08-19 13:45:53 -04:00 committed by mtang-signal
parent 4002dea05d
commit 9a24455085
37 changed files with 73 additions and 1277 deletions

View file

@ -1,15 +0,0 @@
package org.thoughtcrime.securesms.components.reminder
import org.thoughtcrime.securesms.R
class BubbleOptOutReminder : Reminder(R.string.BubbleOptOutTooltip__description) {
init {
addAction(Action(R.string.BubbleOptOutTooltip__turn_off, R.id.reminder_action_bubble_turn_off))
addAction(Action(R.string.BubbleOptOutTooltip__not_now, R.id.reminder_action_bubble_not_now))
}
override fun isDismissable(): Boolean {
return false
}
}

View file

@ -1,42 +0,0 @@
package org.thoughtcrime.securesms.components.reminder
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.keyvalue.SignalStore
import kotlin.time.Duration.Companion.days
/**
* Reminder shown when CDS is in a permanent error state, preventing us from doing a sync.
*/
class CdsPermanentErrorReminder : Reminder(R.string.reminder_cds_permanent_error_body) {
init {
addAction(
Action(
R.string.reminder_cds_permanent_error_learn_more,
R.id.reminder_action_cds_permanent_error_learn_more
)
)
}
override fun isDismissable(): Boolean {
return false
}
override fun getImportance(): Importance {
return Importance.ERROR
}
companion object {
/**
* Even if we're not truly "permanently blocked", if the time until we're unblocked is long enough, we'd rather show the permanent error message than
* telling the user to wait for 3 months or something.
*/
val PERMANENT_TIME_CUTOFF = 30.days.inWholeMilliseconds
@JvmStatic
fun isEligible(): Boolean {
val timeUntilUnblock = SignalStore.misc.cdsBlockedUtil - System.currentTimeMillis()
return SignalStore.misc.isCdsBlocked && timeUntilUnblock >= PERMANENT_TIME_CUTOFF
}
}
}

View file

@ -1,35 +0,0 @@
package org.thoughtcrime.securesms.components.reminder
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.keyvalue.SignalStore
/**
* Reminder shown when CDS is rate-limited, preventing us from temporarily doing a refresh.
*/
class CdsTemporaryErrorReminder : Reminder(R.string.reminder_cds_warning_body) {
init {
addAction(
Action(
R.string.reminder_cds_warning_learn_more,
R.id.reminder_action_cds_temporary_error_learn_more
)
)
}
override fun isDismissable(): Boolean {
return false
}
override fun getImportance(): Importance {
return Importance.ERROR
}
companion object {
@JvmStatic
fun isEligible(): Boolean {
val timeUntilUnblock = SignalStore.misc.cdsBlockedUtil - System.currentTimeMillis()
return SignalStore.misc.isCdsBlocked && timeUntilUnblock < CdsPermanentErrorReminder.PERMANENT_TIME_CUTOFF
}
}
}

View file

@ -1,41 +0,0 @@
package org.thoughtcrime.securesms.components.reminder;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.PowerManager;
import android.provider.Settings;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.util.PowerManagerCompat;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
@SuppressLint("BatteryLife")
public class DozeReminder extends Reminder {
@RequiresApi(api = 23)
public DozeReminder(@NonNull final Context context) {
super(R.string.DozeReminder_optimize_for_missing_play_services, R.string.DozeReminder_this_device_does_not_support_play_services_tap_to_disable_system_battery);
setOkListener(v -> {
TextSecurePreferences.setPromptedOptimizeDoze(context, true);
PowerManagerCompat.requestIgnoreBatteryOptimizations(context);
});
setDismissListener(v -> TextSecurePreferences.setPromptedOptimizeDoze(context, true));
}
public static boolean isEligible(Context context) {
return !SignalStore.account().isFcmEnabled() &&
!TextSecurePreferences.hasPromptedOptimizeDoze(context) &&
Build.VERSION.SDK_INT >= 23 &&
!((PowerManager)context.getSystemService(Context.POWER_SERVICE)).isIgnoringBatteryOptimizations(context.getPackageName());
}
}

View file

@ -1,23 +0,0 @@
package org.thoughtcrime.securesms.components.reminder
import android.content.Context
import android.view.View
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.util.PlayStoreUtil
/**
* Banner to update app to the latest version because of enclave failure
*/
class EnclaveFailureReminder(context: Context) : Reminder(R.string.EnclaveFailureReminder_update_signal) {
init {
addAction(Action(R.string.ExpiredBuildReminder_update_now, R.id.reminder_action_update_now))
okListener = View.OnClickListener { PlayStoreUtil.openPlayStoreOrOurApkDownloadPage(context) }
}
override fun isDismissable(): Boolean = false
override fun getImportance(): Importance {
return Importance.TERMINAL
}
}

View file

@ -1,34 +0,0 @@
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;
/**
* 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(R.string.ExpiredBuildReminder_this_version_of_signal_has_expired);
addAction(new Action(R.string.ExpiredBuildReminder_update_now, R.id.reminder_action_update_now));
}
@Override
public boolean isDismissable() {
return false;
}
@Override
public @NonNull Importance getImportance() {
return Importance.TERMINAL;
}
public static boolean isEligible() {
return SignalStore.misc().isClientDeprecated();
}
}

View file

@ -1,48 +0,0 @@
package org.thoughtcrime.securesms.components.reminder;
import android.content.Context;
import androidx.annotation.NonNull;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.recipients.RecipientId;
import java.util.List;
/**
* Shows a reminder to add anyone that might have been missed in GV1->GV2 migration.
*/
public class GroupsV1MigrationSuggestionsReminder extends Reminder {
private final int suggestionsSize;
public GroupsV1MigrationSuggestionsReminder(@NonNull List<RecipientId> suggestions) {
this.suggestionsSize = suggestions.size();
addAction(new AddMembersAction(suggestionsSize));
addAction(new Action(R.string.GroupsV1MigrationSuggestionsReminder_no_thanks, R.id.reminder_action_gv1_suggestion_no_thanks));
}
@Override
public @NonNull CharSequence getText(@NonNull Context context) {
return context.getResources().getQuantityString(R.plurals.GroupsV1MigrationSuggestionsReminder_members_couldnt_be_added_to_the_new_group, suggestionsSize, suggestionsSize);
}
@Override
public boolean isDismissable() {
return false;
}
private static class AddMembersAction extends Action {
private final int suggestionsSize;
public AddMembersAction(int suggestionsSize) {
super(NO_RESOURCE, R.id.reminder_action_gv1_suggestion_add_members);
this.suggestionsSize = suggestionsSize;
}
@Override
public CharSequence getTitle(@NonNull Context context) {
return context.getResources().getQuantityString(R.plurals.GroupsV1MigrationSuggestionsReminder_add_members, suggestionsSize);
}
}
}

View file

@ -1,48 +0,0 @@
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 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) {
setOkListener(v -> PlayStoreUtil.openPlayStoreOrOurApkDownloadPage(context));
addAction(new Action(R.string.OutdatedBuildReminder_update_now, R.id.reminder_action_update_now));
}
@Override
public @NonNull CharSequence getText(@NonNull Context context) {
int days = getDaysUntilExpiry();
if (days == 0) {
return context.getString(R.string.OutdatedBuildReminder_your_version_of_signal_will_expire_today);
} else {
return context.getResources().getQuantityString(R.plurals.OutdatedBuildReminder_your_version_of_signal_will_expire_in_n_days, days, days);
}
}
@Override
public boolean isDismissable() {
return false;
}
public static boolean isEligible() {
return getDaysUntilExpiry() <= 10;
}
private static int getDaysUntilExpiry() {
return (int) TimeUnit.MILLISECONDS.toDays(Util.getTimeUntilBuildExpiry(SignalStore.misc().getEstimatedServerTime()));
}
}

View file

@ -1,36 +0,0 @@
package org.thoughtcrime.securesms.components.reminder;
import android.content.Context;
import androidx.annotation.NonNull;
import org.thoughtcrime.securesms.R;
/**
* Shown to admins when there are pending group join requests.
*/
public final class PendingGroupJoinRequestsReminder extends Reminder {
private final int count;
public PendingGroupJoinRequestsReminder(int count) {
this.count = count;
addAction(new Action(R.string.PendingGroupJoinRequestsReminder_view, R.id.reminder_action_review_join_requests));
}
@Override
public @NonNull CharSequence getText(@NonNull Context context) {
return context.getResources().getQuantityString(R.plurals.PendingGroupJoinRequestsReminder_d_pending_member_requests, count, count);
}
@Override
public boolean isDismissable() {
return true;
}
@Override
public @NonNull Importance getImportance() {
return Importance.NORMAL;
}
}

View file

@ -1,119 +0,0 @@
package org.thoughtcrime.securesms.components.reminder;
import android.content.Context;
import android.view.View.OnClickListener;
import androidx.annotation.IdRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import org.whispersystems.signalservice.api.util.Preconditions;
import java.util.LinkedList;
import java.util.List;
public abstract class Reminder {
public static final int NO_RESOURCE = -1;
private int title;
private int text;
private OnClickListener okListener;
private OnClickListener dismissListener;
private final List<Action> actions = new LinkedList<>();
/**
* For a reminder that wishes to generate it's own strings by overwriting
* {@link #getText(Context)} and {@link #getTitle(Context)}
*/
public Reminder() {
this(NO_RESOURCE, NO_RESOURCE);
}
public Reminder(@StringRes int textRes) {
this(NO_RESOURCE, textRes);
}
public Reminder(@StringRes int titleRes, @StringRes int textRes) {
this.title = titleRes;
this.text = textRes;
}
public @Nullable CharSequence getTitle(@NonNull Context context) {
if (title == NO_RESOURCE) {
return null;
}
return context.getString(title);
}
public @NonNull CharSequence getText(@NonNull Context context) {
Preconditions.checkArgument(text != NO_RESOURCE);
return context.getString(text);
}
public OnClickListener getOkListener() {
return okListener;
}
public OnClickListener getDismissListener() {
return dismissListener;
}
public void setOkListener(OnClickListener okListener) {
this.okListener = okListener;
}
public void setDismissListener(OnClickListener dismissListener) {
this.dismissListener = dismissListener;
}
public boolean isDismissable() {
return true;
}
public @NonNull Importance getImportance() {
return Importance.NORMAL;
}
protected void addAction(@NonNull Action action) {
actions.add(action);
}
public List<Action> getActions() {
return actions;
}
public int getProgress() {
return -1;
}
public enum Importance {
NORMAL, ERROR, TERMINAL
}
public static class Action {
private final int title;
private final int actionId;
public Action(@IdRes int actionId) {
this(NO_RESOURCE, actionId);
}
public Action(@StringRes int title, @IdRes int actionId) {
this.title = title;
this.actionId = actionId;
}
public CharSequence getTitle(@NonNull Context context) {
Preconditions.checkArgument(title != NO_RESOURCE);
return context.getText(title);
}
public int getActionId() {
return actionId;
}
}
}

View file

@ -1,68 +0,0 @@
package org.thoughtcrime.securesms.components.reminder;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.RecyclerView;
import org.thoughtcrime.securesms.R;
import java.util.Collections;
import java.util.List;
final class ReminderActionsAdapter extends RecyclerView.Adapter<ReminderActionsAdapter.ActionViewHolder> {
private final Reminder.Importance importance;
private final List<Reminder.Action> actions;
private final ReminderView.OnActionClickListener actionClickListener;
ReminderActionsAdapter(Reminder.Importance importance, List<Reminder.Action> actions, ReminderView.OnActionClickListener actionClickListener) {
this.importance = importance;
this.actions = Collections.unmodifiableList(actions);
this.actionClickListener = actionClickListener;
}
@NonNull
@Override
public ActionViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
Context context = parent.getContext();
TextView button = ((TextView) LayoutInflater.from(context).inflate(R.layout.reminder_action_button, parent, false));
if (importance == Reminder.Importance.NORMAL) {
button.setTextColor(ContextCompat.getColor(context, R.color.signal_colorPrimary));
} else if (importance == Reminder.Importance.ERROR || importance == Reminder.Importance.TERMINAL) {
button.setTextColor(ContextCompat.getColor(context, R.color.signal_light_colorOnSurface));
}
return new ActionViewHolder(button);
}
@Override
public void onBindViewHolder(@NonNull ActionViewHolder holder, int position) {
final Reminder.Action action = actions.get(position);
((Button) holder.itemView).setText(action.getTitle(holder.itemView.getContext()));
holder.itemView.setOnClickListener(v -> {
if (holder.getAdapterPosition() == RecyclerView.NO_POSITION) return;
actionClickListener.onActionClick(action.getActionId());
});
}
@Override
public int getItemCount() {
return actions.size();
}
final class ActionViewHolder extends RecyclerView.ViewHolder {
ActionViewHolder(@NonNull View itemView) {
super(itemView);
}
}
}

View file

@ -1,183 +0,0 @@
package org.thoughtcrime.securesms.components.reminder;
import android.content.Context;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.ProgressBar;
import android.widget.Space;
import android.widget.TextView;
import androidx.annotation.IdRes;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.card.MaterialCardView;
import org.signal.core.util.DimensionUnit;
import org.thoughtcrime.securesms.R;
import java.util.List;
/**
* View to display actionable reminders to the user
*/
public final class ReminderView extends FrameLayout {
private ProgressBar progressBar;
private TextView progressText;
private MaterialCardView container;
private ImageButton closeButton;
private TextView title;
private TextView text;
private OnDismissListener dismissListener;
private Space space;
private RecyclerView actionsRecycler;
private OnActionClickListener actionClickListener;
private OnHideListener onHideListener;
public ReminderView(Context context) {
super(context);
initialize();
}
public ReminderView(Context context, AttributeSet attrs) {
super(context, attrs);
initialize();
}
public ReminderView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initialize();
}
private void initialize() {
LayoutInflater.from(getContext()).inflate(R.layout.reminder_header, this, true);
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) {
if (!TextUtils.isEmpty(reminder.getTitle(getContext()))) {
title.setText(reminder.getTitle(getContext()));
title.setVisibility(VISIBLE);
space.setVisibility(GONE);
} else {
title.setText("");
title.setVisibility(GONE);
space.setVisibility(VISIBLE);
text.setTextColor(ContextCompat.getColor(getContext(), R.color.signal_colorOnSurface));
}
if (!reminder.isDismissable()) {
space.setVisibility(GONE);
}
text.setText(reminder.getText(getContext()));
switch (reminder.getImportance()) {
case NORMAL:
title.setTextColor(ContextCompat.getColor(getContext(), R.color.signal_colorOnSurface));
text.setTextColor(ContextCompat.getColor(getContext(), R.color.signal_colorOnSurfaceVariant));
break;
case ERROR:
case TERMINAL:
container.setStrokeWidth(0);
container.setCardBackgroundColor(ContextCompat.getColor(getContext(), R.color.reminder_background));
title.setTextColor(ContextCompat.getColor(getContext(), R.color.signal_light_colorOnSurface));
text.setTextColor(ContextCompat.getColor(getContext(), R.color.signal_light_colorOnSurface));
break;
default:
throw new IllegalStateException();
}
if (reminder.getOkListener() != null) {
setOnClickListener(reminder.getOkListener());
}
closeButton.setVisibility(reminder.isDismissable() ? View.VISIBLE : View.GONE);
closeButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
hide();
if (reminder.getDismissListener() != null) reminder.getDismissListener().onClick(v);
if (dismissListener != null) dismissListener.onDismiss();
}
});
if (reminder.getImportance() == Reminder.Importance.NORMAL) {
closeButton.setColorFilter(ContextCompat.getColor(getContext(), R.color.signal_colorOnSurfaceVariant));
}
int progress = reminder.getProgress();
if (progress != -1) {
progressBar.setProgress(progress);
progressBar.setVisibility(VISIBLE);
progressText.setText(getContext().getString(R.string.reminder_header_progress, progress));
progressText.setVisibility(VISIBLE);
} else {
progressBar.setVisibility(GONE);
progressText.setVisibility(GONE);
}
List<Reminder.Action> actions = reminder.getActions();
if (actions.isEmpty()) {
text.setPadding(0, 0, 0, ((int) DimensionUnit.DP.toPixels(16f)));
actionsRecycler.setVisibility(GONE);
} else {
text.setPadding(0, 0, 0, 0);
actionsRecycler.setVisibility(VISIBLE);
actionsRecycler.setAdapter(new ReminderActionsAdapter(reminder.getImportance(), actions, this::handleActionClicked));
}
container.setVisibility(View.VISIBLE);
}
private void handleActionClicked(@IdRes int actionId) {
if (actionClickListener != null) actionClickListener.onActionClick(actionId);
}
public void setOnDismissListener(OnDismissListener dismissListener) {
this.dismissListener = dismissListener;
}
public void setOnActionClickListener(@Nullable OnActionClickListener actionClickListener) {
this.actionClickListener = actionClickListener;
}
public void setOnHideListener(@Nullable OnHideListener onHideListener) {
this.onHideListener = onHideListener;
}
public void requestDismiss() {
closeButton.performClick();
}
public void hide() {
if (onHideListener != null && onHideListener.onHide()) {
return;
}
container.setVisibility(View.GONE);
}
public interface OnDismissListener {
void onDismiss();
}
public interface OnActionClickListener {
void onActionClick(@IdRes int actionId);
}
public interface OnHideListener {
boolean onHide();
}
}

View file

@ -1,30 +0,0 @@
package org.thoughtcrime.securesms.components.reminder;
import android.content.Context;
import androidx.annotation.NonNull;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
public class ServiceOutageReminder extends Reminder {
public ServiceOutageReminder() {
super(R.string.reminder_header_service_outage_text);
}
public static boolean isEligible(@NonNull Context context) {
return TextSecurePreferences.getServiceOutage(context);
}
@Override
public boolean isDismissable() {
return false;
}
@NonNull
@Override
public Importance getImportance() {
return Importance.ERROR;
}
}

View file

@ -1,31 +0,0 @@
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.TextSecurePreferences;
public class UnauthorizedReminder extends Reminder {
public UnauthorizedReminder() {
super(R.string.UnauthorizedReminder_this_is_likely_because_you_registered_your_phone_number_with_Signal_on_a_different_device);
addAction(new Action(R.string.UnauthorizedReminder_reregister_action, R.id.reminder_action_re_register));
}
@Override
public boolean isDismissable() {
return false;
}
@Override
public @NonNull Importance getImportance() {
return Importance.ERROR;
}
public static boolean isEligible(Context context) {
return TextSecurePreferences.isUnauthorizedReceived(context) || !SignalStore.account().isRegistered();
}
}

View file

@ -1,51 +0,0 @@
package org.thoughtcrime.securesms.components.reminder
import android.content.Context
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.keyvalue.AccountValues.UsernameSyncState
import org.thoughtcrime.securesms.keyvalue.SignalStore
/**
* Displays a reminder message when the local username gets out of sync with
* what the server thinks our username is.
*/
class UsernameOutOfSyncReminder : Reminder(NO_RESOURCE) {
init {
val action = if (SignalStore.account.usernameSyncState == UsernameSyncState.USERNAME_AND_LINK_CORRUPTED) {
R.id.reminder_action_fix_username_and_link
} else {
R.id.reminder_action_fix_username_link
}
addAction(
Action(
R.string.UsernameOutOfSyncReminder__fix_now,
action
)
)
}
override fun getText(context: Context): CharSequence {
return if (SignalStore.account.usernameSyncState == UsernameSyncState.USERNAME_AND_LINK_CORRUPTED) {
context.getString(R.string.UsernameOutOfSyncReminder__username_and_link_corrupt)
} else {
context.getString(R.string.UsernameOutOfSyncReminder__link_corrupt)
}
}
override fun isDismissable(): Boolean {
return false
}
companion object {
@JvmStatic
fun isEligible(): Boolean {
return when (SignalStore.account.usernameSyncState) {
UsernameSyncState.USERNAME_AND_LINK_CORRUPTED -> true
UsernameSyncState.LINK_CORRUPTED -> true
UsernameSyncState.IN_SYNC -> false
}
}
}
}

View file

@ -4,7 +4,6 @@ import android.os.Bundle
import android.view.View
import android.widget.TextView
import android.widget.Toast
import androidx.annotation.IdRes
import androidx.annotation.StringRes
import androidx.compose.ui.platform.ComposeView
import androidx.fragment.app.viewModels
@ -13,9 +12,6 @@ import androidx.navigation.fragment.findNavController
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
import org.signal.core.util.isNotNullOrBlank
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.badges.BadgeImageView
@ -24,10 +20,6 @@ import org.thoughtcrime.securesms.banner.banners.OutdatedBuildBanner
import org.thoughtcrime.securesms.banner.banners.UnauthorizedBanner
import org.thoughtcrime.securesms.components.AvatarImageView
import org.thoughtcrime.securesms.components.emoji.EmojiTextView
import org.thoughtcrime.securesms.components.reminder.ExpiredBuildReminder
import org.thoughtcrime.securesms.components.reminder.Reminder
import org.thoughtcrime.securesms.components.reminder.ReminderView
import org.thoughtcrime.securesms.components.reminder.UnauthorizedReminder
import org.thoughtcrime.securesms.components.settings.DSLConfiguration
import org.thoughtcrime.securesms.components.settings.DSLSettingsFragment
import org.thoughtcrime.securesms.components.settings.DSLSettingsIcon
@ -38,13 +30,10 @@ import org.thoughtcrime.securesms.components.settings.app.subscription.InAppPaym
import org.thoughtcrime.securesms.components.settings.app.subscription.completed.InAppPaymentsBottomSheetDelegate
import org.thoughtcrime.securesms.components.settings.configure
import org.thoughtcrime.securesms.database.model.InAppPaymentSubscriberRecord
import org.thoughtcrime.securesms.events.ReminderUpdateEvent
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.registration.ui.RegistrationActivity
import org.thoughtcrime.securesms.util.Environment
import org.thoughtcrime.securesms.util.PlayStoreUtil
import org.thoughtcrime.securesms.util.RemoteConfig
import org.thoughtcrime.securesms.util.Util
import org.thoughtcrime.securesms.util.ViewUtil
@ -62,17 +51,15 @@ class AppSettingsFragment : DSLSettingsFragment(
private val viewModel: AppSettingsViewModel by viewModels()
private lateinit var reminderView: Stub<ReminderView>
private lateinit var bannerView: Stub<ComposeView>
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
viewLifecycleOwner.lifecycle.addObserver(InAppPaymentsBottomSheetDelegate(childFragmentManager, viewLifecycleOwner))
super.onViewCreated(view, savedInstanceState)
reminderView = ViewUtil.findStubById(view, R.id.reminder_stub)
bannerView = ViewUtil.findStubById(view, R.id.banner_stub)
updateReminders()
updateBanners()
}
override fun bindAdapter(adapter: MappingAdapter) {
@ -85,69 +72,31 @@ class AppSettingsFragment : DSLSettingsFragment(
}
}
@Subscribe(threadMode = ThreadMode.MAIN)
fun onEvent(event: ReminderUpdateEvent?) {
updateReminders()
}
private fun updateReminders() {
if (RemoteConfig.newBannerUi) {
val bannerFlows = listOf(
OutdatedBuildBanner.createFlow(requireContext(), OutdatedBuildBanner.ExpiryStatus.EXPIRED_ONLY),
UnauthorizedBanner.createFlow(requireContext())
)
val bannerManager = BannerManager(bannerFlows)
bannerManager.setContent(bannerView.get())
} else {
if (ExpiredBuildReminder.isEligible()) {
showReminder(ExpiredBuildReminder(context))
} else if (UnauthorizedReminder.isEligible(context)) {
showReminder(UnauthorizedReminder())
} else {
hideReminders()
private fun updateBanners() {
val bannerFlows = listOf(
OutdatedBuildBanner.createFlow(requireContext(), OutdatedBuildBanner.ExpiryStatus.EXPIRED_ONLY),
UnauthorizedBanner.createFlow(requireContext())
)
val bannerManager = BannerManager(bannerFlows,
onNewBannerShownListener = {
if (bannerView.resolved()) {
bannerView.get().addOnLayoutChangeListener { _, _, top, _, bottom, _, _, _, _ ->
recyclerView?.setPadding(0, bottom - top, 0, 0)
}
recyclerView?.clipToPadding = false
}
},
onNoBannerShownListener = {
recyclerView?.clipToPadding = true
}
}
)
bannerManager.setContent(bannerView.get())
viewModel.refreshDeprecatedOrUnregistered()
}
private fun showReminder(reminder: Reminder) {
if (!reminderView.resolved()) {
reminderView.get().addOnLayoutChangeListener { _, _, top, _, bottom, _, _, _, _ ->
recyclerView?.setPadding(0, bottom - top, 0, 0)
}
recyclerView?.clipToPadding = false
}
reminderView.get().showReminder(reminder)
reminderView.get().setOnActionClickListener { reminderActionId: Int -> this.handleReminderAction(reminderActionId) }
}
private fun hideReminders() {
if (reminderView.resolved()) {
reminderView.get().hide()
recyclerView?.clipToPadding = true
}
}
private fun handleReminderAction(@IdRes reminderActionId: Int) {
when (reminderActionId) {
R.id.reminder_action_update_now -> {
PlayStoreUtil.openPlayStoreOrOurApkDownloadPage(requireContext())
}
R.id.reminder_action_re_register -> {
startActivity(RegistrationActivity.newIntentForReRegistration(requireContext()))
}
}
}
override fun onResume() {
super.onResume()
viewModel.refreshExpiredGiftBadge()
EventBus.getDefault().register(this)
}
override fun onPause() {
super.onPause()
EventBus.getDefault().unregister(this)
}
private fun getConfiguration(state: AppSettingsState): DSLConfiguration {

View file

@ -21,8 +21,6 @@ import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.banner.Banner
import org.thoughtcrime.securesms.banner.BannerManager
import org.thoughtcrime.securesms.components.identity.UnverifiedBannerView
import org.thoughtcrime.securesms.components.reminder.Reminder
import org.thoughtcrime.securesms.components.reminder.ReminderView
import org.thoughtcrime.securesms.components.voice.VoiceNotePlayerView
import org.thoughtcrime.securesms.database.identity.IdentityRecordList
import org.thoughtcrime.securesms.database.model.IdentityRecord
@ -50,7 +48,6 @@ class ConversationBannerView @JvmOverloads constructor(
defStyleAttr: Int = 0
) : LinearLayoutCompat(context, attrs, defStyleAttr) {
private val unverifiedBannerStub: Stub<UnverifiedBannerView> by lazy { ViewUtil.findStubById(this, R.id.unverified_banner_stub) }
private val reminderStub: Stub<ReminderView> by lazy { ViewUtil.findStubById(this, R.id.reminder_stub) }
private val bannerStub: Stub<ComposeView> by lazy { ViewUtil.findStubById(this, R.id.banner_stub) }
private val reviewBannerStub: Stub<ReviewBannerView> by lazy { ViewUtil.findStubById(this, R.id.review_banner_stub) }
private val voiceNotePlayerStub: Stub<View> by lazy { ViewUtil.findStubById(this, R.id.voice_note_player_stub) }
@ -68,33 +65,6 @@ class ConversationBannerView @JvmOverloads constructor(
}
}
fun showReminder(reminder: Reminder) {
show(
stub = reminderStub
) {
showReminder(reminder)
setOnActionClickListener {
when (it) {
R.id.reminder_action_update_now -> listener?.updateAppAction()
R.id.reminder_action_re_register -> listener?.reRegisterAction()
R.id.reminder_action_review_join_requests -> listener?.reviewJoinRequestsAction()
R.id.reminder_action_gv1_suggestion_no_thanks -> listener?.gv1SuggestionsAction(it)
R.id.reminder_action_bubble_not_now, R.id.reminder_action_bubble_turn_off -> {
listener?.changeBubbleSettingAction(disableSetting = it == R.id.reminder_action_bubble_turn_off)
}
}
}
setOnHideListener {
clearReminder()
true
}
}
}
fun clearReminder() {
hide(reminderStub)
}
fun showUnverifiedBanner(identityRecords: IdentityRecordList) {
show(
stub = unverifiedBannerStub
@ -169,10 +139,6 @@ class ConversationBannerView @JvmOverloads constructor(
val slideTransition = Slide(Gravity.TOP)
val changeTransition = ChangeBounds().apply {
if (reminderStub.isVisible) {
addTarget(reminderStub.get())
}
if (unverifiedBannerStub.isVisible) {
addTarget(unverifiedBannerStub.get())
}

View file

@ -210,7 +210,6 @@ import org.thoughtcrime.securesms.database.model.databaseprotos.BodyRangeList
import org.thoughtcrime.securesms.databinding.V2ConversationFragmentBinding
import org.thoughtcrime.securesms.dependencies.AppDependencies
import org.thoughtcrime.securesms.events.GroupCallPeekEvent
import org.thoughtcrime.securesms.events.ReminderUpdateEvent
import org.thoughtcrime.securesms.giph.mp4.GiphyMp4ItemDecoration
import org.thoughtcrime.securesms.giph.mp4.GiphyMp4PlaybackController
import org.thoughtcrime.securesms.giph.mp4.GiphyMp4PlaybackPolicy
@ -228,7 +227,6 @@ import org.thoughtcrime.securesms.groups.ui.migration.GroupsV1MigrationInfoBotto
import org.thoughtcrime.securesms.groups.ui.migration.GroupsV1MigrationSuggestionsDialog
import org.thoughtcrime.securesms.groups.v2.GroupBlockJoinRequestResult
import org.thoughtcrime.securesms.invites.InviteActions
import org.thoughtcrime.securesms.jobs.ServiceOutageDetectionJob
import org.thoughtcrime.securesms.keyboard.KeyboardPage
import org.thoughtcrime.securesms.keyboard.KeyboardPagerFragment
import org.thoughtcrime.securesms.keyboard.KeyboardPagerViewModel
@ -1020,35 +1018,25 @@ class ConversationFragment :
val conversationBannerListener = ConversationBannerListener()
binding.conversationBanner.listener = conversationBannerListener
if (RemoteConfig.newBannerUi) {
val serviceOutageObserver = ServiceOutageObserver(requireContext())
val bannerFlows = viewModel.getBannerFlows(
context = requireContext(),
serviceOutageStatusFlow = serviceOutageObserver.flow,
groupJoinClickListener = conversationBannerListener::reviewJoinRequestsAction,
onAddMembers = {
conversationGroupViewModel.groupRecordSnapshot?.let { groupRecord ->
GroupsV1MigrationSuggestionsDialog.show(requireActivity(), groupRecord.id.requireV2(), groupRecord.gv1MigrationSuggestions)
}
},
onNoThanks = conversationGroupViewModel::onSuggestedMembersBannerDismissed,
bubbleClickListener = conversationBannerListener::changeBubbleSettingAction
)
val serviceOutageObserver = ServiceOutageObserver(requireContext())
binding.conversationBanner.collectAndShowBanners(bannerFlows)
} else {
viewModel
.reminder
.subscribeBy { reminder ->
if (reminder.isPresent) {
binding.conversationBanner.showReminder(reminder.get())
} else {
binding.conversationBanner.clearReminder()
}
lifecycle.addObserver(serviceOutageObserver)
val bannerFlows = viewModel.getBannerFlows(
context = requireContext(),
serviceOutageStatusFlow = serviceOutageObserver.flow,
groupJoinClickListener = conversationBannerListener::reviewJoinRequestsAction,
onAddMembers = {
conversationGroupViewModel.groupRecordSnapshot?.let { groupRecord ->
GroupsV1MigrationSuggestionsDialog.show(requireActivity(), groupRecord.id.requireV2(), groupRecord.gv1MigrationSuggestions)
}
.addTo(disposables)
}
},
onNoThanks = conversationGroupViewModel::onSuggestedMembersBannerDismissed,
bubbleClickListener = conversationBannerListener::changeBubbleSettingAction
)
binding.conversationBanner.collectAndShowBanners(bannerFlows)
viewModel
.identityRecordsObservable
@ -4287,11 +4275,6 @@ class ConversationFragment :
groupCallViewModel.onGroupCallPeekEvent(groupCallPeekEvent)
}
@Subscribe(threadMode = ThreadMode.MAIN)
fun onReminderUpdateEvent(reminderUpdateEvent: ReminderUpdateEvent) {
viewModel.refreshReminder()
}
@Subscribe(threadMode = ThreadMode.MAIN)
fun onRecaptchaRequiredEvent(recaptchaRequiredEvent: RecaptchaRequiredEvent) {
RecaptchaProofBottomSheetFragment.show(childFragmentManager)

View file

@ -30,7 +30,6 @@ import org.signal.core.util.concurrent.MaybeCompat
import org.signal.core.util.concurrent.SignalExecutors
import org.signal.core.util.dp
import org.signal.core.util.logging.Log
import org.signal.core.util.toOptional
import org.signal.paging.PagedData
import org.signal.paging.PagingConfig
import org.thoughtcrime.securesms.R
@ -38,13 +37,6 @@ import org.thoughtcrime.securesms.ShortcutLauncherActivity
import org.thoughtcrime.securesms.attachments.TombstoneAttachment
import org.thoughtcrime.securesms.avatar.fallback.FallbackAvatarDrawable
import org.thoughtcrime.securesms.components.emoji.EmojiStrings
import org.thoughtcrime.securesms.components.reminder.BubbleOptOutReminder
import org.thoughtcrime.securesms.components.reminder.ExpiredBuildReminder
import org.thoughtcrime.securesms.components.reminder.GroupsV1MigrationSuggestionsReminder
import org.thoughtcrime.securesms.components.reminder.PendingGroupJoinRequestsReminder
import org.thoughtcrime.securesms.components.reminder.Reminder
import org.thoughtcrime.securesms.components.reminder.ServiceOutageReminder
import org.thoughtcrime.securesms.components.reminder.UnauthorizedReminder
import org.thoughtcrime.securesms.contactshare.Contact
import org.thoughtcrime.securesms.contactshare.ContactUtil
import org.thoughtcrime.securesms.conversation.ConversationMessage
@ -74,9 +66,7 @@ import org.thoughtcrime.securesms.database.model.StickerRecord
import org.thoughtcrime.securesms.database.model.databaseprotos.BodyRangeList
import org.thoughtcrime.securesms.dependencies.AppDependencies
import org.thoughtcrime.securesms.jobs.MultiDeviceViewOnceOpenJob
import org.thoughtcrime.securesms.jobs.ServiceOutageDetectionJob
import org.thoughtcrime.securesms.keyboard.KeyboardUtil
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.linkpreview.LinkPreview
import org.thoughtcrime.securesms.messagerequests.MessageRequestState
import org.thoughtcrime.securesms.mms.OutgoingMessage
@ -101,7 +91,6 @@ import org.thoughtcrime.securesms.util.hasTextSlide
import org.thoughtcrime.securesms.util.isViewOnceMessage
import org.thoughtcrime.securesms.util.requireTextSlide
import java.io.IOException
import java.util.Optional
import kotlin.math.max
import kotlin.time.Duration
import kotlin.time.Duration.Companion.seconds
@ -303,35 +292,6 @@ class ConversationRepository(
return SignalDatabase.messages.getUnreadMentionCount(threadId)
}
fun getReminder(groupRecord: GroupRecord?): Maybe<Optional<Reminder>> {
return Maybe.fromCallable {
val reminder: Reminder? = when {
ExpiredBuildReminder.isEligible() -> ExpiredBuildReminder(applicationContext)
UnauthorizedReminder.isEligible(applicationContext) -> UnauthorizedReminder()
ServiceOutageReminder.isEligible(applicationContext) -> {
AppDependencies.jobManager.add(ServiceOutageDetectionJob())
ServiceOutageReminder()
}
groupRecord != null && groupRecord.actionableRequestingMembersCount > 0 -> {
PendingGroupJoinRequestsReminder(groupRecord.actionableRequestingMembersCount)
}
groupRecord != null && groupRecord.gv1MigrationSuggestions.isNotEmpty() -> {
GroupsV1MigrationSuggestionsReminder(groupRecord.gv1MigrationSuggestions)
}
isInBubble && !SignalStore.tooltips.hasSeenBubbleOptOutTooltip() && Build.VERSION.SDK_INT > 29 -> {
BubbleOptOutReminder()
}
else -> null
}
reminder.toOptional()
}
}
@Suppress("IfThenToElvis")
fun getIdentityRecords(recipient: Recipient, groupRecord: GroupRecord?): Single<IdentityRecordsState> {
return Single.fromCallable {

View file

@ -36,7 +36,6 @@ import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.rx3.asFlow
import org.signal.core.util.concurrent.subscribeWithSubject
import org.signal.core.util.orNull
import org.signal.paging.ProxyPagingController
import org.thoughtcrime.securesms.banner.Banner
@ -46,7 +45,6 @@ import org.thoughtcrime.securesms.banner.banners.OutdatedBuildBanner
import org.thoughtcrime.securesms.banner.banners.PendingGroupJoinRequestsBanner
import org.thoughtcrime.securesms.banner.banners.ServiceOutageBanner
import org.thoughtcrime.securesms.banner.banners.UnauthorizedBanner
import org.thoughtcrime.securesms.components.reminder.Reminder
import org.thoughtcrime.securesms.contactshare.Contact
import org.thoughtcrime.securesms.conversation.ConversationMessage
import org.thoughtcrime.securesms.conversation.ScheduledMessagesRepository
@ -87,7 +85,6 @@ import org.thoughtcrime.securesms.util.hasGiftBadge
import org.thoughtcrime.securesms.util.rx.RxStore
import org.thoughtcrime.securesms.wallpaper.ChatWallpaper
import org.whispersystems.signalservice.api.push.ServiceId
import java.util.Optional
import java.util.concurrent.TimeUnit
import kotlin.time.Duration
@ -96,7 +93,7 @@ import kotlin.time.Duration
*/
class ConversationViewModel(
val threadId: Long,
private val requestedStartingPosition: Int,
requestedStartingPosition: Int,
initialChatColors: ChatColors,
private val repository: ConversationRepository,
recipientRepository: ConversationRecipientRepository,
@ -167,9 +164,6 @@ class ConversationViewModel(
val messageRequestState: MessageRequestState
get() = hasMessageRequestStateSubject.value ?: MessageRequestState()
private val refreshReminder: Subject<Unit> = PublishSubject.create()
val reminder: Observable<Optional<Reminder>>
private val groupRecordFlow: Flow<GroupRecord>
private val refreshIdentityRecords: Subject<Unit> = PublishSubject.create()
@ -286,13 +280,6 @@ class ConversationViewModel(
}
inputReadyState = _inputReadyState.observeOn(AndroidSchedulers.mainThread())
recipientRepository.conversationRecipient.map { Unit }.subscribeWithSubject(refreshReminder, disposables)
reminder = Observable.combineLatest(refreshReminder.startWithItem(Unit), recipientRepository.groupRecord) { _, groupRecord -> groupRecord }
.subscribeOn(Schedulers.io())
.flatMapMaybe { groupRecord -> repository.getReminder(groupRecord.orNull()) }
.observeOn(AndroidSchedulers.mainThread())
groupRecordFlow = recipientRepository.groupRecord
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
@ -356,10 +343,6 @@ class ConversationViewModel(
_searchQuery.onNext(query ?: "")
}
fun refreshReminder() {
refreshReminder.onNext(Unit)
}
fun onDismissReview() {
val recipientId = recipientSnapshot?.id ?: return
repository.dismissRequestReviewState(recipientId)

View file

@ -46,7 +46,6 @@ import android.widget.Toast;
import androidx.activity.OnBackPressedCallback;
import androidx.annotation.ColorInt;
import androidx.annotation.DrawableRes;
import androidx.annotation.IdRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.PluralsRes;
@ -113,16 +112,6 @@ import org.thoughtcrime.securesms.components.menu.ActionItem;
import org.thoughtcrime.securesms.components.menu.SignalBottomActionBar;
import org.thoughtcrime.securesms.components.menu.SignalContextMenu;
import org.thoughtcrime.securesms.components.registration.PulsingFloatingActionButton;
import org.thoughtcrime.securesms.components.reminder.CdsPermanentErrorReminder;
import org.thoughtcrime.securesms.components.reminder.CdsTemporaryErrorReminder;
import org.thoughtcrime.securesms.components.reminder.DozeReminder;
import org.thoughtcrime.securesms.components.reminder.ExpiredBuildReminder;
import org.thoughtcrime.securesms.components.reminder.OutdatedBuildReminder;
import org.thoughtcrime.securesms.components.reminder.Reminder;
import org.thoughtcrime.securesms.components.reminder.ReminderView;
import org.thoughtcrime.securesms.components.reminder.ServiceOutageReminder;
import org.thoughtcrime.securesms.components.reminder.UnauthorizedReminder;
import org.thoughtcrime.securesms.components.reminder.UsernameOutOfSyncReminder;
import org.thoughtcrime.securesms.components.settings.app.AppSettingsActivity;
import org.thoughtcrime.securesms.components.settings.app.notifications.manual.NotificationProfileSelectionFragment;
import org.thoughtcrime.securesms.components.settings.app.subscription.completed.InAppPaymentsBottomSheetDelegate;
@ -136,8 +125,6 @@ import org.thoughtcrime.securesms.contacts.paged.ContactSearchData;
import org.thoughtcrime.securesms.contacts.paged.ContactSearchKey;
import org.thoughtcrime.securesms.contacts.paged.ContactSearchMediator;
import org.thoughtcrime.securesms.contacts.paged.ContactSearchState;
import org.thoughtcrime.securesms.contacts.sync.CdsPermanentErrorBottomSheet;
import org.thoughtcrime.securesms.contacts.sync.CdsTemporaryErrorBottomSheet;
import org.thoughtcrime.securesms.conversationlist.chatfilter.ConversationFilterRequest;
import org.thoughtcrime.securesms.conversationlist.chatfilter.ConversationFilterSource;
import org.thoughtcrime.securesms.conversationlist.chatfilter.ConversationListFilterPullView;
@ -149,10 +136,8 @@ import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.ThreadTable;
import org.thoughtcrime.securesms.database.model.ThreadRecord;
import org.thoughtcrime.securesms.dependencies.AppDependencies;
import org.thoughtcrime.securesms.events.ReminderUpdateEvent;
import org.thoughtcrime.securesms.groups.SelectionLimits;
import org.thoughtcrime.securesms.jobs.RefreshOwnProfileJob;
import org.thoughtcrime.securesms.jobs.ServiceOutageDetectionJob;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.lock.v2.CreateSvrPinActivity;
import org.thoughtcrime.securesms.main.Material3OnScrollHelperBinder;
@ -170,7 +155,6 @@ import org.thoughtcrime.securesms.profiles.manage.UsernameEditFragment;
import org.thoughtcrime.securesms.ratelimit.RecaptchaProofBottomSheetFragment;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.registration.ui.RegistrationActivity;
import org.thoughtcrime.securesms.search.MessageResult;
import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.sms.MessageSender;
@ -181,8 +165,6 @@ import org.thoughtcrime.securesms.util.AppForegroundObserver;
import org.thoughtcrime.securesms.util.AppStartup;
import org.thoughtcrime.securesms.util.CachedInflater;
import org.thoughtcrime.securesms.util.ConversationUtil;
import org.thoughtcrime.securesms.util.PlayStoreUtil;
import org.thoughtcrime.securesms.util.RemoteConfig;
import org.thoughtcrime.securesms.util.ServiceOutageObserver;
import org.thoughtcrime.securesms.util.ServiceUtil;
import org.thoughtcrime.securesms.util.SignalLocalMetrics;
@ -206,12 +188,10 @@ import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import kotlin.Unit;
import kotlin.jvm.functions.Function1;
import kotlinx.coroutines.flow.Flow;
import static android.app.Activity.RESULT_OK;
@ -237,7 +217,6 @@ public class ConversationListFragment extends MainFragment implements ActionMode
private ActionMode actionMode;
private View coordinator;
private RecyclerView list;
private Stub<ReminderView> reminderView;
private Stub<ComposeView> bannerView;
private PulsingFloatingActionButton fab;
private PulsingFloatingActionButton cameraFab;
@ -302,7 +281,6 @@ public class ConversationListFragment extends MainFragment implements ActionMode
coordinator = view.findViewById(R.id.coordinator);
list = view.findViewById(R.id.list);
bottomActionBar = view.findViewById(R.id.conversation_list_bottom_action_bar);
reminderView = new Stub<>(view.findViewById(R.id.reminder));
bannerView = new Stub<>(view.findViewById(R.id.banner_compose_view));
megaphoneContainer = new Stub<>(view.findViewById(R.id.megaphone_container));
voiceNotePlayerViewStub = new Stub<>(view.findViewById(R.id.voice_note_player));
@ -431,10 +409,8 @@ public class ConversationListFragment extends MainFragment implements ActionMode
initializeListAdapters();
initializeTypingObserver();
initializeVoiceNotePlayer();
if (RemoteConfig.newBannerUi()) {
initializeBanners();
maybeScheduleRefreshProfileJob();
}
initializeBanners();
maybeScheduleRefreshProfileJob();
RatingManager.showRatingDialogIfNecessary(requireContext());
@ -473,7 +449,6 @@ public class ConversationListFragment extends MainFragment implements ActionMode
coordinator = null;
list = null;
bottomActionBar = null;
reminderView = null;
megaphoneContainer = null;
voiceNotePlayerViewStub = null;
fab = null;
@ -493,7 +468,6 @@ public class ConversationListFragment extends MainFragment implements ActionMode
super.onResume();
initializeSearchListener();
updateReminders();
EventBus.getDefault().register(this);
itemAnimator.disable();
SpoilerAnnotation.resetRevealedSpoilers();
@ -800,27 +774,6 @@ public class ConversationListFragment extends MainFragment implements ActionMode
dialogFragment.show(getChildFragmentManager(), "megaphone_dialog");
}
private void initializeReminderView() {
reminderView.get().setOnDismissListener(this::updateReminders);
reminderView.get().setOnActionClickListener(this::onReminderAction);
}
private void onReminderAction(@IdRes int reminderActionId) {
if (reminderActionId == R.id.reminder_action_update_now) {
PlayStoreUtil.openPlayStoreOrOurApkDownloadPage(requireContext());
} else if (reminderActionId == R.id.reminder_action_cds_temporary_error_learn_more) {
CdsTemporaryErrorBottomSheet.show(getChildFragmentManager());
} else if (reminderActionId == R.id.reminder_action_cds_permanent_error_learn_more) {
CdsPermanentErrorBottomSheet.show(getChildFragmentManager());
} else if (reminderActionId == R.id.reminder_action_fix_username_and_link) {
startActivityForResult(AppSettingsActivity.usernameRecovery(requireContext()), UsernameEditFragment.REQUEST_CODE);
} else if (reminderActionId == R.id.reminder_action_fix_username_link) {
startActivity(AppSettingsActivity.usernameLinkSettings(requireContext()));
} else if (reminderActionId == R.id.reminder_action_re_register) {
startActivity(RegistrationActivity.newIntentForReRegistration(requireContext()));
}
}
private void hideKeyboard() {
InputMethodManager imm = ServiceUtil.getInputMethodManager(requireContext());
imm.hideSoftInputFromWindow(requireView().getWindowToken(), 0);
@ -1094,46 +1047,6 @@ public class ConversationListFragment extends MainFragment implements ActionMode
viewModel.onMegaphoneVisible(megaphone);
}
private void updateReminders() {
if (RemoteConfig.newBannerUi()) {
return;
}
Context context = requireContext();
SimpleTask.run(getViewLifecycleOwner().getLifecycle(), () -> {
if (ExpiredBuildReminder.isEligible()) {
return Optional.of(new ExpiredBuildReminder(context));
} else if (UnauthorizedReminder.isEligible(context)) {
return Optional.of(new UnauthorizedReminder());
} else if (ServiceOutageReminder.isEligible(context)) {
AppDependencies.getJobManager().add(new ServiceOutageDetectionJob());
return Optional.of(new ServiceOutageReminder());
} else if (OutdatedBuildReminder.isEligible()) {
return Optional.of(new OutdatedBuildReminder(context));
} else if (DozeReminder.isEligible(context)) {
return Optional.of(new DozeReminder(context));
} else if (CdsTemporaryErrorReminder.isEligible()) {
return Optional.of(new CdsTemporaryErrorReminder());
} else if (CdsPermanentErrorReminder.isEligible()) {
return Optional.of(new CdsPermanentErrorReminder());
} else if (UsernameOutOfSyncReminder.isEligible()) {
AppDependencies.getJobManager().add(new RefreshOwnProfileJob());
return Optional.of(new UsernameOutOfSyncReminder());
} else {
return Optional.<Reminder>empty();
}
}, reminder -> {
if (reminder.isPresent() && getActivity() != null && !isRemoving()) {
if (!reminderView.resolved()) {
initializeReminderView();
}
reminderView.get().showReminder(reminder.get());
} else if (reminderView.resolved() && !reminder.isPresent()) {
reminderView.get().hide();
}
});
}
private void handleCreateGroup() {
getNavigator().goToGroupCreation();
}
@ -1578,11 +1491,6 @@ public class ConversationListFragment extends MainFragment implements ActionMode
endActionModeIfActive();
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEvent(ReminderUpdateEvent event) {
updateReminders();
}
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
public void onEvent(MessageSender.MessageSentEvent event) {
EventBus.getDefault().removeStickyEvent(event);

View file

@ -1,5 +0,0 @@
package org.thoughtcrime.securesms.events;
public class ReminderUpdateEvent {
}

View file

@ -3,10 +3,8 @@ package org.thoughtcrime.securesms.jobs;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.greenrobot.eventbus.EventBus;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.BuildConfig;
import org.thoughtcrime.securesms.events.ReminderUpdateEvent;
import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
import org.thoughtcrime.securesms.transport.RetryLaterException;
@ -74,7 +72,6 @@ public class ServiceOutageDetectionJob extends BaseJob {
}
TextSecurePreferences.setLastOutageCheckTime(context, System.currentTimeMillis());
EventBus.getDefault().post(new ReminderUpdateEvent());
} catch (UnknownHostException e) {
throw new RetryLaterException(e);
}
@ -90,7 +87,6 @@ public class ServiceOutageDetectionJob extends BaseJob {
Log.i(TAG, "Service status check could not complete. Assuming success to avoid false positives due to bad network.");
TextSecurePreferences.setServiceOutage(context, false);
TextSecurePreferences.setLastOutageCheckTime(context, System.currentTimeMillis());
EventBus.getDefault().post(new ReminderUpdateEvent());
}
public static final class Factory implements Job.Factory<ServiceOutageDetectionJob> {

View file

@ -66,7 +66,7 @@ public final class SignalPinReminderDialog {
EditText pinEditText = (EditText) DialogCompat.requireViewById(dialog, R.id.pin);
TextView pinStatus = (TextView) DialogCompat.requireViewById(dialog, R.id.pin_status);
TextView reminder = (TextView) DialogCompat.requireViewById(dialog, R.id.reminder);
TextView reminder = (TextView) DialogCompat.requireViewById(dialog, R.id.kbs_reminder_body);
View skip = DialogCompat.requireViewById(dialog, R.id.skip);
View submit = DialogCompat.requireViewById(dialog, R.id.submit);

View file

@ -30,8 +30,6 @@ import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.banner.Banner;
import org.thoughtcrime.securesms.banner.BannerManager;
import org.thoughtcrime.securesms.banner.banners.EnclaveFailureBanner;
import org.thoughtcrime.securesms.components.reminder.EnclaveFailureReminder;
import org.thoughtcrime.securesms.components.reminder.ReminderView;
import org.thoughtcrime.securesms.components.settings.app.AppSettingsActivity;
import org.thoughtcrime.securesms.help.HelpFragment;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
@ -107,7 +105,6 @@ public class PaymentsHomeFragment extends LoggingFragment {
View sendMoney = view.findViewById(R.id.button_end_frame);
View refresh = view.findViewById(R.id.payments_home_fragment_header_refresh);
LottieAnimationView refreshAnimation = view.findViewById(R.id.payments_home_fragment_header_refresh_animation);
Stub<ReminderView> reminderView = ViewUtil.findStubById(view, R.id.reminder);
Stub<ComposeView> bannerView = ViewUtil.findStubById(view, R.id.banner_compose_view);
toolbar.setNavigationOnClickListener(v -> {
@ -264,33 +261,15 @@ public class PaymentsHomeFragment extends LoggingFragment {
}
});
if (RemoteConfig.newBannerUi()) {
viewModel.getEnclaveFailure().observe(getViewLifecycleOwner(), failure -> {
if (failure) {
showUpdateIsRequiredDialog();
}
});
final Flow<Boolean> enclaveFailureFlow = FlowLiveDataConversions.asFlow(viewModel.getEnclaveFailure());
final List<Flow<? extends Banner>> bannerRepositories = List.of(EnclaveFailureBanner.Companion.mapBooleanFlowToBannerFlow(enclaveFailureFlow, requireContext()));
final BannerManager bannerManager = new BannerManager(bannerRepositories);
bannerManager.setContent(bannerView.get());
} else {
viewModel.getEnclaveFailure().observe(getViewLifecycleOwner(), failure -> {
if (failure) {
showUpdateIsRequiredDialog();
reminderView.get().showReminder(new EnclaveFailureReminder(requireContext()));
reminderView.get().setOnActionClickListener(actionId -> {
if (actionId == R.id.reminder_action_update_now) {
PlayStoreUtil.openPlayStoreOrOurApkDownloadPage(requireContext());
} else if (actionId == R.id.reminder_action_re_register) {
startActivity(RegistrationActivity.newIntentForReRegistration(requireContext()));
}
});
} else {
reminderView.get().requestDismiss();
}
});
}
viewModel.getEnclaveFailure().observe(getViewLifecycleOwner(), failure -> {
if (failure) {
showUpdateIsRequiredDialog();
}
});
final Flow<Boolean> enclaveFailureFlow = FlowLiveDataConversions.asFlow(viewModel.getEnclaveFailure());
final List<Flow<? extends Banner>> bannerRepositories = List.of(EnclaveFailureBanner.Companion.mapBooleanFlowToBannerFlow(enclaveFailureFlow, requireContext()));
final BannerManager bannerManager = new BannerManager(bannerRepositories);
bannerManager.setContent(bannerView.get());
requireActivity().getOnBackPressedDispatcher().addCallback(getViewLifecycleOwner(), new OnBackPressed());
}

View file

@ -10,7 +10,6 @@ import android.view.MenuItem
import android.view.View
import android.widget.Toast
import androidx.activity.OnBackPressedCallback
import androidx.annotation.IdRes
import androidx.compose.ui.platform.ComposeView
import androidx.core.app.ActivityOptionsCompat
import androidx.core.app.SharedElementCallback
@ -25,8 +24,6 @@ import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.core.Single
import io.reactivex.rxjava3.kotlin.subscribeBy
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
import org.signal.core.util.concurrent.LifecycleDisposable
import org.thoughtcrime.securesms.MainActivity
import org.thoughtcrime.securesms.R
@ -34,10 +31,6 @@ import org.thoughtcrime.securesms.banner.BannerManager
import org.thoughtcrime.securesms.banner.banners.OutdatedBuildBanner
import org.thoughtcrime.securesms.banner.banners.UnauthorizedBanner
import org.thoughtcrime.securesms.components.Material3SearchToolbar
import org.thoughtcrime.securesms.components.reminder.ExpiredBuildReminder
import org.thoughtcrime.securesms.components.reminder.Reminder
import org.thoughtcrime.securesms.components.reminder.ReminderView
import org.thoughtcrime.securesms.components.reminder.UnauthorizedReminder
import org.thoughtcrime.securesms.components.settings.DSLConfiguration
import org.thoughtcrime.securesms.components.settings.DSLSettingsFragment
import org.thoughtcrime.securesms.components.settings.DSLSettingsText
@ -48,13 +41,11 @@ import org.thoughtcrime.securesms.conversation.mutiselect.forward.MultiselectFor
import org.thoughtcrime.securesms.database.model.MmsMessageRecord
import org.thoughtcrime.securesms.database.model.StoryViewState
import org.thoughtcrime.securesms.dependencies.AppDependencies
import org.thoughtcrime.securesms.events.ReminderUpdateEvent
import org.thoughtcrime.securesms.main.Material3OnScrollHelperBinder
import org.thoughtcrime.securesms.main.SearchBinder
import org.thoughtcrime.securesms.mediasend.camerax.CameraXUtil
import org.thoughtcrime.securesms.mediasend.v2.MediaSelectionActivity
import org.thoughtcrime.securesms.permissions.Permissions
import org.thoughtcrime.securesms.registration.ui.RegistrationActivity
import org.thoughtcrime.securesms.safety.SafetyNumberBottomSheet
import org.thoughtcrime.securesms.stories.StoryTextPostModel
import org.thoughtcrime.securesms.stories.StoryViewerArgs
@ -65,8 +56,6 @@ import org.thoughtcrime.securesms.stories.settings.StorySettingsActivity
import org.thoughtcrime.securesms.stories.tabs.ConversationListTab
import org.thoughtcrime.securesms.stories.tabs.ConversationListTabsViewModel
import org.thoughtcrime.securesms.stories.viewer.StoryViewerActivity
import org.thoughtcrime.securesms.util.PlayStoreUtil
import org.thoughtcrime.securesms.util.RemoteConfig
import org.thoughtcrime.securesms.util.ViewUtil
import org.thoughtcrime.securesms.util.adapter.mapping.MappingAdapter
import org.thoughtcrime.securesms.util.fragments.requireListener
@ -86,7 +75,6 @@ class StoriesLandingFragment : DSLSettingsFragment(layoutId = R.layout.stories_l
private lateinit var emptyNotice: View
private lateinit var cameraFab: FloatingActionButton
private lateinit var reminderView: Stub<ReminderView>
private lateinit var bannerView: Stub<ComposeView>
private val lifecycleDisposable = LifecycleDisposable()
@ -149,63 +137,30 @@ class StoriesLandingFragment : DSLSettingsFragment(layoutId = R.layout.stories_l
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
reminderView = ViewUtil.findStubById(view, R.id.reminder)
bannerView = ViewUtil.findStubById(view, R.id.banner_stub)
updateReminders()
initializeBanners()
}
@Subscribe(threadMode = ThreadMode.MAIN)
fun onEvent(event: ReminderUpdateEvent?) {
updateReminders()
}
private fun updateReminders() {
if (RemoteConfig.newBannerUi) {
val bannerFlows = listOf(
OutdatedBuildBanner.createFlow(requireContext(), OutdatedBuildBanner.ExpiryStatus.EXPIRED_ONLY),
UnauthorizedBanner.createFlow(requireContext())
)
val bannerManager = BannerManager(bannerFlows)
bannerManager.setContent(bannerView.get())
} else {
if (ExpiredBuildReminder.isEligible()) {
showReminder(ExpiredBuildReminder(context))
} else if (UnauthorizedReminder.isEligible(context)) {
showReminder(UnauthorizedReminder())
} else {
hideReminders()
private fun initializeBanners() {
val bannerFlows = listOf(
OutdatedBuildBanner.createFlow(requireContext(), OutdatedBuildBanner.ExpiryStatus.EXPIRED_ONLY),
UnauthorizedBanner.createFlow(requireContext())
)
val bannerManager = BannerManager(
bannerFlows,
onNewBannerShownListener = {
if (bannerView.resolved()) {
bannerView.get().addOnLayoutChangeListener { _, _, top, _, bottom, _, _, _, _ ->
recyclerView?.setPadding(0, bottom - top, 0, 0)
}
recyclerView?.clipToPadding = false
}
},
onNoBannerShownListener = {
recyclerView?.clipToPadding = true
}
}
}
private fun showReminder(reminder: Reminder) {
if (!reminderView.resolved()) {
reminderView.get().addOnLayoutChangeListener { _, _, top, _, bottom, _, _, _, _ ->
recyclerView?.setPadding(0, bottom - top, 0, 0)
}
recyclerView?.clipToPadding = false
}
reminderView.get().showReminder(reminder)
reminderView.get().setOnActionClickListener { reminderActionId: Int -> this.handleReminderAction(reminderActionId) }
}
private fun hideReminders() {
if (reminderView.resolved()) {
reminderView.get().hide()
recyclerView?.clipToPadding = true
}
}
private fun handleReminderAction(@IdRes reminderActionId: Int) {
when (reminderActionId) {
R.id.reminder_action_update_now -> {
PlayStoreUtil.openPlayStoreOrOurApkDownloadPage(requireContext())
}
R.id.reminder_action_re_register -> {
startActivity(RegistrationActivity.newIntentForReRegistration(requireContext()))
}
}
)
bannerManager.setContent(bannerView.get())
}
override fun bindAdapter(adapter: MappingAdapter) {

View file

@ -1082,15 +1082,6 @@ object RemoteConfig {
hotSwappable = true
)
/** Whether to use the new Banner system instead of the old Reminder system. */
@JvmStatic
@get:JvmName("newBannerUi")
val newBannerUi: Boolean by remoteBoolean(
key = "android.newBannerUi",
defaultValue = false,
hotSwappable = true
)
/** Which phase we're in for the SVR3 migration */
val svr3MigrationPhase: Int by remoteInt(
key = "global.svr3.phase",

View file

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<org.thoughtcrime.securesms.components.reminder.ReminderView
xmlns:tools="http://schemas.android.com/tools"
tools:viewBindingIgnore="true"
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/reminder"
android:layout_width="match_parent"
android:layout_height="wrap_content" />

View file

@ -8,4 +8,4 @@
android:id="@+id/banner_compose_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/reminder" />
app:layout_constraintTop_toTopOf="parent" />

View file

@ -17,28 +17,20 @@
android:layout="@layout/voice_note_player_stub"
app:layout_constraintTop_toTopOf="parent" />
<ViewStub
android:id="@+id/reminder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inflatedId="@+id/reminder"
android:layout="@layout/conversation_list_reminder_view"
app:layout_constraintTop_toTopOf="parent" />
<ViewStub
android:id="@+id/banner_compose_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inflatedId="@+id/banner_compose_view"
android:layout="@layout/conversation_list_banner_view"
app:layout_constraintTop_toBottomOf="@id/reminder" />
app:layout_constraintTop_toTopOf="parent" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/banner_barrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="bottom"
app:constraint_referenced_ids="reminder,voice_note_player,banner_compose_view" />
app:constraint_referenced_ids="voice_note_player,banner_compose_view" />
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"

View file

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<org.thoughtcrime.securesms.components.reminder.ReminderView
xmlns:tools="http://schemas.android.com/tools"
tools:viewBindingIgnore="true"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/reminder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/toolbar_barrier" />

View file

@ -19,14 +19,6 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/toolbar" />
<ViewStub
android:id="@+id/reminder_stub"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inflatedId="@+id/reminder"
android:layout="@layout/conversation_activity_reminderview_stub"
app:layout_constraintTop_toTopOf="@id/recycler"/>
<ViewStub
android:id="@+id/banner_stub"
android:layout_width="match_parent"

View file

@ -49,7 +49,7 @@
</TextView>
<TextView
android:id="@+id/reminder"
android:id="@+id/kbs_reminder_body"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:lineSpacingMultiplier="1.3"
@ -76,7 +76,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/KbsReminderDialog__submit"
app:layout_constraintTop_toBottomOf="@id/reminder"
app:layout_constraintTop_toBottomOf="@id/kbs_reminder_body"
app:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -20,14 +20,6 @@
app:title="@string/preferences__payments"
app:titleTextAppearance="@style/Signal.Text.TitleLarge" />
<ViewStub
android:id="@+id/reminder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inflatedId="@+id/reminder"
android:layout="@layout/conversation_list_reminder_view"
app:layout_constraintTop_toBottomOf="@id/payments_home_fragment_toolbar" />
<ViewStub
android:id="@+id/banner_compose_view"

View file

@ -100,14 +100,6 @@
app:layout_constraintTop_toTopOf="parent"
tools:visibility="visible" />
<ViewStub
android:id="@+id/reminder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inflatedId="@+id/reminder"
android:layout="@layout/stories_landing_reminder_view"
app:layout_constraintTop_toTopOf="parent" />
<ViewStub
android:id="@+id/banner_stub"
android:layout_width="match_parent"

View file

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<org.thoughtcrime.securesms.components.reminder.ReminderView
xmlns:tools="http://schemas.android.com/tools"
tools:viewBindingIgnore="true"
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/reminder"
android:layout_width="match_parent"
android:layout_height="wrap_content" />

View file

@ -166,13 +166,6 @@
android:inflatedId="@+id/unverified_banner"
android:layout="@layout/conversation_activity_unverified_banner_stub" />
<ViewStub
android:id="@+id/reminder_stub"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inflatedId="@+id/reminder"
android:layout="@layout/conversation_activity_reminderview_stub" />
<ViewStub
android:id="@+id/review_banner_stub"
android:layout_width="match_parent"