diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/PinValues.java b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/PinValues.java
index 828d646c5f..2b5821e787 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/PinValues.java
+++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/PinValues.java
@@ -1,6 +1,5 @@
package org.thoughtcrime.securesms.keyvalue;
-import androidx.annotation.CheckResult;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -21,6 +20,7 @@ public final class PinValues {
private static final String NEXT_INTERVAL = "pin.interval_index";
private static final String KEYBOARD_TYPE = "kbs.keyboard_type";
private static final String PIN_STATE = "pin.pin_state";
+ public static final String PIN_REMINDERS_ENABLED = "pin.pin_reminders_enabled";
private final KeyValueStore store;
@@ -81,6 +81,14 @@ public final class PinValues {
.commit();
}
+ public void setPinRemindersEnabled(boolean enabled) {
+ store.beginWrite().putBoolean(PIN_REMINDERS_ENABLED, enabled).apply();
+ }
+
+ public boolean arePinRemindersEnabled() {
+ return store.getBoolean(PIN_REMINDERS_ENABLED, true);
+ }
+
public @NonNull PinKeyboardType getKeyboardType() {
return PinKeyboardType.fromCode(store.getString(KEYBOARD_TYPE, null));
}
diff --git a/app/src/main/java/org/thoughtcrime/securesms/megaphone/SignalPinReminderSchedule.java b/app/src/main/java/org/thoughtcrime/securesms/megaphone/SignalPinReminderSchedule.java
index 29bbafa8b0..6da7d66819 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/megaphone/SignalPinReminderSchedule.java
+++ b/app/src/main/java/org/thoughtcrime/securesms/megaphone/SignalPinReminderSchedule.java
@@ -15,6 +15,10 @@ final class SignalPinReminderSchedule implements MegaphoneSchedule {
return false;
}
+ if (!SignalStore.pinValues().arePinRemindersEnabled()) {
+ return false;
+ }
+
long lastSuccessTime = SignalStore.pinValues().getLastSuccessfulEntryTime();
long interval = SignalStore.pinValues().getCurrentInterval();
diff --git a/app/src/main/java/org/thoughtcrime/securesms/preferences/AppProtectionPreferenceFragment.java b/app/src/main/java/org/thoughtcrime/securesms/preferences/AppProtectionPreferenceFragment.java
index f0711c8a45..9ced2f337c 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/preferences/AppProtectionPreferenceFragment.java
+++ b/app/src/main/java/org/thoughtcrime/securesms/preferences/AppProtectionPreferenceFragment.java
@@ -3,11 +3,26 @@ package org.thoughtcrime.securesms.preferences;
import android.app.KeyguardManager;
import android.content.Context;
import android.content.Intent;
+import android.graphics.Typeface;
import android.os.Bundle;
+import android.text.InputType;
+import android.text.TextUtils;
+import android.util.DisplayMetrics;
+import android.view.Display;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.widget.EditText;
+import android.widget.ProgressBar;
+import android.widget.TextView;
import android.widget.Toast;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
+import androidx.autofill.HintConstants;
+import androidx.core.app.DialogCompat;
+import androidx.core.view.ViewCompat;
import androidx.preference.CheckBoxPreference;
import androidx.preference.Preference;
@@ -19,15 +34,20 @@ import org.thoughtcrime.securesms.BlockedContactsActivity;
import org.thoughtcrime.securesms.PassphraseChangeActivity;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.components.SwitchPreferenceCompat;
+import org.thoughtcrime.securesms.contactshare.SimpleTextWatcher;
import org.thoughtcrime.securesms.crypto.MasterSecretUtil;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.jobs.MultiDeviceConfigurationUpdateJob;
import org.thoughtcrime.securesms.jobs.RefreshAttributesJob;
import org.thoughtcrime.securesms.keyvalue.KbsValues;
+import org.thoughtcrime.securesms.keyvalue.PinValues;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
+import org.thoughtcrime.securesms.lock.PinHashing;
import org.thoughtcrime.securesms.lock.RegistrationLockV1Dialog;
+import org.thoughtcrime.securesms.lock.SignalPinReminderDialog;
import org.thoughtcrime.securesms.lock.v2.CreateKbsPinActivity;
+import org.thoughtcrime.securesms.lock.v2.KbsConstants;
import org.thoughtcrime.securesms.lock.v2.RegistrationLockUtil;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.pin.PinState;
@@ -37,13 +57,17 @@ import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.storage.StorageSyncHelper;
import org.thoughtcrime.securesms.util.CommunicationActions;
import org.thoughtcrime.securesms.util.FeatureFlags;
+import org.thoughtcrime.securesms.util.ServiceUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
+import org.thoughtcrime.securesms.util.ThemeUtil;
import org.thoughtcrime.securesms.util.concurrent.SignalExecutors;
import org.thoughtcrime.securesms.util.concurrent.SimpleTask;
import org.thoughtcrime.securesms.util.views.SimpleProgressDialog;
+import org.w3c.dom.Text;
import java.io.IOException;
import java.util.Locale;
+import java.util.Objects;
import java.util.concurrent.TimeUnit;
import mobi.upod.timedurationpicker.TimeDurationPickerDialog;
@@ -62,10 +86,15 @@ public class AppProtectionPreferenceFragment extends CorrectedPreferenceFragment
super.onCreate(paramBundle);
disablePassphrase = (CheckBoxPreference) this.findPreference("pref_enable_passphrase_temporary");
+
this.findPreference(KbsValues.V2_LOCK_ENABLED).setPreferenceDataStore(SignalStore.getPreferenceDataStore());
((SwitchPreferenceCompat) this.findPreference(KbsValues.V2_LOCK_ENABLED)).setChecked(SignalStore.kbsValues().isV2RegistrationLockEnabled());
this.findPreference(KbsValues.V2_LOCK_ENABLED).setOnPreferenceChangeListener(new RegistrationLockV2ChangedListener());
+ this.findPreference(PinValues.PIN_REMINDERS_ENABLED).setPreferenceDataStore(SignalStore.getPreferenceDataStore());
+ ((SwitchPreferenceCompat) this.findPreference(PinValues.PIN_REMINDERS_ENABLED)).setChecked(SignalStore.pinValues().arePinRemindersEnabled());
+ this.findPreference(PinValues.PIN_REMINDERS_ENABLED).setOnPreferenceChangeListener(new PinRemindersChangedListener());
+
this.findPreference(TextSecurePreferences.SCREEN_LOCK).setOnPreferenceChangeListener(new ScreenLockListener());
this.findPreference(TextSecurePreferences.SCREEN_LOCK_TIMEOUT).setOnPreferenceClickListener(new ScreenLockTimeoutListener());
@@ -102,6 +131,7 @@ public class AppProtectionPreferenceFragment extends CorrectedPreferenceFragment
SwitchPreferenceCompat registrationLockV1 = (SwitchPreferenceCompat) this.findPreference(TextSecurePreferences.REGISTRATION_LOCK_PREF_V1);
Preference signalPinGroup = this.findPreference("prefs_signal_pin");
Preference signalPinCreateChange = this.findPreference(TextSecurePreferences.SIGNAL_PIN_CHANGE);
+ SwitchPreferenceCompat signalPinReminders = (SwitchPreferenceCompat) this.findPreference(PinValues.PIN_REMINDERS_ENABLED);
SwitchPreferenceCompat registrationLockV2 = (SwitchPreferenceCompat) this.findPreference(KbsValues.V2_LOCK_ENABLED);
@@ -115,6 +145,7 @@ public class AppProtectionPreferenceFragment extends CorrectedPreferenceFragment
} else {
signalPinCreateChange.setOnPreferenceClickListener(new KbsPinCreateListener());
signalPinCreateChange.setTitle(R.string.preferences_app_protection__create_a_pin);
+ signalPinReminders.setEnabled(false);
registrationLockV2.setEnabled(false);
}
} else {
@@ -430,4 +461,75 @@ public class AppProtectionPreferenceFragment extends CorrectedPreferenceFragment
return false;
}
}
+
+ private class PinRemindersChangedListener implements Preference.OnPreferenceChangeListener {
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ boolean value = (boolean) newValue;
+
+ if (!value) {
+ Context context = preference.getContext();
+ DisplayMetrics metrics = preference.getContext().getResources().getDisplayMetrics();
+ AlertDialog dialog = new AlertDialog.Builder(context, ThemeUtil.isDarkTheme(context) ? R.style.Theme_Signal_AlertDialog_Dark_Cornered_ColoredAccent : R.style.Theme_Signal_AlertDialog_Light_Cornered_ColoredAccent)
+ .setView(R.layout.pin_disable_reminders_dialog)
+ .create();
+
+
+ dialog.show();
+ dialog.getWindow().setLayout((int)(metrics.widthPixels * .80), ViewGroup.LayoutParams.WRAP_CONTENT);
+
+ EditText pinEditText = (EditText) DialogCompat.requireViewById(dialog, R.id.reminder_disable_pin);
+ TextView statusText = (TextView) DialogCompat.requireViewById(dialog, R.id.reminder_disable_status);
+ View cancelButton = DialogCompat.requireViewById(dialog, R.id.reminder_disable_cancel);
+ View turnOffButton = DialogCompat.requireViewById(dialog, R.id.reminder_disable_turn_off);
+
+ pinEditText.post(() -> {
+ if (pinEditText.requestFocus()) {
+ ServiceUtil.getInputMethodManager(pinEditText.getContext()).showSoftInput(pinEditText, 0);
+ }
+ });
+
+ ViewCompat.setAutofillHints(pinEditText, HintConstants.AUTOFILL_HINT_PASSWORD);
+
+ switch (SignalStore.pinValues().getKeyboardType()) {
+ case NUMERIC:
+ pinEditText.setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_PASSWORD);
+ break;
+ case ALPHA_NUMERIC:
+ pinEditText.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
+ break;
+ default:
+ throw new AssertionError("Unexpected type!");
+ }
+
+ pinEditText.addTextChangedListener(new SimpleTextWatcher() {
+ @Override
+ public void onTextChanged(String text) {
+ turnOffButton.setEnabled(text.length() >= KbsConstants.MINIMUM_PIN_LENGTH);
+ }
+ });
+
+ pinEditText.setTypeface(Typeface.DEFAULT);
+
+ turnOffButton.setOnClickListener(v -> {
+ String pin = pinEditText.getText().toString();
+ boolean correct = PinHashing.verifyLocalPinHash(Objects.requireNonNull(SignalStore.kbsValues().getLocalPinHash()), pin);
+
+ if (correct) {
+ SignalStore.pinValues().setPinRemindersEnabled(false);
+ ((SwitchPreferenceCompat) findPreference(PinValues.PIN_REMINDERS_ENABLED)).setChecked(false);
+ dialog.dismiss();
+ } else {
+ statusText.setText(R.string.preferences_app_protection__incorrect_pin_try_again);
+ }
+ });
+
+ cancelButton.setOnClickListener(v -> dialog.dismiss());
+
+ return false;
+ } else {
+ return true;
+ }
+ }
+ }
}
diff --git a/app/src/main/res/layout/pin_disable_reminders_dialog.xml b/app/src/main/res/layout/pin_disable_reminders_dialog.xml
new file mode 100644
index 0000000000..ecb3f98a9e
--- /dev/null
+++ b/app/src/main/res/layout/pin_disable_reminders_dialog.xml
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index bbc88d9316..2048ecf636 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -2195,8 +2195,15 @@
Signal PIN
Create a PIN
Change your PIN
+ PIN reminders
PINs keep information stored with Signal encrypted so only you can access it. Your profile, settings, and contacts will restore when you reinstall Signal.
Add extra security by requiring your Signal PIN to register your phone number with Signal again.
+ Reminders help you remember your PIN since it can\'t be recovered. You\'ll be asked less frequently over time.
+ Turn off
+ Confirm PIN
+ Confirm your Signal PIN
+ Make sure you memorize or securely store your PIN as it can\'t be recovered. If you forget your PIN, you may lose data when re-registering your Signal account.
+ Incorrect PIN. Try again.
Failed to enable registration lock.
Failed to disable registration lock.
None
@@ -2226,7 +2233,7 @@
I forgot my PIN.
Forgotten PIN?
Registration Lock helps protect your phone number from unauthorized registration attempts. This feature can be disabled at any time in your Signal privacy settings
- Registration Lock
+ Registration lock
Enable
The Registration Lock PIN must be at least %d digits.
The two PINs you entered do not match.
diff --git a/app/src/main/res/xml/preferences_app_protection.xml b/app/src/main/res/xml/preferences_app_protection.xml
index d8b8fa4d7f..37416c2477 100644
--- a/app/src/main/res/xml/preferences_app_protection.xml
+++ b/app/src/main/res/xml/preferences_app_protection.xml
@@ -115,6 +115,11 @@
android:summary="@string/preferences_app_protection__pins_keep_information_stored_with_signal_encrypted"
android:title="@string/preferences_app_protection__change_your_pin" />
+
+