Add Reminders and Conversation Banner to CFv2.

This commit is contained in:
Cody Henthorne
2023-05-24 22:47:05 -04:00
parent 0aca03a919
commit 6b91e525db
35 changed files with 501 additions and 182 deletions

View File

@@ -1,13 +1,12 @@
package org.thoughtcrime.securesms.components.reminder
import android.content.Context
import org.thoughtcrime.securesms.R
class BubbleOptOutReminder(context: Context) : Reminder(null, context.getString(R.string.BubbleOptOutTooltip__description)) {
class BubbleOptOutReminder : Reminder(R.string.BubbleOptOutTooltip__description) {
init {
addAction(Action(context.getString(R.string.BubbleOptOutTooltip__turn_off), R.id.reminder_action_turn_off))
addAction(Action(context.getString(R.string.BubbleOptOutTooltip__not_now), R.id.reminder_action_not_now))
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 {

View File

@@ -1,6 +1,5 @@
package org.thoughtcrime.securesms.components.reminder
import android.content.Context
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.keyvalue.SignalStore
import kotlin.time.Duration.Companion.days
@@ -8,12 +7,12 @@ import kotlin.time.Duration.Companion.days
/**
* Reminder shown when CDS is in a permanent error state, preventing us from doing a sync.
*/
class CdsPermanentErrorReminder(context: Context) : Reminder(null, context.getString(R.string.reminder_cds_permanent_error_body)) {
class CdsPermanentErrorReminder : Reminder(R.string.reminder_cds_permanent_error_body) {
init {
addAction(
Action(
context.getString(R.string.reminder_cds_permanent_error_learn_more),
R.string.reminder_cds_permanent_error_learn_more,
R.id.reminder_action_cds_permanent_error_learn_more
)
)

View File

@@ -1,18 +1,17 @@
package org.thoughtcrime.securesms.components.reminder
import android.content.Context
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 CdsTemporyErrorReminder(context: Context) : Reminder(null, context.getString(R.string.reminder_cds_warning_body)) {
class CdsTemporaryErrorReminder : Reminder(R.string.reminder_cds_warning_body) {
init {
addAction(
Action(
context.getString(R.string.reminder_cds_warning_learn_more),
R.string.reminder_cds_warning_learn_more,
R.id.reminder_action_cds_temporary_error_learn_more
)
)

View File

@@ -19,10 +19,9 @@ import org.thoughtcrime.securesms.util.TextSecurePreferences;
@SuppressLint("BatteryLife")
public class DozeReminder extends Reminder {
@RequiresApi(api = Build.VERSION_CODES.M)
@RequiresApi(api = 23)
public DozeReminder(@NonNull final Context context) {
super(context.getString(R.string.DozeReminder_optimize_for_missing_play_services),
context.getString(R.string.DozeReminder_this_device_does_not_support_play_services_tap_to_disable_system_battery));
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);
@@ -40,5 +39,4 @@ public class DozeReminder extends Reminder {
Build.VERSION.SDK_INT >= 23 &&
!((PowerManager)context.getSystemService(Context.POWER_SERVICE)).isIgnoringBatteryOptimizations(context.getPackageName());
}
}

View File

@@ -8,13 +8,10 @@ import org.thoughtcrime.securesms.util.PlayStoreUtil
/**
* Banner to update app to the latest version because of enclave failure
*/
class EnclaveFailureReminder(context: Context) : Reminder(
null,
context.getString(R.string.EnclaveFailureReminder_update_signal)
) {
class EnclaveFailureReminder(context: Context) : Reminder(R.string.EnclaveFailureReminder_update_signal) {
init {
addAction(Action(context.getString(R.string.ExpiredBuildReminder_update_now), R.id.reminder_action_update_now))
addAction(Action(R.string.ExpiredBuildReminder_update_now, R.id.reminder_action_update_now))
okListener = View.OnClickListener { PlayStoreUtil.openPlayStoreOrOurApkDownloadPage(context) }
}

View File

@@ -19,8 +19,8 @@ import java.util.List;
public class ExpiredBuildReminder extends Reminder {
public ExpiredBuildReminder(final Context context) {
super(null, context.getString(R.string.ExpiredBuildReminder_this_version_of_signal_has_expired));
addAction(new Action(context.getString(R.string.ExpiredBuildReminder_update_now), R.id.reminder_action_update_now));
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
@@ -28,11 +28,6 @@ public class ExpiredBuildReminder extends Reminder {
return false;
}
@Override
public List<Action> getActions() {
return super.getActions();
}
@Override
public @NonNull Importance getImportance() {
return Importance.TERMINAL;

View File

@@ -5,17 +5,21 @@ import android.content.Context;
import androidx.annotation.NonNull;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.recipients.Recipient;
public final class FirstInviteReminder extends Reminder {
public FirstInviteReminder(final @NonNull Context context,
final @NonNull Recipient recipient,
final int percentIncrease) {
super(context.getString(R.string.FirstInviteReminder__title),
context.getString(R.string.FirstInviteReminder__description, percentIncrease));
private final int percentIncrease;
addAction(new Action(context.getString(R.string.InsightsReminder__invite), R.id.reminder_action_invite));
addAction(new Action(context.getString(R.string.InsightsReminder__view_insights), R.id.reminder_action_view_insights));
public FirstInviteReminder(final int percentIncrease) {
super(R.string.FirstInviteReminder__title, NO_RESOURCE);
this.percentIncrease = percentIncrease;
addAction(new Action(R.string.InsightsReminder__invite, R.id.reminder_action_invite));
addAction(new Action(R.string.InsightsReminder__view_insights, R.id.reminder_action_view_insights));
}
@Override
public @NonNull CharSequence getText(@NonNull Context context) {
return context.getString(R.string.FirstInviteReminder__description, percentIncrease);
}
}

View File

@@ -13,14 +13,36 @@ import java.util.List;
* Shows a reminder to add anyone that might have been missed in GV1->GV2 migration.
*/
public class GroupsV1MigrationSuggestionsReminder extends Reminder {
public GroupsV1MigrationSuggestionsReminder(@NonNull Context context, @NonNull List<RecipientId> suggestions) {
super(null, context.getResources().getQuantityString(R.plurals.GroupsV1MigrationSuggestionsReminder_members_couldnt_be_added_to_the_new_group, suggestions.size(), suggestions.size()));
addAction(new Action(context.getResources().getQuantityString(R.plurals.GroupsV1MigrationSuggestionsReminder_add_members, suggestions.size()), R.id.reminder_action_gv1_suggestion_add_members));
addAction(new Action(context.getResources().getString(R.string.GroupsV1MigrationSuggestionsReminder_no_thanks), R.id.reminder_action_gv1_suggestion_no_thanks));
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

@@ -2,6 +2,8 @@ package org.thoughtcrime.securesms.components.reminder;
import android.content.Context;
import androidx.annotation.NonNull;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.util.PlayStoreUtil;
import org.thoughtcrime.securesms.util.Util;
@@ -15,13 +17,12 @@ import java.util.concurrent.TimeUnit;
public class OutdatedBuildReminder extends Reminder {
public OutdatedBuildReminder(final Context 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));
addAction(new Action(R.string.OutdatedBuildReminder_update_now, R.id.reminder_action_update_now));
}
private static CharSequence getPluralsText(final Context context) {
@Override
public @NonNull CharSequence getText(@NonNull Context context) {
int days = getDaysUntilExpiry();
if (days == 0) {

View File

@@ -3,7 +3,6 @@ package org.thoughtcrime.securesms.components.reminder;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.thoughtcrime.securesms.R;
@@ -12,19 +11,17 @@ import org.thoughtcrime.securesms.R;
*/
public final class PendingGroupJoinRequestsReminder extends Reminder {
private PendingGroupJoinRequestsReminder(@Nullable CharSequence title,
@NonNull CharSequence text)
{
super(title, text);
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));
}
public static Reminder create(@NonNull Context context, int count) {
String message = context.getResources().getQuantityString(R.plurals.PendingGroupJoinRequestsReminder_d_pending_member_requests, count, count);
Reminder reminder = new PendingGroupJoinRequestsReminder(null, message);
reminder.addAction(new Action(context.getString(R.string.PendingGroupJoinRequestsReminder_view), R.id.reminder_action_review_join_requests));
return reminder;
@Override
public @NonNull CharSequence getText(@NonNull Context context) {
return context.getResources().getQuantityString(R.plurals.PendingGroupJoinRequestsReminder_d_pending_member_requests, count, count);
}
@Override

View File

@@ -9,8 +9,7 @@ import org.thoughtcrime.securesms.registration.RegistrationNavigationActivity;
public class PushRegistrationReminder extends Reminder {
public PushRegistrationReminder(final Context context) {
super(context.getString(R.string.reminder_header_push_title),
context.getString(R.string.reminder_header_push_text));
super(R.string.reminder_header_push_title, R.string.reminder_header_push_text);
setOkListener(v -> context.startActivity(RegistrationNavigationActivity.newIntentForReRegistration(context)));
}
@@ -20,7 +19,7 @@ public class PushRegistrationReminder extends Reminder {
return false;
}
public static boolean isEligible(Context context) {
public static boolean isEligible() {
return !SignalStore.account().isRegistered();
}
}

View File

@@ -1,36 +1,57 @@
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 {
private CharSequence title;
private CharSequence text;
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<>();
public Reminder(@Nullable CharSequence title,
@NonNull CharSequence text)
{
this.title = title;
this.text = text;
/**
* 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 @Nullable CharSequence getTitle() {
return title;
public Reminder(@StringRes int textRes) {
this(NO_RESOURCE, textRes);
}
public CharSequence getText() {
return text;
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() {
@@ -73,17 +94,22 @@ public abstract class Reminder {
NORMAL, ERROR, TERMINAL
}
public static final class Action {
private final CharSequence title;
private final int actionId;
public static class Action {
private final int title;
private final int actionId;
public Action(CharSequence title, @IdRes 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() {
return title;
public CharSequence getTitle(@NonNull Context context) {
Preconditions.checkArgument(title != NO_RESOURCE);
return context.getText(title);
}
public int getActionId() {

View File

@@ -47,7 +47,7 @@ final class ReminderActionsAdapter extends RecyclerView.Adapter<ReminderActionsA
public void onBindViewHolder(@NonNull ActionViewHolder holder, int position) {
final Reminder.Action action = actions.get(position);
((Button) holder.itemView).setText(action.getTitle());
((Button) holder.itemView).setText(action.getTitle(holder.itemView.getContext()));
holder.itemView.setOnClickListener(v -> {
if (holder.getAdapterPosition() == RecyclerView.NO_POSITION) return;

View File

@@ -38,6 +38,7 @@ public final class ReminderView extends FrameLayout {
private Space space;
private RecyclerView actionsRecycler;
private OnActionClickListener actionClickListener;
private OnHideListener onHideListener;
public ReminderView(Context context) {
super(context);
@@ -67,8 +68,8 @@ public final class ReminderView extends FrameLayout {
}
public void showReminder(final Reminder reminder) {
if (!TextUtils.isEmpty(reminder.getTitle())) {
title.setText(reminder.getTitle());
if (!TextUtils.isEmpty(reminder.getTitle(getContext()))) {
title.setText(reminder.getTitle(getContext()));
title.setVisibility(VISIBLE);
space.setVisibility(GONE);
} else {
@@ -82,7 +83,7 @@ public final class ReminderView extends FrameLayout {
space.setVisibility(GONE);
}
text.setText(reminder.getText());
text.setText(reminder.getText(getContext()));
switch (reminder.getImportance()) {
case NORMAL:
title.setTextColor(ContextCompat.getColor(getContext(), R.color.signal_colorOnSurface));
@@ -153,11 +154,19 @@ public final class ReminderView extends FrameLayout {
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);
}
@@ -168,4 +177,8 @@ public final class ReminderView extends FrameLayout {
public interface OnActionClickListener {
void onActionClick(@IdRes int actionId);
}
public interface OnHideListener {
boolean onHide();
}
}

View File

@@ -9,19 +9,25 @@ import org.thoughtcrime.securesms.recipients.Recipient;
public final class SecondInviteReminder extends Reminder {
private final int progress;
private final Recipient recipient;
private final int progress;
public SecondInviteReminder(final @NonNull Context context,
final @NonNull Recipient recipient,
final int percent)
{
super(context.getString(R.string.SecondInviteReminder__title),
context.getString(R.string.SecondInviteReminder__description, recipient.getDisplayName(context)));
super(R.string.SecondInviteReminder__title, NO_RESOURCE);
this.recipient = recipient;
this.progress = percent;
addAction(new Action(context.getString(R.string.InsightsReminder__invite), R.id.reminder_action_invite));
addAction(new Action(context.getString(R.string.InsightsReminder__view_insights), R.id.reminder_action_view_insights));
addAction(new Action(R.string.InsightsReminder__invite, R.id.reminder_action_invite));
addAction(new Action(R.string.InsightsReminder__view_insights, R.id.reminder_action_view_insights));
}
@Override
public @NonNull CharSequence getText(@NonNull Context context) {
return context.getString(R.string.SecondInviteReminder__description, recipient.getDisplayName(context));
}
@Override

View File

@@ -9,9 +9,8 @@ import org.thoughtcrime.securesms.util.TextSecurePreferences;
public class ServiceOutageReminder extends Reminder {
public ServiceOutageReminder(@NonNull Context context) {
super(null,
context.getString(R.string.reminder_header_service_outage_text));
public ServiceOutageReminder() {
super(R.string.reminder_header_service_outage_text);
}
public static boolean isEligible(@NonNull Context context) {

View File

@@ -11,14 +11,13 @@ import org.thoughtcrime.securesms.util.TextSecurePreferences;
public class UnauthorizedReminder extends Reminder {
public UnauthorizedReminder(final Context context) {
super(null,
context.getString(R.string.UnauthorizedReminder_this_is_likely_because_you_registered_your_phone_number_with_Signal_on_a_different_device));
super(R.string.UnauthorizedReminder_this_is_likely_because_you_registered_your_phone_number_with_Signal_on_a_different_device);
setOkListener(v -> {
context.startActivity(RegistrationNavigationActivity.newIntentForReRegistration(context));
});
addAction(new Action(context.getString(R.string.UnauthorizedReminder_reregister_action), R.id.reminder_action_re_register));
addAction(new Action(R.string.UnauthorizedReminder_reregister_action, R.id.reminder_action_re_register));
}
@Override

View File

@@ -1,6 +1,5 @@
package org.thoughtcrime.securesms.components.reminder
import android.content.Context
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.util.FeatureFlags
@@ -9,15 +8,12 @@ import org.thoughtcrime.securesms.util.FeatureFlags
* Displays a reminder message when the local username gets out of sync with
* what the server thinks our username is.
*/
class UsernameOutOfSyncReminder(context: Context) : Reminder(
null,
context.getString(R.string.UsernameOutOfSyncReminder__something_went_wrong)
) {
class UsernameOutOfSyncReminder : Reminder(R.string.UsernameOutOfSyncReminder__something_went_wrong) {
init {
addAction(
Action(
context.getString(R.string.UsernameOutOfSyncReminder__fix_now),
R.string.UsernameOutOfSyncReminder__fix_now,
R.id.reminder_action_fix_username
)
)