kopia lustrzana https://github.com/ryukoposting/Signal-Android
Fix bottom sheet behavior and design.
rodzic
2ffc576387
commit
f3922c4156
|
@ -14,6 +14,7 @@ import androidx.annotation.Nullable;
|
|||
import androidx.annotation.StringRes;
|
||||
import androidx.navigation.Navigation;
|
||||
|
||||
import com.google.android.material.button.MaterialButton;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
|
@ -28,9 +29,7 @@ import org.thoughtcrime.securesms.components.registration.VerificationPinKeyboar
|
|||
import org.thoughtcrime.securesms.registration.ReceivedSmsEvent;
|
||||
import org.thoughtcrime.securesms.registration.VerifyAccountRepository;
|
||||
import org.thoughtcrime.securesms.registration.viewmodel.BaseRegistrationViewModel;
|
||||
import org.thoughtcrime.securesms.util.CommunicationActions;
|
||||
import org.thoughtcrime.securesms.util.LifecycleDisposable;
|
||||
import org.thoughtcrime.securesms.util.SupportEmailUtil;
|
||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||
import org.thoughtcrime.securesms.util.concurrent.AssertedSuccessListener;
|
||||
import org.thoughtcrime.securesms.util.dualsim.MccMncProducer;
|
||||
|
@ -62,7 +61,8 @@ public abstract class BaseEnterSmsCodeFragment<ViewModel extends BaseRegistratio
|
|||
private VerificationPinKeyboard keyboard;
|
||||
private ActionCountDownButton callMeCountDown;
|
||||
private ActionCountDownButton resendSmsCountDown;
|
||||
private View wrongNumber;
|
||||
private MaterialButton wrongNumber;
|
||||
private MaterialButton bottomSheetButton;
|
||||
private boolean autoCompleting;
|
||||
|
||||
private ViewModel viewModel;
|
||||
|
@ -87,6 +87,7 @@ public abstract class BaseEnterSmsCodeFragment<ViewModel extends BaseRegistratio
|
|||
callMeCountDown = view.findViewById(R.id.call_me_count_down);
|
||||
resendSmsCountDown = view.findViewById(R.id.resend_sms_count_down);
|
||||
wrongNumber = view.findViewById(R.id.wrong_number);
|
||||
bottomSheetButton = view.findViewById(R.id.having_trouble_button);
|
||||
|
||||
new SignalStrengthPhoneStateListener(this, this);
|
||||
|
||||
|
@ -96,6 +97,7 @@ public abstract class BaseEnterSmsCodeFragment<ViewModel extends BaseRegistratio
|
|||
setOnCodeFullyEnteredListener(verificationCodeView);
|
||||
|
||||
wrongNumber.setOnClickListener(v -> returnToPhoneEntryScreen());
|
||||
bottomSheetButton.setOnClickListener( v -> showBottomSheet());
|
||||
|
||||
callMeCountDown.setTextResources(R.string.RegistrationActivity_call, R.string.RegistrationActivity_call_me_instead_available_in);
|
||||
resendSmsCountDown.setTextResources(R.string.RegistrationActivity_resend_code, R.string.RegistrationActivity_resend_sms_available_in);
|
||||
|
@ -120,9 +122,9 @@ public abstract class BaseEnterSmsCodeFragment<ViewModel extends BaseRegistratio
|
|||
|
||||
disposables.bindTo(getViewLifecycleOwner().getLifecycle());
|
||||
viewModel = getViewModel();
|
||||
viewModel.getSuccessfulCodeRequestAttempts().observe(getViewLifecycleOwner(), (attempts) -> {
|
||||
viewModel.getIncorrectCodeAttempts().observe(getViewLifecycleOwner(), (attempts) -> {
|
||||
if (attempts >= 3) {
|
||||
new ContactSupportBottomSheetFragment(this::openTroubleshootingSteps, this::sendEmailToSupport).show(getChildFragmentManager(), "support_bottom_sheet");
|
||||
bottomSheetButton.setVisibility(View.VISIBLE);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -229,6 +231,8 @@ public abstract class BaseEnterSmsCodeFragment<ViewModel extends BaseRegistratio
|
|||
}
|
||||
|
||||
protected void handleIncorrectCodeError() {
|
||||
viewModel.incrementIncorrectCodeAttempts();
|
||||
|
||||
Toast.makeText(requireContext(), R.string.RegistrationActivity_incorrect_code, Toast.LENGTH_LONG).show();
|
||||
keyboard.displayFailure().addListener(new AssertedSuccessListener<Boolean>() {
|
||||
@Override
|
||||
|
@ -416,19 +420,10 @@ public abstract class BaseEnterSmsCodeFragment<ViewModel extends BaseRegistratio
|
|||
});
|
||||
}
|
||||
|
||||
private void openTroubleshootingSteps() {
|
||||
CommunicationActions.openBrowserLink(requireContext(), getString(R.string.support_center_url));
|
||||
}
|
||||
|
||||
private void sendEmailToSupport() {
|
||||
String body = SupportEmailUtil.generateSupportEmailBody(requireContext(),
|
||||
R.string.RegistrationActivity_code_support_subject,
|
||||
null,
|
||||
null);
|
||||
CommunicationActions.openEmail(requireContext(),
|
||||
SupportEmailUtil.getSupportEmailAddress(requireContext()),
|
||||
getString(R.string.RegistrationActivity_code_support_subject),
|
||||
body);
|
||||
private void showBottomSheet() {
|
||||
ContactSupportBottomSheetFragment bottomSheet = new ContactSupportBottomSheetFragment();
|
||||
bottomSheet.show(getChildFragmentManager(), "support_bottom_sheet");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -6,31 +6,98 @@ import androidx.compose.foundation.layout.padding
|
|||
import androidx.compose.foundation.layout.wrapContentSize
|
||||
import androidx.compose.foundation.text.ClickableText
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.buildAnnotatedString
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.withStyle
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.compose.ComposeBottomSheetDialogFragment
|
||||
import org.thoughtcrime.securesms.util.CommunicationActions
|
||||
import org.thoughtcrime.securesms.util.SupportEmailUtil
|
||||
|
||||
/**
|
||||
* Helpful bottom sheet dialog displayed during registration when the user enters the wrong verification code too many times.
|
||||
*/
|
||||
class ContactSupportBottomSheetFragment(private val troubleshootingStepsListener: Runnable, private val contactSupportListener: Runnable) : ComposeBottomSheetDialogFragment() {
|
||||
class ContactSupportBottomSheetFragment : ComposeBottomSheetDialogFragment() {
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
override fun SheetContent() {
|
||||
val annotatedText = buildAnnotatedString {
|
||||
withStyle(SpanStyle(fontSize = 28.sp, fontWeight = FontWeight.Bold, color = MaterialTheme.colorScheme.onSurface)) {
|
||||
append(stringResource(R.string.RegistrationActivity_support_bottom_sheet_title))
|
||||
val annotatedText = buildClickableString()
|
||||
|
||||
return Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.wrapContentSize(Alignment.Center)
|
||||
.padding(16.dp)
|
||||
) {
|
||||
Handle()
|
||||
Text(
|
||||
text = buildAnnotatedString {
|
||||
withStyle(SpanStyle(fontSize = 20.sp, fontWeight = FontWeight.Bold, color = MaterialTheme.colorScheme.onSurface)) {
|
||||
append(stringResource(R.string.RegistrationActivity_support_bottom_sheet_title))
|
||||
}
|
||||
},
|
||||
modifier = Modifier.padding(8.dp)
|
||||
)
|
||||
Text(
|
||||
text = stringResource(R.string.RegistrationActivity_support_bottom_sheet_body_suggestions),
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
modifier = Modifier.padding(8.dp)
|
||||
)
|
||||
ClickableText(
|
||||
text = annotatedText,
|
||||
onClick = { offset ->
|
||||
annotatedText.getStringAnnotations(
|
||||
tag = "URL",
|
||||
start = offset,
|
||||
end = offset
|
||||
)
|
||||
.firstOrNull()?.let { annotation ->
|
||||
when (annotation.item) {
|
||||
TROUBLESHOOTING_STEPS_KEY -> openTroubleshootingSteps()
|
||||
CONTACT_SUPPORT_KEY -> sendEmailToSupport()
|
||||
}
|
||||
}
|
||||
},
|
||||
modifier = Modifier.padding(8.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun buildClickableString(): AnnotatedString {
|
||||
val troubleshootingStepsString = stringResource(R.string.RegistrationActivity_support_bottom_sheet_cta_troubleshooting_steps_substring)
|
||||
val contactSupportString = stringResource(R.string.RegistrationActivity_support_bottom_sheet_cta_contact_support_substring)
|
||||
val completeString = stringResource(R.string.RegistrationActivity_support_bottom_sheet_body_call_to_action)
|
||||
|
||||
val troubleshootingStartIndex = completeString.indexOf(troubleshootingStepsString)
|
||||
val troubleshootingEndIndex = troubleshootingStartIndex + troubleshootingStepsString.length
|
||||
|
||||
val contactSupportStartIndex = completeString.indexOf(contactSupportString)
|
||||
val contactSupportEndIndex = contactSupportStartIndex + contactSupportString.length
|
||||
|
||||
val doesStringEndWithContactSupport = contactSupportEndIndex >= completeString.lastIndex
|
||||
|
||||
return buildAnnotatedString {
|
||||
withStyle(
|
||||
style = SpanStyle(
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
fontWeight = FontWeight.Normal
|
||||
)
|
||||
) {
|
||||
append(completeString.substring(0, troubleshootingStartIndex))
|
||||
}
|
||||
append(stringResource(R.string.RegistrationActivity_support_bottom_sheet_body_part_1))
|
||||
pushStringAnnotation(
|
||||
tag = "URL",
|
||||
annotation = TROUBLESHOOTING_STEPS_KEY
|
||||
|
@ -41,10 +108,17 @@ class ContactSupportBottomSheetFragment(private val troubleshootingStepsListener
|
|||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
) {
|
||||
append(stringResource(R.string.RegistrationActivity_support_bottom_sheet_body_part_2))
|
||||
append(troubleshootingStepsString)
|
||||
}
|
||||
pop()
|
||||
append(stringResource(R.string.RegistrationActivity_support_bottom_sheet_body_part_3))
|
||||
withStyle(
|
||||
style = SpanStyle(
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
fontWeight = FontWeight.Normal
|
||||
)
|
||||
) {
|
||||
append(completeString.substring(troubleshootingEndIndex, contactSupportStartIndex))
|
||||
}
|
||||
pushStringAnnotation(
|
||||
tag = "URL",
|
||||
annotation = CONTACT_SUPPORT_KEY
|
||||
|
@ -55,38 +129,34 @@ class ContactSupportBottomSheetFragment(private val troubleshootingStepsListener
|
|||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
) {
|
||||
append(stringResource(R.string.RegistrationActivity_support_bottom_sheet_body_part_4))
|
||||
append(contactSupportString)
|
||||
}
|
||||
pop()
|
||||
}
|
||||
|
||||
return Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.wrapContentSize(Alignment.Center)
|
||||
) {
|
||||
Handle()
|
||||
ClickableText(
|
||||
text = annotatedText,
|
||||
onClick = { offset ->
|
||||
// We check if there is an *URL* annotation attached to the text
|
||||
// at the clicked position
|
||||
annotatedText.getStringAnnotations(
|
||||
tag = "URL",
|
||||
start = offset,
|
||||
end = offset
|
||||
)
|
||||
.firstOrNull()?.let { annotation ->
|
||||
when (annotation.item) {
|
||||
TROUBLESHOOTING_STEPS_KEY -> troubleshootingStepsListener.run()
|
||||
CONTACT_SUPPORT_KEY -> contactSupportListener.run()
|
||||
}
|
||||
}
|
||||
},
|
||||
modifier = Modifier.padding(16.dp)
|
||||
)
|
||||
if (!doesStringEndWithContactSupport) {
|
||||
append(completeString.substring(contactSupportEndIndex, completeString.lastIndex))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun openTroubleshootingSteps() {
|
||||
CommunicationActions.openBrowserLink(requireContext(), getString(R.string.support_center_url))
|
||||
}
|
||||
|
||||
private fun sendEmailToSupport() {
|
||||
val body = SupportEmailUtil.generateSupportEmailBody(
|
||||
requireContext(),
|
||||
R.string.RegistrationActivity_code_support_subject,
|
||||
null,
|
||||
null
|
||||
)
|
||||
CommunicationActions.openEmail(
|
||||
requireContext(),
|
||||
SupportEmailUtil.getSupportEmailAddress(requireContext()),
|
||||
getString(R.string.RegistrationActivity_code_support_subject),
|
||||
body
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TROUBLESHOOTING_STEPS_KEY = "troubleshooting"
|
||||
private const val CONTACT_SUPPORT_KEY = "contact_support"
|
||||
|
|
|
@ -36,17 +36,17 @@ import io.reactivex.rxjava3.core.Single;
|
|||
*/
|
||||
public abstract class BaseRegistrationViewModel extends ViewModel {
|
||||
|
||||
private static final String STATE_NUMBER = "NUMBER";
|
||||
private static final String STATE_REGISTRATION_SECRET = "REGISTRATION_SECRET";
|
||||
private static final String STATE_VERIFICATION_CODE = "TEXT_CODE_ENTERED";
|
||||
private static final String STATE_CAPTCHA = "CAPTCHA";
|
||||
private static final String STATE_SUCCESSFUL_CODE_REQUEST_ATTEMPTS = "SUCCESSFUL_CODE_REQUEST_ATTEMPTS";
|
||||
private static final String STATE_REQUEST_RATE_LIMITER = "REQUEST_RATE_LIMITER";
|
||||
private static final String STATE_KBS_TOKEN = "KBS_TOKEN";
|
||||
private static final String STATE_TIME_REMAINING = "TIME_REMAINING";
|
||||
private static final String STATE_CAN_CALL_AT_TIME = "CAN_CALL_AT_TIME";
|
||||
private static final String STATE_CAN_SMS_AT_TIME = "CAN_SMS_AT_TIME";
|
||||
private static final String STATE_RECOVERY_PASSWORD = "RECOVERY_PASSWORD";
|
||||
private static final String STATE_NUMBER = "NUMBER";
|
||||
private static final String STATE_REGISTRATION_SECRET = "REGISTRATION_SECRET";
|
||||
private static final String STATE_VERIFICATION_CODE = "TEXT_CODE_ENTERED";
|
||||
private static final String STATE_CAPTCHA = "CAPTCHA";
|
||||
private static final String STATE_INCORRECT_CODE_ATTEMPTS = "STATE_INCORRECT_CODE_ATTEMPTS";
|
||||
private static final String STATE_REQUEST_RATE_LIMITER = "REQUEST_RATE_LIMITER";
|
||||
private static final String STATE_KBS_TOKEN = "KBS_TOKEN";
|
||||
private static final String STATE_TIME_REMAINING = "TIME_REMAINING";
|
||||
private static final String STATE_CAN_CALL_AT_TIME = "CAN_CALL_AT_TIME";
|
||||
private static final String STATE_CAN_SMS_AT_TIME = "CAN_SMS_AT_TIME";
|
||||
private static final String STATE_RECOVERY_PASSWORD = "RECOVERY_PASSWORD";
|
||||
|
||||
protected final SavedStateHandle savedState;
|
||||
protected final VerifyAccountRepository verifyAccountRepository;
|
||||
|
@ -65,7 +65,7 @@ public abstract class BaseRegistrationViewModel extends ViewModel {
|
|||
setInitialDefaultValue(STATE_NUMBER, NumberViewState.INITIAL);
|
||||
setInitialDefaultValue(STATE_REGISTRATION_SECRET, password);
|
||||
setInitialDefaultValue(STATE_VERIFICATION_CODE, "");
|
||||
setInitialDefaultValue(STATE_SUCCESSFUL_CODE_REQUEST_ATTEMPTS, 0);
|
||||
setInitialDefaultValue(STATE_INCORRECT_CODE_ATTEMPTS, 0);
|
||||
setInitialDefaultValue(STATE_REQUEST_RATE_LIMITER, new LocalCodeRequestRateLimiter(60_000));
|
||||
setInitialDefaultValue(STATE_RECOVERY_PASSWORD, SignalStore.kbsValues().getRecoveryPassword());
|
||||
}
|
||||
|
@ -160,13 +160,13 @@ public abstract class BaseRegistrationViewModel extends ViewModel {
|
|||
savedState.set(STATE_VERIFICATION_CODE, code);
|
||||
}
|
||||
|
||||
public void markASuccessfulAttempt() {
|
||||
public void incrementIncorrectCodeAttempts() {
|
||||
//noinspection ConstantConditions
|
||||
savedState.set(STATE_SUCCESSFUL_CODE_REQUEST_ATTEMPTS, (Integer) savedState.get(STATE_SUCCESSFUL_CODE_REQUEST_ATTEMPTS) + 1);
|
||||
savedState.set(STATE_INCORRECT_CODE_ATTEMPTS, (Integer) savedState.get(STATE_INCORRECT_CODE_ATTEMPTS) + 1);
|
||||
}
|
||||
|
||||
public LiveData<Integer> getSuccessfulCodeRequestAttempts() {
|
||||
return savedState.getLiveData(STATE_SUCCESSFUL_CODE_REQUEST_ATTEMPTS, 0);
|
||||
public LiveData<Integer> getIncorrectCodeAttempts() {
|
||||
return savedState.getLiveData(STATE_INCORRECT_CODE_ATTEMPTS, 0);
|
||||
}
|
||||
|
||||
public @Nullable TokenData getKeyBackupCurrentToken() {
|
||||
|
@ -244,10 +244,6 @@ public abstract class BaseRegistrationViewModel extends ViewModel {
|
|||
})
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.doOnSuccess((RegistrationSessionProcessor processor) -> {
|
||||
if (processor.hasResult()) {
|
||||
markASuccessfulAttempt();
|
||||
}
|
||||
|
||||
if (processor.hasResult() && processor.isAllowedToRequestCode()) {
|
||||
setCanSmsAtTime(processor.getNextCodeViaSmsAttempt());
|
||||
setCanCallAtTime(processor.getNextCodeViaCallAttempt());
|
||||
|
|
|
@ -101,6 +101,20 @@
|
|||
app:layout_constraintVertical_bias="1.0"
|
||||
tools:text="@string/RegistrationActivity_resend_code" />
|
||||
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/having_trouble_button"
|
||||
style="@style/Signal.Widget.Button.Large.Secondary"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="32dp"
|
||||
android:text="@string/RegistrationActivity_support_bottom_sheet_title"
|
||||
android:textColor="@color/signal_colorPrimary"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/code" />
|
||||
|
||||
<org.thoughtcrime.securesms.components.registration.VerificationCodeView
|
||||
android:id="@+id/code"
|
||||
android:layout_width="0dp"
|
||||
|
|
|
@ -1824,11 +1824,16 @@
|
|||
<string name="RegistrationActivity_call">Call</string>
|
||||
<string name="RegistrationActivity_verification_code">Verification Code</string>
|
||||
<string name="RegistrationActivity_resend_code">Resend Code</string>
|
||||
<!-- A title for a bottom sheet dialog offering to help a user having trouble entering their verification code.-->
|
||||
<string name="RegistrationActivity_support_bottom_sheet_title">Having trouble registering?</string>
|
||||
<string name="RegistrationActivity_support_bottom_sheet_body_part_1">• Make sure your phone has a cellular signal to receive your SMS or call\n • Confirm you can receive a phone call to the number\n • Check that you have entered your phone number correctly.\nFor more information, please follow </string>
|
||||
<string name="RegistrationActivity_support_bottom_sheet_body_part_2">these troubleshooting steps</string>
|
||||
<string name="RegistrationActivity_support_bottom_sheet_body_part_3"> or </string>
|
||||
<string name="RegistrationActivity_support_bottom_sheet_body_part_4">Contact Support</string>
|
||||
<!-- A list of suggestions to try for a user having trouble entering their verification code.-->
|
||||
<string name="RegistrationActivity_support_bottom_sheet_body_suggestions">• Make sure your phone has a cellular signal to receive your SMS or call\n • Confirm you can receive a phone call to the number\n • Check that you have entered your phone number correctly.</string>
|
||||
<!-- A call to action for a user having trouble entering the verification to seek further help. -->
|
||||
<string name="RegistrationActivity_support_bottom_sheet_body_call_to_action">For more information, please follow these troubleshooting steps or Contact Support</string>
|
||||
<!-- A clickable piece of text that will take the user to our website with additional suggestions.-->
|
||||
<string name="RegistrationActivity_support_bottom_sheet_cta_troubleshooting_steps_substring">these troubleshooting steps</string>
|
||||
<!-- A clickable piece of text that will pre-fill a request for support email in the user's email app.-->
|
||||
<string name="RegistrationActivity_support_bottom_sheet_cta_contact_support_substring">Contact Support</string>
|
||||
|
||||
<!-- RegistrationLockV2Dialog -->
|
||||
<string name="RegistrationLockV2Dialog_turn_on_registration_lock">Turn on Registration Lock?</string>
|
||||
|
|
Ładowanie…
Reference in New Issue