kopia lustrzana https://github.com/ryukoposting/Signal-Android
299 wiersze
13 KiB
Java
299 wiersze
13 KiB
Java
package org.thoughtcrime.securesms.registration.fragments;
|
|
|
|
import static org.thoughtcrime.securesms.registration.fragments.RegistrationViewDelegate.setDebugLogSubmitMultiTapView;
|
|
import static org.thoughtcrime.securesms.registration.fragments.RegistrationViewDelegate.showConfirmNumberDialogIfTranslated;
|
|
import static org.thoughtcrime.securesms.util.CircularProgressButtonUtil.cancelSpinning;
|
|
import static org.thoughtcrime.securesms.util.CircularProgressButtonUtil.setSpinning;
|
|
|
|
import android.content.Context;
|
|
import android.os.Bundle;
|
|
import android.text.TextUtils;
|
|
import android.view.LayoutInflater;
|
|
import android.view.Menu;
|
|
import android.view.MenuInflater;
|
|
import android.view.MenuItem;
|
|
import android.view.View;
|
|
import android.view.ViewGroup;
|
|
import android.widget.ScrollView;
|
|
import android.widget.Spinner;
|
|
import android.widget.Toast;
|
|
|
|
import androidx.annotation.NonNull;
|
|
import androidx.annotation.Nullable;
|
|
import androidx.appcompat.app.AppCompatActivity;
|
|
import androidx.appcompat.widget.Toolbar;
|
|
import androidx.lifecycle.ViewModelProvider;
|
|
import androidx.navigation.NavController;
|
|
import androidx.navigation.Navigation;
|
|
import androidx.navigation.fragment.NavHostFragment;
|
|
|
|
import com.dd.CircularProgressButton;
|
|
import com.google.android.gms.auth.api.phone.SmsRetriever;
|
|
import com.google.android.gms.auth.api.phone.SmsRetrieverClient;
|
|
import com.google.android.gms.common.ConnectionResult;
|
|
import com.google.android.gms.common.GoogleApiAvailability;
|
|
import com.google.android.gms.tasks.Task;
|
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
|
|
|
import org.signal.core.util.ThreadUtil;
|
|
import org.signal.core.util.logging.Log;
|
|
import org.thoughtcrime.securesms.LoggingFragment;
|
|
import org.thoughtcrime.securesms.R;
|
|
import org.thoughtcrime.securesms.components.LabeledEditText;
|
|
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
|
import org.thoughtcrime.securesms.registration.VerifyAccountRepository.Mode;
|
|
import org.thoughtcrime.securesms.registration.util.RegistrationNumberInputController;
|
|
import org.thoughtcrime.securesms.registration.viewmodel.NumberViewState;
|
|
import org.thoughtcrime.securesms.registration.viewmodel.RegistrationViewModel;
|
|
import org.thoughtcrime.securesms.util.Dialogs;
|
|
import org.thoughtcrime.securesms.util.LifecycleDisposable;
|
|
import org.thoughtcrime.securesms.util.PlayServicesUtil;
|
|
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
|
import org.thoughtcrime.securesms.util.ViewUtil;
|
|
|
|
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
|
import io.reactivex.rxjava3.disposables.Disposable;
|
|
|
|
public final class EnterPhoneNumberFragment extends LoggingFragment implements RegistrationNumberInputController.Callbacks {
|
|
|
|
private static final String TAG = Log.tag(EnterPhoneNumberFragment.class);
|
|
|
|
private LabeledEditText countryCode;
|
|
private LabeledEditText number;
|
|
private CircularProgressButton register;
|
|
private Spinner countrySpinner;
|
|
private View cancel;
|
|
private ScrollView scrollView;
|
|
private RegistrationViewModel viewModel;
|
|
|
|
private final LifecycleDisposable disposables = new LifecycleDisposable();
|
|
|
|
@Override
|
|
public void onCreate(@Nullable Bundle savedInstanceState) {
|
|
super.onCreate(savedInstanceState);
|
|
setHasOptionsMenu(true);
|
|
}
|
|
|
|
@Override
|
|
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
|
return inflater.inflate(R.layout.fragment_registration_enter_phone_number, container, false);
|
|
}
|
|
|
|
@Override
|
|
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
|
super.onViewCreated(view, savedInstanceState);
|
|
|
|
setDebugLogSubmitMultiTapView(view.findViewById(R.id.verify_header));
|
|
|
|
countryCode = view.findViewById(R.id.country_code);
|
|
number = view.findViewById(R.id.number);
|
|
countrySpinner = view.findViewById(R.id.country_spinner);
|
|
cancel = view.findViewById(R.id.cancel_button);
|
|
scrollView = view.findViewById(R.id.scroll_view);
|
|
register = view.findViewById(R.id.registerButton);
|
|
|
|
RegistrationNumberInputController controller = new RegistrationNumberInputController(requireContext(),
|
|
countryCode,
|
|
number,
|
|
countrySpinner,
|
|
false,
|
|
this);
|
|
|
|
register.setOnClickListener(v -> handleRegister(requireContext()));
|
|
|
|
disposables.bindTo(getViewLifecycleOwner().getLifecycle());
|
|
viewModel = new ViewModelProvider(requireActivity()).get(RegistrationViewModel.class);
|
|
|
|
if (viewModel.isReregister()) {
|
|
cancel.setVisibility(View.VISIBLE);
|
|
cancel.setOnClickListener(v -> Navigation.findNavController(v).navigateUp());
|
|
} else {
|
|
cancel.setVisibility(View.GONE);
|
|
}
|
|
|
|
viewModel.getLiveNumber().observe(getViewLifecycleOwner(), controller::updateNumber);
|
|
|
|
if (viewModel.hasCaptchaToken()) {
|
|
ThreadUtil.runOnMainDelayed(() -> handleRegister(requireContext()), 250);
|
|
}
|
|
|
|
Toolbar toolbar = view.findViewById(R.id.toolbar);
|
|
((AppCompatActivity) requireActivity()).setSupportActionBar(toolbar);
|
|
((AppCompatActivity) requireActivity()).getSupportActionBar().setTitle(null);
|
|
}
|
|
|
|
@Override
|
|
public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
|
|
inflater.inflate(R.menu.enter_phone_number, menu);
|
|
}
|
|
|
|
@Override
|
|
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
|
if (item.getItemId() == R.id.phone_menu_use_proxy) {
|
|
Navigation.findNavController(requireView()).navigate(EnterPhoneNumberFragmentDirections.actionEditProxy());
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private void handleRegister(@NonNull Context context) {
|
|
if (TextUtils.isEmpty(countryCode.getText())) {
|
|
Toast.makeText(context, getString(R.string.RegistrationActivity_you_must_specify_your_country_code), Toast.LENGTH_LONG).show();
|
|
return;
|
|
}
|
|
|
|
if (TextUtils.isEmpty(this.number.getText())) {
|
|
Toast.makeText(context, getString(R.string.RegistrationActivity_you_must_specify_your_phone_number), Toast.LENGTH_LONG).show();
|
|
return;
|
|
}
|
|
|
|
final NumberViewState number = viewModel.getNumber();
|
|
final String e164number = number.getE164Number();
|
|
|
|
if (!number.isValid()) {
|
|
Dialogs.showAlertDialog(context,
|
|
getString(R.string.RegistrationActivity_invalid_number),
|
|
String.format(getString(R.string.RegistrationActivity_the_number_you_specified_s_is_invalid), e164number));
|
|
return;
|
|
}
|
|
|
|
PlayServicesUtil.PlayServicesStatus fcmStatus = PlayServicesUtil.getPlayServicesStatus(context);
|
|
|
|
if (fcmStatus == PlayServicesUtil.PlayServicesStatus.SUCCESS) {
|
|
confirmNumberPrompt(context, e164number, () -> handleRequestVerification(context, true));
|
|
} else if (fcmStatus == PlayServicesUtil.PlayServicesStatus.MISSING) {
|
|
confirmNumberPrompt(context, e164number, () -> handlePromptForNoPlayServices(context));
|
|
} else if (fcmStatus == PlayServicesUtil.PlayServicesStatus.NEEDS_UPDATE) {
|
|
GoogleApiAvailability.getInstance().getErrorDialog(requireActivity(), ConnectionResult.SERVICE_VERSION_UPDATE_REQUIRED, 0).show();
|
|
} else {
|
|
Dialogs.showAlertDialog(context,
|
|
getString(R.string.RegistrationActivity_play_services_error),
|
|
getString(R.string.RegistrationActivity_google_play_services_is_updating_or_unavailable));
|
|
}
|
|
}
|
|
|
|
private void handleRequestVerification(@NonNull Context context, boolean fcmSupported) {
|
|
setSpinning(register);
|
|
disableAllEntries();
|
|
|
|
if (fcmSupported) {
|
|
SmsRetrieverClient client = SmsRetriever.getClient(context);
|
|
Task<Void> task = client.startSmsRetriever();
|
|
|
|
task.addOnSuccessListener(none -> {
|
|
Log.i(TAG, "Successfully registered SMS listener.");
|
|
requestVerificationCode(Mode.SMS_WITH_LISTENER);
|
|
});
|
|
|
|
task.addOnFailureListener(e -> {
|
|
Log.w(TAG, "Failed to register SMS listener.", e);
|
|
requestVerificationCode(Mode.SMS_WITHOUT_LISTENER);
|
|
});
|
|
} else {
|
|
Log.i(TAG, "FCM is not supported, using no SMS listener");
|
|
requestVerificationCode(Mode.SMS_WITHOUT_LISTENER);
|
|
}
|
|
}
|
|
|
|
private void disableAllEntries() {
|
|
countryCode.setEnabled(false);
|
|
number.setEnabled(false);
|
|
countrySpinner.setEnabled(false);
|
|
cancel.setVisibility(View.GONE);
|
|
}
|
|
|
|
private void enableAllEntries() {
|
|
countryCode.setEnabled(true);
|
|
number.setEnabled(true);
|
|
countrySpinner.setEnabled(true);
|
|
if (viewModel.isReregister()) {
|
|
cancel.setVisibility(View.VISIBLE);
|
|
}
|
|
}
|
|
|
|
private void requestVerificationCode(@NonNull Mode mode) {
|
|
NavController navController = NavHostFragment.findNavController(this);
|
|
|
|
Disposable request = viewModel.requestVerificationCode(mode)
|
|
.doOnSubscribe(unused -> TextSecurePreferences.setPushRegistered(ApplicationDependencies.getApplication(), false))
|
|
.observeOn(AndroidSchedulers.mainThread())
|
|
.subscribe(processor -> {
|
|
if (processor.hasResult()) {
|
|
navController.navigate(EnterPhoneNumberFragmentDirections.actionEnterVerificationCode());
|
|
} else if (processor.localRateLimit()) {
|
|
Log.i(TAG, "Unable to request sms code due to local rate limit");
|
|
navController.navigate(EnterPhoneNumberFragmentDirections.actionEnterVerificationCode());
|
|
} else if (processor.captchaRequired()) {
|
|
Log.i(TAG, "Unable to request sms code due to captcha required");
|
|
navController.navigate(EnterPhoneNumberFragmentDirections.actionRequestCaptcha());
|
|
} else if (processor.rateLimit()) {
|
|
Log.i(TAG, "Unable to request sms code due to rate limit");
|
|
Toast.makeText(register.getContext(), R.string.RegistrationActivity_rate_limited_to_service, Toast.LENGTH_LONG).show();
|
|
} else {
|
|
Log.w(TAG, "Unable to request sms code", processor.getError());
|
|
Toast.makeText(register.getContext(), R.string.RegistrationActivity_unable_to_connect_to_service, Toast.LENGTH_LONG).show();
|
|
}
|
|
|
|
cancelSpinning(register);
|
|
enableAllEntries();
|
|
});
|
|
|
|
disposables.add(request);
|
|
}
|
|
|
|
@Override
|
|
public void onNumberFocused() {
|
|
scrollView.postDelayed(() -> scrollView.smoothScrollTo(0, register.getBottom()), 250);
|
|
}
|
|
|
|
@Override
|
|
public void onNumberInputNext(@NonNull View view) {
|
|
// Intentionally left blank
|
|
}
|
|
|
|
@Override
|
|
public void onNumberInputDone(@NonNull View view) {
|
|
ViewUtil.hideKeyboard(requireContext(), view);
|
|
handleRegister(requireContext());
|
|
}
|
|
|
|
@Override
|
|
public void onPickCountry(@NonNull View view) {
|
|
Navigation.findNavController(view).navigate(R.id.action_pickCountry);
|
|
}
|
|
|
|
@Override
|
|
public void setNationalNumber(@NonNull String number) {
|
|
viewModel.setNationalNumber(number);
|
|
}
|
|
|
|
@Override
|
|
public void setCountry(int countryCode) {
|
|
viewModel.onCountrySelected(null, countryCode);
|
|
}
|
|
|
|
private void handlePromptForNoPlayServices(@NonNull Context context) {
|
|
new MaterialAlertDialogBuilder(context)
|
|
.setTitle(R.string.RegistrationActivity_missing_google_play_services)
|
|
.setMessage(R.string.RegistrationActivity_this_device_is_missing_google_play_services)
|
|
.setPositiveButton(R.string.RegistrationActivity_i_understand, (dialog1, which) -> handleRequestVerification(context, false))
|
|
.setNegativeButton(android.R.string.cancel, null)
|
|
.show();
|
|
}
|
|
|
|
protected final void confirmNumberPrompt(@NonNull Context context,
|
|
@NonNull String e164number,
|
|
@NonNull Runnable onConfirmed)
|
|
{
|
|
showConfirmNumberDialogIfTranslated(context,
|
|
R.string.RegistrationActivity_a_verification_code_will_be_sent_to,
|
|
e164number,
|
|
() -> {
|
|
ViewUtil.hideKeyboard(context, number.getInput());
|
|
onConfirmed.run();
|
|
},
|
|
() -> number.focusAndMoveCursorToEndAndOpenKeyboard());
|
|
}
|
|
}
|