mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-02-23 19:26:17 +00:00
Update screen lock.
This commit is contained in:
committed by
mtang-signal
parent
c880db0f4a
commit
3bdbd69a7d
@@ -33,7 +33,7 @@ public final class AppInitialization {
|
||||
TextSecurePreferences.setJobManagerVersion(context, JobManager.CURRENT_VERSION);
|
||||
TextSecurePreferences.setLastVersionCode(context, Util.getCanonicalVersionCode());
|
||||
TextSecurePreferences.setHasSeenStickerIntroTooltip(context, true);
|
||||
TextSecurePreferences.setPasswordDisabled(context, true);
|
||||
SignalStore.settings().setPassphraseDisabled(true);
|
||||
TextSecurePreferences.setReadReceiptsEnabled(context, true);
|
||||
TextSecurePreferences.setTypingIndicatorsEnabled(context, true);
|
||||
TextSecurePreferences.setHasSeenWelcomeScreen(context, false);
|
||||
@@ -54,7 +54,7 @@ public final class AppInitialization {
|
||||
SignalStore.onFirstEverAppLaunch();
|
||||
SignalStore.onboarding().clearAll();
|
||||
TextSecurePreferences.onPostBackupRestore(context);
|
||||
TextSecurePreferences.setPasswordDisabled(context, true);
|
||||
SignalStore.settings().setPassphraseDisabled(true);
|
||||
AppDependencies.getJobManager().add(StickerPackDownloadJob.forInstall(BlessedPacks.ZOZO.getPackId(), BlessedPacks.ZOZO.getPackKey(), false));
|
||||
AppDependencies.getJobManager().add(StickerPackDownloadJob.forInstall(BlessedPacks.BANDIT.getPackId(), BlessedPacks.BANDIT.getPackKey(), false));
|
||||
AppDependencies.getJobManager().add(StickerPackDownloadJob.forInstall(BlessedPacks.DAY_BY_DAY.getPackId(), BlessedPacks.DAY_BY_DAY.getPackKey(), false));
|
||||
@@ -73,7 +73,7 @@ public final class AppInitialization {
|
||||
TextSecurePreferences.setJobManagerVersion(context, JobManager.CURRENT_VERSION);
|
||||
TextSecurePreferences.setLastVersionCode(context, Util.getCanonicalVersionCode());
|
||||
TextSecurePreferences.setHasSeenStickerIntroTooltip(context, true);
|
||||
TextSecurePreferences.setPasswordDisabled(context, true);
|
||||
SignalStore.settings().setPassphraseDisabled(true);
|
||||
AppDependencies.getMegaphoneRepository().onFirstEverAppLaunch();
|
||||
SignalStore.onFirstEverAppLaunch();
|
||||
AppDependencies.getJobManager().add(StickerPackDownloadJob.forInstall(BlessedPacks.ZOZO.getPackId(), BlessedPacks.ZOZO.getPackKey(), false));
|
||||
|
||||
@@ -376,12 +376,12 @@ public class ApplicationContext extends MultiDexApplication implements AppForegr
|
||||
|
||||
Log.i(TAG, "Setting first install version to " + BuildConfig.CANONICAL_VERSION_CODE);
|
||||
TextSecurePreferences.setFirstInstallVersion(this, BuildConfig.CANONICAL_VERSION_CODE);
|
||||
} else if (!TextSecurePreferences.isPasswordDisabled(this) && VersionTracker.getDaysSinceFirstInstalled(this) < 90) {
|
||||
} else if (!SignalStore.settings().getPassphraseDisabled() && VersionTracker.getDaysSinceFirstInstalled(this) < 90) {
|
||||
Log.i(TAG, "Detected a new install that doesn't have passphrases disabled -- assuming bad initialization.");
|
||||
AppInitialization.onRepairFirstEverAppLaunch(this);
|
||||
} else if (!TextSecurePreferences.isPasswordDisabled(this) && VersionTracker.getDaysSinceFirstInstalled(this) < 912) {
|
||||
} else if (!SignalStore.settings().getPassphraseDisabled() && VersionTracker.getDaysSinceFirstInstalled(this) < 912) {
|
||||
Log.i(TAG, "Detected a not-recent install that doesn't have passphrases disabled -- disabling now.");
|
||||
TextSecurePreferences.setPasswordDisabled(this, true);
|
||||
SignalStore.settings().setPassphraseDisabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ import org.thoughtcrime.securesms.util.ServiceUtil
|
||||
class BiometricDeviceAuthentication(
|
||||
private val biometricManager: BiometricManager,
|
||||
private val biometricPrompt: BiometricPrompt,
|
||||
private val biometricPromptInfo: PromptInfo
|
||||
private var biometricPromptInfo: PromptInfo
|
||||
) {
|
||||
companion object {
|
||||
const val AUTHENTICATED = 1
|
||||
@@ -69,6 +69,10 @@ class BiometricDeviceAuthentication(
|
||||
fun cancelAuthentication() {
|
||||
biometricPrompt.cancelAuthentication()
|
||||
}
|
||||
|
||||
fun updatePromptInfo(promptInfo: PromptInfo) {
|
||||
biometricPromptInfo = promptInfo
|
||||
}
|
||||
}
|
||||
|
||||
class BiometricDeviceLockContract : ActivityResultContract<String, Int>() {
|
||||
|
||||
@@ -29,9 +29,9 @@ import org.signal.core.util.logging.Log;
|
||||
import org.thoughtcrime.securesms.crypto.InvalidPassphraseException;
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecretUtil;
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||
import org.thoughtcrime.securesms.util.DynamicLanguage;
|
||||
import org.thoughtcrime.securesms.util.DynamicTheme;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
|
||||
/**
|
||||
* Activity for changing a user's local encryption passphrase.
|
||||
@@ -81,7 +81,7 @@ public class PassphraseChangeActivity extends PassphraseActivity {
|
||||
this.okButton.setOnClickListener(new OkButtonClickListener());
|
||||
this.cancelButton.setOnClickListener(new CancelButtonClickListener());
|
||||
|
||||
if (TextSecurePreferences.isPasswordDisabled(this)) {
|
||||
if (SignalStore.settings().getPassphraseDisabled()) {
|
||||
this.originalPassphrase.setVisibility(View.GONE);
|
||||
} else {
|
||||
this.originalPassphrase.setVisibility(View.VISIBLE);
|
||||
@@ -97,7 +97,7 @@ public class PassphraseChangeActivity extends PassphraseActivity {
|
||||
String passphrase = (newText == null ? "" : newText.toString());
|
||||
String passphraseRepeat = (repeatText == null ? "" : repeatText.toString());
|
||||
|
||||
if (TextSecurePreferences.isPasswordDisabled(this)) {
|
||||
if (SignalStore.settings().getPassphraseDisabled()) {
|
||||
original = MasterSecretUtil.UNENCRYPTED_PASSPHRASE;
|
||||
}
|
||||
|
||||
@@ -142,7 +142,7 @@ public class PassphraseChangeActivity extends PassphraseActivity {
|
||||
protected MasterSecret doInBackground(String... params) {
|
||||
try {
|
||||
MasterSecret masterSecret = MasterSecretUtil.changeMasterSecretPassphrase(context, params[0], params[1]);
|
||||
TextSecurePreferences.setPasswordDisabled(context, false);
|
||||
SignalStore.settings().setPassphraseDisabled(false);
|
||||
|
||||
return masterSecret;
|
||||
|
||||
|
||||
@@ -20,7 +20,6 @@ import android.animation.Animator;
|
||||
import android.app.KeyguardManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.text.InputType;
|
||||
@@ -34,21 +33,18 @@ import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.animation.Animation;
|
||||
import android.view.animation.BounceInterpolator;
|
||||
import android.view.animation.TranslateAnimation;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.biometric.BiometricManager;
|
||||
import androidx.biometric.BiometricPrompt;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import com.airbnb.lottie.LottieAnimationView;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
|
||||
import org.signal.core.util.ThreadUtil;
|
||||
@@ -64,7 +60,7 @@ import org.thoughtcrime.securesms.util.CommunicationActions;
|
||||
import org.thoughtcrime.securesms.util.DynamicIntroTheme;
|
||||
import org.thoughtcrime.securesms.util.DynamicLanguage;
|
||||
import org.thoughtcrime.securesms.util.SupportEmailUtil;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.thoughtcrime.securesms.util.views.LearnMoreTextView;
|
||||
|
||||
import kotlin.Unit;
|
||||
|
||||
@@ -79,14 +75,14 @@ public class PassphrasePromptActivity extends PassphraseActivity {
|
||||
private static final short AUTHENTICATE_REQUEST_CODE = 1007;
|
||||
private static final String BUNDLE_ALREADY_SHOWN = "bundle_already_shown";
|
||||
public static final String FROM_FOREGROUND = "from_foreground";
|
||||
private static final int HELP_COUNT_THRESHOLD = 3;
|
||||
|
||||
private DynamicIntroTheme dynamicTheme = new DynamicIntroTheme();
|
||||
private DynamicLanguage dynamicLanguage = new DynamicLanguage();
|
||||
|
||||
private View passphraseAuthContainer;
|
||||
private ImageView fingerprintPrompt;
|
||||
private TextView lockScreenButton;
|
||||
private View passphraseAuthContainer;
|
||||
private LottieAnimationView unlockView;
|
||||
private TextView lockScreenButton;
|
||||
private LearnMoreTextView learnMoreText;
|
||||
|
||||
private EditText passphraseText;
|
||||
private ImageButton showButton;
|
||||
@@ -134,14 +130,11 @@ public class PassphrasePromptActivity extends PassphraseActivity {
|
||||
|
||||
setLockTypeVisibility();
|
||||
|
||||
if (TextSecurePreferences.isScreenLockEnabled(this) && !authenticated && !hadFailure) {
|
||||
if (SignalStore.settings().getScreenLockEnabled() && !authenticated && !hadFailure) {
|
||||
ThreadUtil.postToMain(resumeScreenLockRunnable);
|
||||
}
|
||||
|
||||
hadFailure = false;
|
||||
|
||||
fingerprintPrompt.setImageResource(R.drawable.ic_fingerprint_white_48dp);
|
||||
fingerprintPrompt.getBackground().setColorFilter(getResources().getColor(R.color.signal_accent_primary), PorterDuff.Mode.SRC_IN);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -174,9 +167,6 @@ public class PassphrasePromptActivity extends PassphraseActivity {
|
||||
if (item.getItemId() == R.id.menu_submit_debug_logs) {
|
||||
handleLogSubmit();
|
||||
return true;
|
||||
} else if (item.getItemId() == R.id.menu_contact_support) {
|
||||
sendEmailToSupport();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -193,7 +183,7 @@ public class PassphrasePromptActivity extends PassphraseActivity {
|
||||
} else {
|
||||
Log.w(TAG, "Authentication failed");
|
||||
hadFailure = true;
|
||||
incrementAttemptCountAndShowHelpIfNecessary();
|
||||
showHelpDialog();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,7 +203,6 @@ public class PassphrasePromptActivity extends PassphraseActivity {
|
||||
passphraseText.setText("");
|
||||
passphraseText.setError(
|
||||
getString(R.string.PassphrasePromptActivity_invalid_passphrase_exclamation));
|
||||
incrementAttemptCountAndShowHelpIfNecessary();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -223,7 +212,6 @@ public class PassphrasePromptActivity extends PassphraseActivity {
|
||||
|
||||
MasterSecret masterSecret = MasterSecretUtil.getMasterSecret(this, MasterSecretUtil.UNENCRYPTED_PASSPHRASE);
|
||||
setMasterSecret(masterSecret);
|
||||
SignalStore.misc().setLockScreenAttemptCount(0);
|
||||
} catch (InvalidPassphraseException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
@@ -251,8 +239,9 @@ public class PassphrasePromptActivity extends PassphraseActivity {
|
||||
visibilityToggle = findViewById(R.id.button_toggle);
|
||||
passphraseText = findViewById(R.id.passphrase_edit);
|
||||
passphraseAuthContainer = findViewById(R.id.password_auth_container);
|
||||
fingerprintPrompt = findViewById(R.id.fingerprint_auth_container);
|
||||
lockScreenButton = findViewById(R.id.lock_screen_auth_container);
|
||||
unlockView = findViewById(R.id.unlock_view);
|
||||
lockScreenButton = findViewById(R.id.lock_screen_button);
|
||||
learnMoreText = findViewById(R.id.learn_more_text);
|
||||
biometricManager = BiometricManager.from(this);
|
||||
biometricPrompt = new BiometricPrompt(this, new BiometricAuthenticationListener());
|
||||
BiometricPrompt.PromptInfo biometricPromptInfo = new BiometricPrompt.PromptInfo
|
||||
@@ -276,34 +265,22 @@ public class PassphrasePromptActivity extends PassphraseActivity {
|
||||
passphraseText.setImeActionLabel(getString(R.string.prompt_passphrase_activity__unlock),
|
||||
EditorInfo.IME_ACTION_DONE);
|
||||
|
||||
fingerprintPrompt.setImageResource(R.drawable.ic_fingerprint_white_48dp);
|
||||
fingerprintPrompt.getBackground().setColorFilter(getResources().getColor(R.color.core_ultramarine), PorterDuff.Mode.SRC_IN);
|
||||
|
||||
lockScreenButton.setOnClickListener(v -> resumeScreenLock(true));
|
||||
|
||||
if (SignalStore.misc().getLockScreenAttemptCount() > HELP_COUNT_THRESHOLD) {
|
||||
showHelpDialogAndResetAttemptCount(null);
|
||||
}
|
||||
}
|
||||
|
||||
private void setLockTypeVisibility() {
|
||||
if (TextSecurePreferences.isScreenLockEnabled(this)) {
|
||||
if (SignalStore.settings().getScreenLockEnabled()) {
|
||||
passphraseAuthContainer.setVisibility(View.GONE);
|
||||
fingerprintPrompt.setVisibility(biometricManager.canAuthenticate(BiometricDeviceAuthentication.BIOMETRIC_AUTHENTICATORS) == BiometricManager.BIOMETRIC_SUCCESS ? View.VISIBLE
|
||||
: View.GONE);
|
||||
unlockView.setVisibility(View.VISIBLE);
|
||||
lockScreenButton.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
passphraseAuthContainer.setVisibility(View.VISIBLE);
|
||||
fingerprintPrompt.setVisibility(View.GONE);
|
||||
unlockView.setVisibility(View.GONE);
|
||||
lockScreenButton.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
private void resumeScreenLock(boolean force) {
|
||||
if (incrementAttemptCountAndShowHelpIfNecessary(() -> resumeScreenLock(force))) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!biometricAuth.authenticate(getApplicationContext(), force, this::showConfirmDeviceCredentialIntent)) {
|
||||
handleAuthenticated();
|
||||
}
|
||||
@@ -328,33 +305,6 @@ public class PassphrasePromptActivity extends PassphraseActivity {
|
||||
return Unit.INSTANCE;
|
||||
}
|
||||
|
||||
private boolean incrementAttemptCountAndShowHelpIfNecessary() {
|
||||
return incrementAttemptCountAndShowHelpIfNecessary(null);
|
||||
}
|
||||
|
||||
private boolean incrementAttemptCountAndShowHelpIfNecessary(Runnable onDismissed) {
|
||||
SignalStore.misc().incrementLockScreenAttemptCount();
|
||||
|
||||
if (SignalStore.misc().getLockScreenAttemptCount() > HELP_COUNT_THRESHOLD) {
|
||||
showHelpDialogAndResetAttemptCount(onDismissed);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void showHelpDialogAndResetAttemptCount(@Nullable Runnable onDismissed) {
|
||||
new MaterialAlertDialogBuilder(this)
|
||||
.setMessage(R.string.PassphrasePromptActivity_help_prompt_body)
|
||||
.setPositiveButton(android.R.string.ok, (dialog, which) -> {
|
||||
SignalStore.misc().setLockScreenAttemptCount(0);
|
||||
if (onDismissed != null) {
|
||||
onDismissed.run();
|
||||
}
|
||||
})
|
||||
.show();
|
||||
}
|
||||
|
||||
private class PassphraseActionListener implements TextView.OnEditorActionListener {
|
||||
@Override
|
||||
public boolean onEditorAction(TextView exampleView, int actionId, KeyEvent keyEvent) {
|
||||
@@ -403,13 +353,31 @@ public class PassphrasePromptActivity extends PassphraseActivity {
|
||||
System.gc();
|
||||
}
|
||||
|
||||
private void showHelpDialog() {
|
||||
lockScreenButton.setText(R.string.prompt_passphrase_activity__try_again);
|
||||
|
||||
learnMoreText.setVisibility(View.VISIBLE);
|
||||
learnMoreText.setLearnMoreVisible(true);
|
||||
learnMoreText.setLinkColor(ContextCompat.getColor(PassphrasePromptActivity.this, R.color.signal_colorPrimary));
|
||||
|
||||
learnMoreText.setOnClickListener(v ->
|
||||
new MaterialAlertDialogBuilder(PassphrasePromptActivity.this)
|
||||
.setTitle(R.string.prompt_passphrase_activity__unlock_signal)
|
||||
.setMessage(R.string.prompt_passphrase_activity__screen_lock_is_on)
|
||||
.setCancelable(true)
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
.setNegativeButton(R.string.prompt_passphrase_activity__contact_support, (d,w) -> sendEmailToSupport())
|
||||
.show()
|
||||
);
|
||||
}
|
||||
|
||||
private class BiometricAuthenticationListener extends BiometricPrompt.AuthenticationCallback {
|
||||
@Override
|
||||
public void onAuthenticationError(int errorCode, @NonNull CharSequence errorString) {
|
||||
Log.w(TAG, "Authentication error: " + errorCode);
|
||||
hadFailure = true;
|
||||
|
||||
incrementAttemptCountAndShowHelpIfNecessary();
|
||||
showHelpDialog();
|
||||
|
||||
if (errorCode != BiometricPrompt.ERROR_CANCELED && errorCode != BiometricPrompt.ERROR_USER_CANCELED) {
|
||||
onAuthenticationFailed();
|
||||
@@ -419,41 +387,19 @@ public class PassphrasePromptActivity extends PassphraseActivity {
|
||||
@Override
|
||||
public void onAuthenticationSucceeded(@NonNull BiometricPrompt.AuthenticationResult result) {
|
||||
Log.i(TAG, "onAuthenticationSucceeded");
|
||||
fingerprintPrompt.setImageResource(R.drawable.symbol_check_white_48);
|
||||
fingerprintPrompt.getBackground().setColorFilter(getResources().getColor(R.color.green_500), PorterDuff.Mode.SRC_IN);
|
||||
fingerprintPrompt.animate().setInterpolator(new BounceInterpolator()).scaleX(1.1f).scaleY(1.1f).setDuration(500).setListener(new AnimationCompleteListener() {
|
||||
unlockView.addAnimatorListener(new AnimationCompleteListener() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
handleAuthenticated();
|
||||
}
|
||||
}).start();
|
||||
});
|
||||
unlockView.playAnimation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAuthenticationFailed() {
|
||||
Log.w(TAG, "onAuthenticationFailed()");
|
||||
|
||||
fingerprintPrompt.setImageResource(R.drawable.symbol_x_white_48);
|
||||
fingerprintPrompt.getBackground().setColorFilter(getResources().getColor(R.color.red_500), PorterDuff.Mode.SRC_IN);
|
||||
|
||||
TranslateAnimation shake = new TranslateAnimation(0, 30, 0, 0);
|
||||
shake.setDuration(50);
|
||||
shake.setRepeatCount(7);
|
||||
shake.setAnimationListener(new Animation.AnimationListener() {
|
||||
@Override
|
||||
public void onAnimationStart(Animation animation) {}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animation animation) {
|
||||
fingerprintPrompt.setImageResource(R.drawable.ic_fingerprint_white_48dp);
|
||||
fingerprintPrompt.getBackground().setColorFilter(getResources().getColor(R.color.signal_accent_primary), PorterDuff.Mode.SRC_IN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animation animation) {}
|
||||
});
|
||||
|
||||
fingerprintPrompt.startAnimation(shake);
|
||||
showHelpDialog();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -287,8 +287,8 @@ public abstract class PassphraseRequiredActivity extends BaseActivity implements
|
||||
this.clearKeyReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
Log.i(TAG, "onReceive() for clear key event. PasswordDisabled: " + TextSecurePreferences.isPasswordDisabled(context) + ", ScreenLock: " + TextSecurePreferences.isScreenLockEnabled(context));
|
||||
if (TextSecurePreferences.isScreenLockEnabled(context) || !TextSecurePreferences.isPasswordDisabled(context)) {
|
||||
Log.i(TAG, "onReceive() for clear key event. PasswordDisabled: " + SignalStore.settings().getPassphraseDisabled() + ", ScreenLock: " + SignalStore.settings().getScreenLockEnabled());
|
||||
if (SignalStore.settings().getScreenLockEnabled() || !SignalStore.settings().getPassphraseDisabled()) {
|
||||
onMasterSecretCleared();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,8 +109,13 @@ class LearnMoreTextPreferenceViewHolder(itemView: View) : PreferenceViewHolder<L
|
||||
class ClickPreferenceViewHolder(itemView: View) : PreferenceViewHolder<ClickPreference>(itemView) {
|
||||
override fun bind(model: ClickPreference) {
|
||||
super.bind(model)
|
||||
itemView.setOnClickListener { model.onClick() }
|
||||
itemView.setOnLongClickListener { model.onLongClick?.invoke() ?: false }
|
||||
if (!itemView.isEnabled && model.onDisabledClicked != null) {
|
||||
itemView.isEnabled = true
|
||||
itemView.setOnClickListener { model.onDisabledClicked() }
|
||||
} else {
|
||||
itemView.setOnClickListener { model.onClick() }
|
||||
itemView.setOnLongClickListener { model.onLongClick?.invoke() ?: false }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ import androidx.navigation.fragment.NavHostFragment
|
||||
import androidx.navigation.fragment.navArgs
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.thoughtcrime.securesms.BiometricDeviceAuthentication
|
||||
import org.thoughtcrime.securesms.BiometricDeviceLockContract
|
||||
@@ -36,9 +37,9 @@ import org.thoughtcrime.securesms.components.settings.PreferenceModel
|
||||
import org.thoughtcrime.securesms.components.settings.PreferenceViewHolder
|
||||
import org.thoughtcrime.securesms.components.settings.configure
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecretUtil
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.service.KeyCachingService
|
||||
import org.thoughtcrime.securesms.util.CommunicationActions
|
||||
import org.thoughtcrime.securesms.util.ConversationUtil
|
||||
import org.thoughtcrime.securesms.util.ExpirationUtil
|
||||
import org.thoughtcrime.securesms.util.ServiceUtil
|
||||
import org.thoughtcrime.securesms.util.SpanUtil
|
||||
@@ -197,7 +198,7 @@ class PrivacySettingsFragment : DSLSettingsFragment(R.string.preferences__privac
|
||||
KeyCachingService.getMasterSecret(context),
|
||||
MasterSecretUtil.UNENCRYPTED_PASSPHRASE
|
||||
)
|
||||
TextSecurePreferences.setPasswordDisabled(activity, true)
|
||||
SignalStore.settings.passphraseDisabled = true
|
||||
val intent = Intent(activity, KeyCachingService::class.java)
|
||||
intent.action = KeyCachingService.DISABLE_ACTION
|
||||
requireActivity().startService(intent)
|
||||
@@ -249,33 +250,21 @@ class PrivacySettingsFragment : DSLSettingsFragment(R.string.preferences__privac
|
||||
} else {
|
||||
val isKeyguardSecure = ServiceUtil.getKeyguardManager(requireContext()).isKeyguardSecure
|
||||
|
||||
switchPref(
|
||||
title = DSLSettingsText.from(R.string.preferences_app_protection__screen_lock),
|
||||
summary = DSLSettingsText.from(R.string.preferences_app_protection__lock_signal_access_with_android_screen_lock_or_fingerprint),
|
||||
isChecked = state.screenLock && isKeyguardSecure,
|
||||
isEnabled = isKeyguardSecure,
|
||||
onClick = {
|
||||
viewModel.setScreenLockEnabled(!state.screenLock)
|
||||
|
||||
val intent = Intent(requireContext(), KeyCachingService::class.java)
|
||||
intent.action = KeyCachingService.LOCK_TOGGLED_EVENT
|
||||
requireContext().startService(intent)
|
||||
|
||||
ConversationUtil.refreshRecipientShortcuts()
|
||||
}
|
||||
)
|
||||
|
||||
clickPref(
|
||||
title = DSLSettingsText.from(R.string.preferences_app_protection__screen_lock_inactivity_timeout),
|
||||
summary = DSLSettingsText.from(getScreenLockInactivityTimeoutSummary(state.screenLockActivityTimeout)),
|
||||
isEnabled = isKeyguardSecure && state.screenLock,
|
||||
title = DSLSettingsText.from(R.string.preferences_app_protection__screen_lock),
|
||||
summary = DSLSettingsText.from(getScreenLockInactivityTimeoutSummary(isKeyguardSecure && state.screenLock, state.screenLockActivityTimeout)),
|
||||
onClick = {
|
||||
childFragmentManager.clearFragmentResult(TimeDurationPickerDialog.RESULT_DURATION)
|
||||
childFragmentManager.clearFragmentResultListener(TimeDurationPickerDialog.RESULT_DURATION)
|
||||
childFragmentManager.setFragmentResultListener(TimeDurationPickerDialog.RESULT_DURATION, this@PrivacySettingsFragment) { _, bundle ->
|
||||
viewModel.setScreenLockTimeout(bundle.getLong(TimeDurationPickerDialog.RESULT_KEY_DURATION_MILLISECONDS).milliseconds.inWholeSeconds)
|
||||
}
|
||||
TimeDurationPickerDialog.create(state.screenLockActivityTimeout.seconds).show(childFragmentManager, null)
|
||||
Navigation.findNavController(requireView()).safeNavigate(R.id.action_privacySettingsFragment_to_screenLockSettingsFragment)
|
||||
},
|
||||
isEnabled = isKeyguardSecure,
|
||||
onDisabledClicked = {
|
||||
Snackbar
|
||||
.make(
|
||||
requireView(),
|
||||
resources.getString(R.string.preferences_app_protection__to_use_screen_lock),
|
||||
Snackbar.LENGTH_LONG
|
||||
)
|
||||
.show()
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -362,9 +351,11 @@ class PrivacySettingsFragment : DSLSettingsFragment(R.string.preferences__privac
|
||||
}
|
||||
}
|
||||
|
||||
private fun getScreenLockInactivityTimeoutSummary(timeoutSeconds: Long): String {
|
||||
return if (timeoutSeconds <= 0) {
|
||||
getString(R.string.AppProtectionPreferenceFragment_none)
|
||||
private fun getScreenLockInactivityTimeoutSummary(enabledScreenLock: Boolean, timeoutSeconds: Long): String {
|
||||
return if (!enabledScreenLock) {
|
||||
getString(R.string.ScreenLockSettingsFragment__off)
|
||||
} else if (timeoutSeconds == 0L) {
|
||||
getString(R.string.ScreenLockSettingsFragment__immediately)
|
||||
} else {
|
||||
ExpirationUtil.getExpirationDisplayValue(requireContext(), timeoutSeconds.toInt())
|
||||
}
|
||||
|
||||
@@ -37,16 +37,6 @@ class PrivacySettingsViewModel(
|
||||
refresh()
|
||||
}
|
||||
|
||||
fun setScreenLockEnabled(enabled: Boolean) {
|
||||
sharedPreferences.edit().putBoolean(TextSecurePreferences.SCREEN_LOCK, enabled).apply()
|
||||
refresh()
|
||||
}
|
||||
|
||||
fun setScreenLockTimeout(seconds: Long) {
|
||||
TextSecurePreferences.setScreenLockTimeout(AppDependencies.application, seconds)
|
||||
refresh()
|
||||
}
|
||||
|
||||
fun setScreenSecurityEnabled(enabled: Boolean) {
|
||||
sharedPreferences.edit().putBoolean(TextSecurePreferences.SCREEN_SECURITY_PREF, enabled).apply()
|
||||
refresh()
|
||||
@@ -63,12 +53,12 @@ class PrivacySettingsViewModel(
|
||||
}
|
||||
|
||||
fun setObsoletePasswordTimeoutEnabled(enabled: Boolean) {
|
||||
sharedPreferences.edit().putBoolean(TextSecurePreferences.PASSPHRASE_TIMEOUT_PREF, enabled).apply()
|
||||
SignalStore.settings.passphraseTimeoutEnabled = enabled
|
||||
refresh()
|
||||
}
|
||||
|
||||
fun setObsoletePasswordTimeout(minutes: Int) {
|
||||
TextSecurePreferences.setPassphraseTimeoutInterval(AppDependencies.application, minutes)
|
||||
SignalStore.settings.passphraseTimeout = minutes
|
||||
refresh()
|
||||
}
|
||||
|
||||
@@ -81,14 +71,14 @@ class PrivacySettingsViewModel(
|
||||
blockedCount = 0,
|
||||
readReceipts = TextSecurePreferences.isReadReceiptsEnabled(AppDependencies.application),
|
||||
typingIndicators = TextSecurePreferences.isTypingIndicatorsEnabled(AppDependencies.application),
|
||||
screenLock = TextSecurePreferences.isScreenLockEnabled(AppDependencies.application),
|
||||
screenLockActivityTimeout = TextSecurePreferences.getScreenLockTimeout(AppDependencies.application),
|
||||
screenLock = SignalStore.settings.screenLockEnabled,
|
||||
screenLockActivityTimeout = SignalStore.settings.screenLockTimeout,
|
||||
screenSecurity = TextSecurePreferences.isScreenSecurityEnabled(AppDependencies.application),
|
||||
incognitoKeyboard = TextSecurePreferences.isIncognitoKeyboardEnabled(AppDependencies.application),
|
||||
paymentLock = SignalStore.payments.paymentLock,
|
||||
isObsoletePasswordEnabled = !TextSecurePreferences.isPasswordDisabled(AppDependencies.application),
|
||||
isObsoletePasswordTimeoutEnabled = TextSecurePreferences.isPassphraseTimeoutEnabled(AppDependencies.application),
|
||||
obsoletePasswordTimeout = TextSecurePreferences.getPassphraseTimeoutInterval(AppDependencies.application),
|
||||
isObsoletePasswordEnabled = !SignalStore.settings.passphraseDisabled,
|
||||
isObsoletePasswordTimeoutEnabled = SignalStore.settings.passphraseTimeoutEnabled,
|
||||
obsoletePasswordTimeout = SignalStore.settings.passphraseTimeout,
|
||||
universalExpireTimer = SignalStore.settings.universalExpireTimer
|
||||
)
|
||||
}
|
||||
|
||||
@@ -65,6 +65,12 @@ class CustomExpireTimerSelectorView @JvmOverloads constructor(
|
||||
valuePicker.maxValue = timerUnit.maxValue
|
||||
}
|
||||
|
||||
fun setUnits(minValue: Int, maxValue: Int, timeUnitRes: Int) {
|
||||
unitPicker.minValue = minValue
|
||||
unitPicker.maxValue = maxValue
|
||||
unitPicker.displayedValues = context.resources.getStringArray(timeUnitRes)
|
||||
}
|
||||
|
||||
private enum class TimerUnit(val minValue: Int, val maxValue: Int, val valueMultiplier: Long) {
|
||||
SECONDS(1, 59, TimeUnit.SECONDS.toSeconds(1)),
|
||||
MINUTES(1, 59, TimeUnit.MINUTES.toSeconds(1)),
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
package org.thoughtcrime.securesms.components.settings.app.privacy.screenlock
|
||||
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.components.settings.app.privacy.expire.CustomExpireTimerSelectorView
|
||||
|
||||
/**
|
||||
* Dialog for selecting a custom timer value when setting the screen lock timeout.
|
||||
*/
|
||||
class CustomScreenLockTimerSelectDialog : DialogFragment() {
|
||||
|
||||
private val viewModel: ScreenLockSettingsViewModel by activityViewModels()
|
||||
private lateinit var selector: CustomExpireTimerSelectorView
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
val dialogView: View = LayoutInflater.from(context).inflate(R.layout.custom_expire_timer_select_dialog, null, false)
|
||||
|
||||
selector = dialogView.findViewById(R.id.custom_expire_timer_select_dialog_selector)
|
||||
selector.setUnits(1, 3, R.array.CustomScreenLockTimerSelectorView__unit_labels)
|
||||
selector.setTimer(viewModel.state.value.screenLockActivityTimeout.toInt())
|
||||
|
||||
return MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(R.string.ExpireTimerSettingsFragment__custom_time)
|
||||
.setView(dialogView)
|
||||
.setPositiveButton(R.string.ExpireTimerSettingsFragment__set) { _, _ ->
|
||||
viewModel.setScreenLockTimeout(selector.getTimer().toLong())
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.create()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,284 @@
|
||||
package org.thoughtcrime.securesms.components.settings.app.privacy.screenlock
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.biometric.BiometricManager
|
||||
import androidx.biometric.BiometricPrompt
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.defaultMinSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.selection.selectable
|
||||
import androidx.compose.foundation.selection.selectableGroup
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.RadioButton
|
||||
import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.semantics.Role
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import org.signal.core.ui.Previews
|
||||
import org.signal.core.ui.Scaffolds
|
||||
import org.signal.core.ui.SignalPreview
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.thoughtcrime.securesms.BiometricDeviceAuthentication
|
||||
import org.thoughtcrime.securesms.BiometricDeviceLockContract
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.compose.ComposeFragment
|
||||
import org.thoughtcrime.securesms.service.KeyCachingService
|
||||
import org.thoughtcrime.securesms.util.ConversationUtil
|
||||
import org.thoughtcrime.securesms.util.ExpirationUtil
|
||||
import org.thoughtcrime.securesms.util.navigation.safeNavigate
|
||||
|
||||
/**
|
||||
* Fragment that allows user to turn on screen lock and set a timer to lock
|
||||
*/
|
||||
class ScreenLockSettingsFragment : ComposeFragment() {
|
||||
|
||||
companion object {
|
||||
private val TAG = Log.tag(ScreenLockSettingsFragment::class)
|
||||
}
|
||||
|
||||
private val viewModel: ScreenLockSettingsViewModel by activityViewModels()
|
||||
|
||||
private lateinit var biometricAuth: BiometricDeviceAuthentication
|
||||
private lateinit var biometricDeviceLockLauncher: ActivityResultLauncher<String>
|
||||
private lateinit var disableLockPromptInfo: BiometricPrompt.PromptInfo
|
||||
private lateinit var enableLockPromptInfo: BiometricPrompt.PromptInfo
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
biometricDeviceLockLauncher = registerForActivityResult(BiometricDeviceLockContract()) { result: Int ->
|
||||
if (result == BiometricDeviceAuthentication.AUTHENTICATED) {
|
||||
toggleScreenLock()
|
||||
}
|
||||
}
|
||||
|
||||
enableLockPromptInfo = BiometricPrompt.PromptInfo.Builder()
|
||||
.setAllowedAuthenticators(BiometricDeviceAuthentication.ALLOWED_AUTHENTICATORS)
|
||||
.setTitle(requireContext().getString(R.string.ScreenLockSettingsFragment__use_signal_screen_lock))
|
||||
.setConfirmationRequired(true)
|
||||
.build()
|
||||
|
||||
disableLockPromptInfo = BiometricPrompt.PromptInfo.Builder()
|
||||
.setAllowedAuthenticators(BiometricDeviceAuthentication.ALLOWED_AUTHENTICATORS)
|
||||
.setTitle(requireContext().getString(R.string.ScreenLockSettingsFragment__turn_off_signal_lock))
|
||||
.setConfirmationRequired(true)
|
||||
.build()
|
||||
|
||||
biometricAuth = BiometricDeviceAuthentication(
|
||||
BiometricManager.from(requireActivity()),
|
||||
BiometricPrompt(requireActivity(), BiometricAuthenticationListener()),
|
||||
enableLockPromptInfo
|
||||
)
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
biometricAuth.cancelAuthentication()
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun FragmentContent() {
|
||||
val state by viewModel.state.collectAsState()
|
||||
val navController: NavController by remember { mutableStateOf(findNavController()) }
|
||||
|
||||
Scaffolds.Settings(
|
||||
title = stringResource(id = R.string.preferences_app_protection__screen_lock),
|
||||
onNavigationClick = { navController.popBackStack() },
|
||||
navigationIconPainter = painterResource(id = R.drawable.ic_arrow_left_24),
|
||||
navigationContentDescription = stringResource(id = R.string.Material3SearchToolbar__close)
|
||||
) { contentPadding: PaddingValues ->
|
||||
ScreenLockScreen(
|
||||
state = state,
|
||||
onChecked = { checked ->
|
||||
if (biometricAuth.canAuthenticate() && !checked) {
|
||||
biometricAuth.updatePromptInfo(disableLockPromptInfo)
|
||||
biometricAuth.authenticate(requireContext(), true) {
|
||||
biometricDeviceLockLauncher.launch(getString(R.string.ScreenLockSettingsFragment__turn_off_signal_lock))
|
||||
}
|
||||
} else if (biometricAuth.canAuthenticate() && checked) {
|
||||
biometricAuth.updatePromptInfo(enableLockPromptInfo)
|
||||
biometricAuth.authenticate(requireContext(), true) {
|
||||
biometricDeviceLockLauncher.launch(getString(R.string.ScreenLockSettingsFragment__use_screen_lock))
|
||||
}
|
||||
}
|
||||
},
|
||||
onTimeClicked = viewModel::setScreenLockTimeout,
|
||||
onCustomTimeClicked = { navController.safeNavigate(R.id.action_screenLockSettingsFragment_to_customScreenLockTimerSelectDialog) },
|
||||
modifier = Modifier.padding(contentPadding)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun toggleScreenLock() {
|
||||
viewModel.toggleScreenLock()
|
||||
|
||||
val intent = Intent(requireContext(), KeyCachingService::class.java)
|
||||
intent.action = KeyCachingService.LOCK_TOGGLED_EVENT
|
||||
requireContext().startService(intent)
|
||||
|
||||
ConversationUtil.refreshRecipientShortcuts()
|
||||
}
|
||||
|
||||
private inner class BiometricAuthenticationListener : BiometricPrompt.AuthenticationCallback() {
|
||||
override fun onAuthenticationError(errorCode: Int, errorString: CharSequence) {
|
||||
Log.w(TAG, "Authentication error: $errorCode")
|
||||
onAuthenticationFailed()
|
||||
}
|
||||
|
||||
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
|
||||
Log.i(TAG, "Authentication succeeded")
|
||||
toggleScreenLock()
|
||||
}
|
||||
|
||||
override fun onAuthenticationFailed() {
|
||||
Log.w(TAG, "Unable to authenticate")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ScreenLockScreen(
|
||||
state: ScreenLockSettingsState,
|
||||
onChecked: (Boolean) -> Unit,
|
||||
onTimeClicked: (Long) -> Unit,
|
||||
onCustomTimeClicked: () -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
Column(modifier = modifier.verticalScroll(rememberScrollState())) {
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_screen_lock),
|
||||
contentDescription = null,
|
||||
tint = Color.Unspecified,
|
||||
modifier = Modifier.padding(top = 24.dp, bottom = 24.dp)
|
||||
)
|
||||
Text(
|
||||
text = stringResource(id = R.string.ScreenLockSettingsFragment__your_android_device),
|
||||
textAlign = TextAlign.Center,
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
modifier = Modifier.padding(start = 40.dp, end = 40.dp, bottom = 24.dp)
|
||||
)
|
||||
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 24.dp)
|
||||
.background(color = MaterialTheme.colorScheme.surfaceVariant, shape = RoundedCornerShape(24.dp))
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 24.dp, vertical = 12.dp)
|
||||
) {
|
||||
Text(stringResource(id = R.string.ScreenLockSettingsFragment__use_screen_lock))
|
||||
Spacer(Modifier.weight(1f))
|
||||
Switch(checked = state.screenLock, onCheckedChange = onChecked)
|
||||
}
|
||||
}
|
||||
|
||||
if (state.screenLock) {
|
||||
val labels: List<String> = LocalContext.current.resources.getStringArray(R.array.ScreenLockSettingsFragment__labels).toList()
|
||||
val values: List<Long> = LocalContext.current.resources.getIntArray(R.array.ScreenLockSettingsFragment__values).map { it.toLong() }
|
||||
|
||||
Text(
|
||||
stringResource(id = R.string.ScreenLockSettingsFragment__start_screen_lock),
|
||||
style = MaterialTheme.typography.titleSmall,
|
||||
modifier = Modifier.padding(top = 24.dp, bottom = 16.dp, start = 24.dp)
|
||||
)
|
||||
Column(Modifier.selectableGroup()) {
|
||||
var isCustomTime = true
|
||||
labels.zip(values).forEach { (label, seconds) ->
|
||||
val isSelected = seconds == state.screenLockActivityTimeout
|
||||
Row(
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.defaultMinSize(minHeight = 56.dp)
|
||||
.selectable(
|
||||
selected = isSelected,
|
||||
onClick = { onTimeClicked(seconds) },
|
||||
role = Role.RadioButton
|
||||
)
|
||||
.padding(horizontal = 24.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
RadioButton(selected = isSelected, onClick = null)
|
||||
Text(
|
||||
text = label,
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
modifier = Modifier.padding(start = 16.dp)
|
||||
)
|
||||
isCustomTime = isCustomTime && !isSelected
|
||||
}
|
||||
}
|
||||
|
||||
Row(
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.height(56.dp)
|
||||
.selectable(
|
||||
selected = isCustomTime,
|
||||
onClick = onCustomTimeClicked,
|
||||
role = Role.RadioButton
|
||||
)
|
||||
.padding(horizontal = 24.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
RadioButton(selected = isCustomTime, onClick = null)
|
||||
Column(modifier = Modifier.padding(start = 16.dp)) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.ScreenLockSettingsFragment__custom_time),
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
color = MaterialTheme.colorScheme.onSurface
|
||||
)
|
||||
if (isCustomTime && state.screenLockActivityTimeout > 0) {
|
||||
Text(
|
||||
text = ExpirationUtil.getExpirationDisplayValue(LocalContext.current, state.screenLockActivityTimeout.toInt()),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SignalPreview
|
||||
@Composable
|
||||
fun ScreenLockScreenPreview() {
|
||||
Previews.Preview {
|
||||
ScreenLockScreen(
|
||||
state = ScreenLockSettingsState(true, 60),
|
||||
onChecked = {},
|
||||
onTimeClicked = {},
|
||||
onCustomTimeClicked = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package org.thoughtcrime.securesms.components.settings.app.privacy.screenlock
|
||||
|
||||
/**
|
||||
* Information about the screen lock state. Used in [ScreenLockSettingsViewModel].
|
||||
*/
|
||||
data class ScreenLockSettingsState(
|
||||
val screenLock: Boolean = false,
|
||||
val screenLockActivityTimeout: Long = 0L
|
||||
)
|
||||
@@ -0,0 +1,42 @@
|
||||
package org.thoughtcrime.securesms.components.settings.app.privacy.screenlock
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
|
||||
/**
|
||||
* Maintains the state of the [ScreenLockSettingsFragment]
|
||||
*/
|
||||
class ScreenLockSettingsViewModel : ViewModel() {
|
||||
|
||||
private val _state = MutableStateFlow(getState())
|
||||
val state = _state.asStateFlow()
|
||||
|
||||
fun toggleScreenLock() {
|
||||
val enabled = !_state.value.screenLock
|
||||
SignalStore.settings.screenLockEnabled = enabled
|
||||
_state.update {
|
||||
it.copy(
|
||||
screenLock = enabled
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun setScreenLockTimeout(seconds: Long) {
|
||||
SignalStore.settings.screenLockTimeout = seconds
|
||||
_state.update {
|
||||
it.copy(
|
||||
screenLockActivityTimeout = seconds
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getState(): ScreenLockSettingsState {
|
||||
return ScreenLockSettingsState(
|
||||
screenLock = SignalStore.settings.screenLockEnabled,
|
||||
screenLockActivityTimeout = SignalStore.settings.screenLockTimeout
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -105,9 +105,10 @@ class DSLConfiguration {
|
||||
iconEnd: DSLSettingsIcon? = null,
|
||||
isEnabled: Boolean = true,
|
||||
onClick: () -> Unit,
|
||||
onLongClick: (() -> Boolean)? = null
|
||||
onLongClick: (() -> Boolean)? = null,
|
||||
onDisabledClicked: () -> Unit = {}
|
||||
) {
|
||||
val preference = ClickPreference(title, summary, icon, iconEnd, isEnabled, onClick, onLongClick)
|
||||
val preference = ClickPreference(title, summary, icon, iconEnd, isEnabled, onClick, onLongClick, onDisabledClicked)
|
||||
children.add(preference)
|
||||
}
|
||||
|
||||
@@ -344,7 +345,8 @@ class ClickPreference(
|
||||
override val iconEnd: DSLSettingsIcon? = null,
|
||||
override val isEnabled: Boolean = true,
|
||||
val onClick: () -> Unit,
|
||||
val onLongClick: (() -> Boolean)? = null
|
||||
val onLongClick: (() -> Boolean)? = null,
|
||||
val onDisabledClicked: () -> Unit = {}
|
||||
) : PreferenceModel<ClickPreference>()
|
||||
|
||||
class LongClickPreference(
|
||||
|
||||
@@ -568,7 +568,7 @@ public class ConversationListFragment extends MainFragment implements ActionMode
|
||||
|
||||
@Override
|
||||
public void onPrepareOptionsMenu(Menu menu) {
|
||||
menu.findItem(R.id.menu_clear_passphrase).setVisible(!TextSecurePreferences.isPasswordDisabled(requireContext()));
|
||||
menu.findItem(R.id.menu_clear_passphrase).setVisible(!SignalStore.settings().getPassphraseDisabled());
|
||||
|
||||
ConversationFilterRequest request = viewModel.getConversationFilterRequest();
|
||||
boolean isChatFilterEnabled = request != null && request.getFilter() == ConversationFilter.UNREAD;
|
||||
|
||||
@@ -5,12 +5,12 @@ import org.signal.core.util.logging.Log
|
||||
import org.thoughtcrime.securesms.dependencies.AppDependencies
|
||||
import org.thoughtcrime.securesms.jobmanager.Job
|
||||
import org.thoughtcrime.securesms.jobmanager.JsonJobData
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId
|
||||
import org.thoughtcrime.securesms.transport.RetryLaterException
|
||||
import org.thoughtcrime.securesms.util.ConversationUtil
|
||||
import org.thoughtcrime.securesms.util.ConversationUtil.Direction
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
/**
|
||||
@@ -65,7 +65,7 @@ class ConversationShortcutRankingUpdateJob private constructor(
|
||||
override fun getFactoryKey() = KEY
|
||||
|
||||
override fun onRun() {
|
||||
if (TextSecurePreferences.isScreenLockEnabled(context)) {
|
||||
if (SignalStore.settings.screenLockEnabled) {
|
||||
Log.i(TAG, "Screen lock enabled. Clearing shortcuts.")
|
||||
ConversationUtil.clearAllShortcuts(context)
|
||||
return
|
||||
|
||||
@@ -9,10 +9,10 @@ import org.thoughtcrime.securesms.database.ThreadTable;
|
||||
import org.thoughtcrime.securesms.database.model.ThreadRecord;
|
||||
import org.thoughtcrime.securesms.dependencies.AppDependencies;
|
||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.transport.RetryLaterException;
|
||||
import org.thoughtcrime.securesms.util.ConversationUtil;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -58,7 +58,7 @@ public class ConversationShortcutUpdateJob extends BaseJob {
|
||||
|
||||
@Override
|
||||
protected void onRun() throws Exception {
|
||||
if (TextSecurePreferences.isScreenLockEnabled(context)) {
|
||||
if (SignalStore.settings().getScreenLockEnabled()) {
|
||||
Log.i(TAG, "Screen lock enabled. Clearing shortcuts.");
|
||||
ConversationUtil.clearAllShortcuts(context);
|
||||
return;
|
||||
|
||||
@@ -67,6 +67,11 @@ public final class SettingsValues extends SignalStoreValues {
|
||||
private static final String KEEP_MUTED_CHATS_ARCHIVED = "settings.keepMutedChatsArchived";
|
||||
private static final String USE_COMPACT_NAVIGATION_BAR = "settings.useCompactNavigationBar";
|
||||
private static final String THREAD_TRIM_SYNC_TO_LINKED_DEVICES = "settings.storage.syncThreadTrimDeletes";
|
||||
private static final String PASSPHRASE_DISABLED = "settings.passphrase.disabled";
|
||||
private static final String PASSPHRASE_TIMEOUT_ENABLED = "settings.passphrase.timeout.enabled";
|
||||
private static final String PASSPHRASE_TIMEOUT = "settings.passphrase.timeout";
|
||||
private static final String SCREEN_LOCK_ENABLED = "settings.screen.lock.enabled";
|
||||
private static final String SCREEN_LOCK_TIMEOUT = "settings.screen.lock.timeout";
|
||||
|
||||
public static final int BACKUP_DEFAULT_HOUR = 2;
|
||||
public static final int BACKUP_DEFAULT_MINUTE = 0;
|
||||
@@ -75,6 +80,20 @@ public final class SettingsValues extends SignalStoreValues {
|
||||
|
||||
SettingsValues(@NonNull KeyValueStore store) {
|
||||
super(store);
|
||||
|
||||
if (!store.containsKey(SCREEN_LOCK_ENABLED)) {
|
||||
migrateFromSharedPrefsV1(AppDependencies.getApplication());
|
||||
}
|
||||
}
|
||||
|
||||
private void migrateFromSharedPrefsV1(@NonNull Context context) {
|
||||
Log.i(TAG, "[V1] Migrating screen lock values from shared prefs.");
|
||||
|
||||
putBoolean(PASSPHRASE_DISABLED, TextSecurePreferences.getBooleanPreference(context, "pref_disable_passphrase", true));
|
||||
putBoolean(PASSPHRASE_TIMEOUT_ENABLED, TextSecurePreferences.getBooleanPreference(context, "pref_timeout_passphrase", false));
|
||||
putInteger(PASSPHRASE_TIMEOUT, TextSecurePreferences.getIntegerPreference(context, "pref_timeout_interval", 5 * 60));
|
||||
putBoolean(SCREEN_LOCK_ENABLED, TextSecurePreferences.getBooleanPreference(context, "pref_android_screen_lock", false));
|
||||
putLong(SCREEN_LOCK_TIMEOUT, TextSecurePreferences.getLongPreference(context, "pref_android_screen_lock_timeout", 0));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -121,7 +140,12 @@ public final class SettingsValues extends SignalStoreValues {
|
||||
SENT_MEDIA_QUALITY,
|
||||
KEEP_MUTED_CHATS_ARCHIVED,
|
||||
USE_COMPACT_NAVIGATION_BAR,
|
||||
THREAD_TRIM_SYNC_TO_LINKED_DEVICES);
|
||||
THREAD_TRIM_SYNC_TO_LINKED_DEVICES,
|
||||
PASSPHRASE_DISABLED,
|
||||
PASSPHRASE_TIMEOUT_ENABLED,
|
||||
PASSPHRASE_TIMEOUT,
|
||||
SCREEN_LOCK_ENABLED,
|
||||
SCREEN_LOCK_TIMEOUT);
|
||||
}
|
||||
|
||||
public @NonNull LiveData<String> getOnConfigurationSettingChanged() {
|
||||
@@ -461,6 +485,46 @@ public final class SettingsValues extends SignalStoreValues {
|
||||
return getBoolean(USE_COMPACT_NAVIGATION_BAR, false);
|
||||
}
|
||||
|
||||
public void setPassphraseDisabled(boolean disabled) {
|
||||
putBoolean(PASSPHRASE_DISABLED, disabled);
|
||||
}
|
||||
|
||||
public boolean getPassphraseDisabled() {
|
||||
return getBoolean(PASSPHRASE_DISABLED, true);
|
||||
}
|
||||
|
||||
public void setPassphraseTimeoutEnabled(boolean enabled) {
|
||||
putBoolean(PASSPHRASE_TIMEOUT_ENABLED, enabled);
|
||||
}
|
||||
|
||||
public boolean getPassphraseTimeoutEnabled() {
|
||||
return getBoolean(PASSPHRASE_TIMEOUT_ENABLED, false);
|
||||
}
|
||||
|
||||
public void setPassphraseTimeout(int minutes) {
|
||||
putLong(PASSPHRASE_TIMEOUT, minutes);
|
||||
}
|
||||
|
||||
public int getPassphraseTimeout() {
|
||||
return getInteger(PASSPHRASE_TIMEOUT, 0);
|
||||
}
|
||||
|
||||
public void setScreenLockEnabled(boolean enabled) {
|
||||
putBoolean(SCREEN_LOCK_ENABLED, enabled);
|
||||
}
|
||||
|
||||
public boolean getScreenLockEnabled() {
|
||||
return getBoolean(SCREEN_LOCK_ENABLED, false);
|
||||
}
|
||||
|
||||
public void setScreenLockTimeout(long seconds) {
|
||||
putLong(SCREEN_LOCK_TIMEOUT, seconds);
|
||||
}
|
||||
|
||||
public long getScreenLockTimeout() {
|
||||
return getLong(SCREEN_LOCK_TIMEOUT, 0);
|
||||
}
|
||||
|
||||
private @Nullable Uri getUri(@NonNull String key) {
|
||||
String uri = getString(key, "");
|
||||
|
||||
|
||||
@@ -19,9 +19,9 @@ final class LogSectionKeyPreferences implements LogSection {
|
||||
|
||||
@Override
|
||||
public @NonNull CharSequence getContent(@NonNull Context context) {
|
||||
return new StringBuilder().append("Screen Lock : ").append(TextSecurePreferences.isScreenLockEnabled(context)).append("\n")
|
||||
.append("Screen Lock Timeout : ").append(TextSecurePreferences.getScreenLockTimeout(context)).append("\n")
|
||||
.append("Password Disabled : ").append(TextSecurePreferences.isPasswordDisabled(context)).append("\n")
|
||||
return new StringBuilder().append("Screen Lock : ").append(SignalStore.settings().getScreenLockEnabled()).append("\n")
|
||||
.append("Screen Lock Timeout : ").append(SignalStore.settings().getScreenLockTimeout()).append("\n")
|
||||
.append("Password Disabled : ").append(SignalStore.settings().getPassphraseDisabled()).append("\n")
|
||||
.append("Prefer Contact Photos : ").append(SignalStore.settings().isPreferSystemContactPhotos()).append("\n")
|
||||
.append("Call Data Mode : ").append(SignalStore.settings().getCallDataMode()).append("\n")
|
||||
.append("Media Quality : ").append(SignalStore.settings().getSentMediaQuality()).append("\n")
|
||||
|
||||
@@ -32,6 +32,7 @@ import android.os.SystemClock;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.thoughtcrime.securesms.BuildConfig;
|
||||
@@ -42,11 +43,11 @@ import org.thoughtcrime.securesms.crypto.InvalidPassphraseException;
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecretUtil;
|
||||
import org.thoughtcrime.securesms.dependencies.AppDependencies;
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||
import org.thoughtcrime.securesms.migrations.ApplicationMigrations;
|
||||
import org.thoughtcrime.securesms.notifications.NotificationChannels;
|
||||
import org.thoughtcrime.securesms.util.DynamicLanguage;
|
||||
import org.thoughtcrime.securesms.util.ServiceUtil;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@@ -80,17 +81,17 @@ public class KeyCachingService extends Service {
|
||||
public KeyCachingService() {}
|
||||
|
||||
public static synchronized boolean isLocked(Context context) {
|
||||
boolean locked = masterSecret == null && (!TextSecurePreferences.isPasswordDisabled(context) || TextSecurePreferences.isScreenLockEnabled(context));
|
||||
boolean locked = masterSecret == null && (!SignalStore.settings().getPassphraseDisabled() || SignalStore.settings().getScreenLockEnabled());
|
||||
|
||||
if (locked) {
|
||||
Log.d(TAG, "Locked! PasswordDisabled: " + TextSecurePreferences.isPasswordDisabled(context) + ", ScreenLock: " + TextSecurePreferences.isScreenLockEnabled(context));
|
||||
Log.d(TAG, "Locked! PasswordDisabled: " + SignalStore.settings().getPassphraseDisabled() + ", ScreenLock: " + SignalStore.settings().getScreenLockEnabled());
|
||||
}
|
||||
|
||||
return locked;
|
||||
}
|
||||
|
||||
public static synchronized @Nullable MasterSecret getMasterSecret(Context context) {
|
||||
if (masterSecret == null && (TextSecurePreferences.isPasswordDisabled(context) && !TextSecurePreferences.isScreenLockEnabled(context))) {
|
||||
if (masterSecret == null && (SignalStore.settings().getPassphraseDisabled() && !SignalStore.settings().getScreenLockEnabled())) {
|
||||
try {
|
||||
return MasterSecretUtil.getMasterSecret(context, MasterSecretUtil.UNENCRYPTED_PASSPHRASE);
|
||||
} catch (InvalidPassphraseException e) {
|
||||
@@ -102,7 +103,7 @@ public class KeyCachingService extends Service {
|
||||
}
|
||||
|
||||
public static void onAppForegrounded(@NonNull Context context) {
|
||||
if (TextSecurePreferences.isScreenLockEnabled(context) || !TextSecurePreferences.isPasswordDisabled(context)) {
|
||||
if (SignalStore.settings().getScreenLockEnabled() || !SignalStore.settings().getPassphraseDisabled()) {
|
||||
ServiceUtil.getAlarmManager(context).cancel(buildExpirationPendingIntent(context));
|
||||
}
|
||||
}
|
||||
@@ -155,7 +156,7 @@ public class KeyCachingService extends Service {
|
||||
Log.i(TAG, "onCreate()");
|
||||
super.onCreate();
|
||||
|
||||
if (TextSecurePreferences.isPasswordDisabled(this) && !TextSecurePreferences.isScreenLockEnabled(this)) {
|
||||
if (SignalStore.settings().getPassphraseDisabled() && !SignalStore.settings().getScreenLockEnabled()) {
|
||||
try {
|
||||
MasterSecret masterSecret = MasterSecretUtil.getMasterSecret(this, MasterSecretUtil.UNENCRYPTED_PASSPHRASE);
|
||||
setMasterSecret(masterSecret);
|
||||
@@ -215,8 +216,8 @@ public class KeyCachingService extends Service {
|
||||
}
|
||||
|
||||
private void handleDisableService() {
|
||||
if (TextSecurePreferences.isPasswordDisabled(this) &&
|
||||
!TextSecurePreferences.isScreenLockEnabled(this))
|
||||
if (SignalStore.settings().getPassphraseDisabled() &&
|
||||
!SignalStore.settings().getScreenLockEnabled())
|
||||
{
|
||||
stopForeground(true);
|
||||
}
|
||||
@@ -231,33 +232,40 @@ public class KeyCachingService extends Service {
|
||||
boolean appVisible = AppDependencies.getAppForegroundObserver().isForegrounded();
|
||||
boolean secretSet = KeyCachingService.masterSecret != null;
|
||||
|
||||
boolean timeoutEnabled = TextSecurePreferences.isPassphraseTimeoutEnabled(context);
|
||||
boolean passLockActive = timeoutEnabled && !TextSecurePreferences.isPasswordDisabled(context);
|
||||
boolean timeoutEnabled = SignalStore.settings().getPassphraseTimeoutEnabled();
|
||||
boolean passLockActive = timeoutEnabled && !SignalStore.settings().getPassphraseDisabled();
|
||||
|
||||
long screenTimeout = TextSecurePreferences.getScreenLockTimeout(context);
|
||||
boolean screenLockActive = screenTimeout >= 60 && TextSecurePreferences.isScreenLockEnabled(context);
|
||||
long screenTimeout = SignalStore.settings().getScreenLockTimeout();
|
||||
boolean screenLockActive = SignalStore.settings().getScreenLockEnabled();
|
||||
boolean immediateScreenLock = screenTimeout == 0 && screenLockActive;
|
||||
|
||||
if (!appVisible && secretSet && (passLockActive || screenLockActive)) {
|
||||
long passphraseTimeoutMinutes = TextSecurePreferences.getPassphraseTimeoutInterval(context);
|
||||
long screenLockTimeoutSeconds = TextSecurePreferences.getScreenLockTimeout(context);
|
||||
if (immediateScreenLock) {
|
||||
Log.i(TAG, "Starting immediate screen lock");
|
||||
Intent intent = new Intent(PASSPHRASE_EXPIRED_EVENT, null, context, KeyCachingService.class);
|
||||
context.startService(intent);
|
||||
} else {
|
||||
long passphraseTimeoutMinutes = SignalStore.settings().getPassphraseTimeout();
|
||||
long screenLockTimeoutSeconds = SignalStore.settings().getScreenLockTimeout();
|
||||
|
||||
long timeoutMillis;
|
||||
long timeoutMillis;
|
||||
|
||||
if (!TextSecurePreferences.isPasswordDisabled(context)) timeoutMillis = TimeUnit.MINUTES.toMillis(passphraseTimeoutMinutes);
|
||||
else timeoutMillis = TimeUnit.SECONDS.toMillis(screenLockTimeoutSeconds);
|
||||
if (!SignalStore.settings().getPassphraseDisabled()) timeoutMillis = TimeUnit.MINUTES.toMillis(passphraseTimeoutMinutes);
|
||||
else timeoutMillis = TimeUnit.SECONDS.toMillis(screenLockTimeoutSeconds);
|
||||
|
||||
Log.i(TAG, "Starting timeout: " + timeoutMillis);
|
||||
Log.i(TAG, "Starting timeout: " + timeoutMillis);
|
||||
|
||||
AlarmManager alarmManager = ServiceUtil.getAlarmManager(context);
|
||||
PendingIntent expirationIntent = buildExpirationPendingIntent(context);
|
||||
AlarmManager alarmManager = ServiceUtil.getAlarmManager(context);
|
||||
PendingIntent expirationIntent = buildExpirationPendingIntent(context);
|
||||
|
||||
alarmManager.cancel(expirationIntent);
|
||||
alarmManager.set(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + timeoutMillis, expirationIntent);
|
||||
alarmManager.cancel(expirationIntent);
|
||||
alarmManager.set(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + timeoutMillis, expirationIntent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void foregroundService() {
|
||||
if (TextSecurePreferences.isPasswordDisabled(this) && !TextSecurePreferences.isScreenLockEnabled(this)) {
|
||||
if (SignalStore.settings().getPassphraseDisabled() && !SignalStore.settings().getScreenLockEnabled()) {
|
||||
stopForeground(true);
|
||||
return;
|
||||
}
|
||||
@@ -267,7 +275,8 @@ public class KeyCachingService extends Service {
|
||||
|
||||
builder.setContentTitle(getString(R.string.KeyCachingService_passphrase_cached));
|
||||
builder.setContentText(getString(R.string.KeyCachingService_signal_passphrase_cached));
|
||||
builder.setSmallIcon(R.drawable.icon_cached);
|
||||
builder.setSmallIcon(R.drawable.ic_notification_unlocked);
|
||||
builder.setColor(ContextCompat.getColor(this, R.color.signal_light_colorSecondary));
|
||||
builder.setWhen(0);
|
||||
builder.setPriority(Notification.PRIORITY_MIN);
|
||||
builder.setOngoing(true);
|
||||
|
||||
@@ -3,8 +3,8 @@ package org.thoughtcrime.securesms.service
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.util.ServiceUtil
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||
|
||||
/**
|
||||
* Respond to a PanicKit trigger Intent by locking the app. PanicKit provides a
|
||||
@@ -14,7 +14,7 @@ import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||
*/
|
||||
class PanicResponderListener : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
val passwordEnabled = !TextSecurePreferences.isPasswordDisabled(context)
|
||||
val passwordEnabled = !SignalStore.settings.passphraseDisabled
|
||||
val keyguardSecure = ServiceUtil.getKeyguardManager(context).isKeyguardSecure
|
||||
val intentAction = intent.action
|
||||
if ((passwordEnabled || keyguardSecure) && "info.guardianproject.panic.action.TRIGGER" == intentAction) {
|
||||
|
||||
Reference in New Issue
Block a user