Firebase не отправляет код подтверждения на телефон во второй раз - PullRequest
4 голосов
/ 13 февраля 2020

В моем приложении я использую Firebase для проверки номера телефона пользователя. Однако система проверки несовместима и отправляет OTP только в первый раз. Например, я получаю OTP при первом входе в систему, но если я выхожу и пытаюсь войти снова, я не получаю OTP.

Это действие, при котором пользователю предлагается ввести OTP:

import android.app.ProgressDialog;
import android.content.Intent;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.snackbar.Snackbar;
import com.google.firebase.FirebaseException;
import com.google.firebase.auth.AuthResult;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.auth.PhoneAuthCredential;
import com.google.firebase.auth.PhoneAuthProvider;
import com.google.firebase.firestore.FirebaseFirestore;
import com.google.firebase.firestore.QueryDocumentSnapshot;
import com.google.firebase.firestore.QuerySnapshot;
import com.google.firebase.iid.FirebaseInstanceId;
import com.google.firebase.iid.InstanceIdResult;

import java.util.concurrent.TimeUnit;

public class EnterOTPActivity extends AppCompatActivity {
    String userPhoneNumber;

    private int autoResendCount=0, resumeCount=0;
    private final String ctryCode = "+91";
    private String verificationId;
    private FirebaseAuth mAuth;
    private TextView resendOtp, enterOtpMessage, timerTV;
    private ProgressDialog progressDialog;
    private EditText otpField;
    private boolean firstSend=true,timerOn=true;
    FirebaseFirestore db;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_enter_otp);
        mAuth = FirebaseAuth.getInstance();
        otpField = findViewById(R.id.editTextOtp);
        resendOtp =  findViewById(R.id.resendOTPButton);
        timerTV = findViewById(R.id.resendTimer);
        userPhoneNumber = getIntent().getStringExtra("USER_MOB").trim();
        sendVerificationCode(userPhoneNumber);
        startResendTimer(15);
        getSupportActionBar().setTitle("Verification");
        resendOtp.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (!timerOn) {
                    startResendTimer(30);
                    firstSend = false;
                    sendVerificationCode(userPhoneNumber);
                }
            }
        });
        FloatingActionButton next = findViewById(R.id.fab2);
        enterOtpMessage =  findViewById(R.id.aboutotpverif);
        enterOtpMessage.setText(enterOtpMessage.getText().toString() + " " + ctryCode + userPhoneNumber);
        progressDialog = new ProgressDialog(this);
        progressDialog.setMessage("Please wait while we check your verification code");
        progressDialog.setTitle("Verification");
        progressDialog.setCancelable(false);
        next.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                //check if otp is correct here
                if (otpField.getText().toString().length() == 6) {
                    verifyCode(otpField.getText().toString().trim());
                    if(!progressDialog.isShowing())
                    progressDialog.show();
                } else {
                    View parentLayout = findViewById(android.R.id.content);
                    Snackbar.make(parentLayout, "A valid verification code has 6 digits", Snackbar.LENGTH_SHORT)
                            .setAction("OKAY", new View.OnClickListener() {
                                @Override
                                public void onClick(View view) {

                                }
                            })
                            .setActionTextColor(getResources().getColor(android.R.color.holo_red_light))
                            .show();
                }
            }
        });
    }

    private void verifyCode(String code) {

        try {
            PhoneAuthCredential credential = PhoneAuthProvider.getCredential(verificationId, code);
            signInwithCredential(credential);

        }
        catch (Exception e) {
            // code here is executed when we don't get the OTP and enter a wrong one
            progressDialog.dismiss();
            progressDialog.setCancelable(true);
            View parentLayout = findViewById(android.R.id.content);
            Snackbar.make(parentLayout, getString(R.string.incorrect_code_t1), Snackbar.LENGTH_LONG)
                    .setAction("OKAY", new View.OnClickListener() {
                        @Override
                        public void onClick(View view) {

                        }
                    })
                    .setActionTextColor(getResources().getColor(android.R.color.holo_red_light))
                    .show();
            if(progressDialog.isShowing())
            progressDialog.dismiss();
        }
    }

    private void signInwithCredential(PhoneAuthCredential credential) {
        mAuth.signInWithCredential(credential).addOnCompleteListener(new OnCompleteListener<AuthResult>() {
            @Override
            public void onComplete(@NonNull Task<AuthResult> task) {
                if (task.isSuccessful()) {
                    isRegisteredUser();
                }
                else {
                    if(progressDialog.isShowing())
                    progressDialog.dismiss();
                    View parentLayout = findViewById(android.R.id.content);
                    Snackbar.make(parentLayout, "Incorrect verification code", Snackbar.LENGTH_SHORT)
                            .setAction("OKAY", new View.OnClickListener() {
                                @Override
                                public void onClick(View view) {

                                }
                            })
                            .setActionTextColor(getResources().getColor(android.R.color.holo_red_light))
                            .show();
                    if(progressDialog.isShowing())
                    progressDialog.dismiss();

                }
            }
        });

    }

    private void sendVerificationCode(String number) {
        final String phoneNumber = ctryCode + number;
        PhoneAuthProvider.getInstance().verifyPhoneNumber(
                phoneNumber,        // Phone number to verify
                120,                 // Timeout duration
                TimeUnit.SECONDS,   // Unit of timeout
                this,               // Activity (for callback binding)
                mCallbacks);        // OnVerificationStateChangedCallbacks
    }

    private PhoneAuthProvider.OnVerificationStateChangedCallbacks mCallbacks = new PhoneAuthProvider.OnVerificationStateChangedCallbacks() {

        @Override
        public void onCodeSent(@NonNull String s, @NonNull PhoneAuthProvider.ForceResendingToken forceResendingToken) {
            super.onCodeSent(s, forceResendingToken);
            verificationId = s;
        }

        @Override
        public void onVerificationCompleted(@NonNull PhoneAuthCredential phoneAuthCredential) {
            String code = phoneAuthCredential.getSmsCode();
            if (code != null) {
                verifyCode(code);
                otpField.setText(code);
                if(!progressDialog.isShowing())
                progressDialog.show();
            }
        }

        @Override
        public void onVerificationFailed(@NonNull FirebaseException e) {
            Toast.makeText(getApplicationContext(), e.getMessage(), Toast.LENGTH_LONG).show();
        }
    };

    private void isRegisteredUser() {

        final String userId = ctryCode + userPhoneNumber;
        db = FirebaseFirestore.getInstance();
        db.collection("users")
                .whereEqualTo("phone", userId)
                .get()
                .addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {

                    @Override
                    public void onComplete(@NonNull Task<QuerySnapshot> task) {
                        try{
                        if (task.isSuccessful()) {

                            saveToken(userId);

                            for (QueryDocumentSnapshot document : task.getResult()) {
                                if (document.getId().equalsIgnoreCase(userId)) {

                                    Intent startmainact = new Intent(EnterOTPActivity.this, MainActivity.class);
                                    startmainact.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
                                    startActivity(startmainact);
                                    finish();
                                    return;
                                }
                                //Log.d(TAG, document.getId() + " => " + document.getData());
                            }

                            Intent startposact = new Intent(EnterOTPActivity.this, ParOrStudActivity.class);

                            startposact.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
                            startActivity(startposact);
                            finish();

                        } else {
                            View parentLayout = findViewById(android.R.id.content);
                            Snackbar.make(parentLayout, "Database error: please try again", Snackbar.LENGTH_SHORT)
                                    .setAction("OKAY", new View.OnClickListener() {
                                        @Override
                                        public void onClick(View view) {

                                        }
                                    })
                                    .setActionTextColor(getResources().getColor(android.R.color.holo_red_light))
                                    .show();
                        }
                    }
                        catch(Exception e){
                            View parentLayout = findViewById(android.R.id.content);
                            Snackbar.make(parentLayout, "Database error: please try again", Snackbar.LENGTH_SHORT)
                                    .setAction("OKAY", new View.OnClickListener() {
                                        @Override
                                        public void onClick(View view) {

                                        }
                                    })
                                    .setActionTextColor(getResources().getColor(android.R.color.holo_red_light))
                                    .show();
                        }
                    }

                });
    }

    private void saveToken(final String userId) {
        FirebaseInstanceId.getInstance().getInstanceId()
                .addOnCompleteListener(new OnCompleteListener<InstanceIdResult>() {
                    @Override
                    public void onComplete(@NonNull Task<InstanceIdResult> task) {
                        if (!task.isSuccessful()) {
                            return;
                        }

                        // Get new Instance ID token
                        String token = task.getResult().getToken();
                        FirebaseFirestore.getInstance().collection("users").document(userId).update("token",token);
                    }
                });
    }


    @Override
    protected void onResume() {
        super.onResume();
        if (autoResendCount<2 && resumeCount>0) {
            sendVerificationCode(userPhoneNumber);
            autoResendCount=autoResendCount+1;
            resumeCount=resumeCount+1;
        }
    }


    @Override
    protected void onStop() {
        super.onStop();
        progressDialog.dismiss();
    }

    public void startResendTimer(int seconds) {
        timerTV.setVisibility(View.VISIBLE);
        resendOtp.setEnabled(false);

        new CountDownTimer(seconds*1000, 1000) {

            public void onTick(long millisUntilFinished) {
                String secondsString = Long.toString(millisUntilFinished/1000);
                if (millisUntilFinished<10000) {
                    secondsString = "0"+secondsString;
                }
                timerTV.setText(" (0:"+ secondsString+")");
            }

            public void onFinish() {
                resendOtp.setEnabled(true);
                timerTV.setVisibility(View.GONE);
                timerOn=false;
            }
        }.start();
    }

    @Override
    public void onBackPressed() {
        // do not allow user to go back to the screen before
    }
}

Эта система проверки с ошибками c выдает некомпетентный UX. Есть ли способ обойти это?

Ответы [ 3 ]

5 голосов
/ 19 февраля 2020

Если вы прочитали Документы Firebase о PhoneAuthProvider , то будет четко указано:

Метод verifyPhoneNumber является реентерабельным: если вы вызываете его несколько раз, например, в методе onStart действия, метод verifyPhoneNumber не будет отправлять второе SMS, пока не истечет время ожидания исходного запроса.

В вашем случае вы устанавливаете время ожидания на 120 секунд . Если вы действительно хотите проверить это поведение, я предлагаю сократить время ожидания до 10 или 20 секунд и посмотреть, можно ли повторно отправить OTP после истечения времени ожидания.

Кроме того, если вы хотите получить событие времени ожидания, вы можете добавьте onCodeAutoRetrievalTimeOut как часть PhoneAuthProvider.OnVerificationStateChangedCallbacks. Я думаю, что безопасно установить resendOtp в качестве true в этом обратном вызове. И это намного лучше, чем определение собственного таймера.

2 голосов
/ 22 февраля 2020
@Override
public void onVerificationCompleted(PhoneAuthCredential credential) {
    // This callback will be invoked in two situations:
    // 1 - Instant verification. In some cases the phone number can be instantly
    //     verified without needing to send or enter a verification code.
    // 2 - Auto-retrieval. On some devices Google Play services can automatically
    //     detect the incoming verification SMS and perform verification without
    //     user action.
    Log.d(TAG, "onVerificationCompleted:" + credential);

    String code = phoneAuthCredential.getSmsCode();

        Log.d("REGISTER_USER", "code generated first " + code);
        if (code != null) {
            //verifying the code like in normal flow
            verifyUserAuthCode(code);
        } else {
           //you dont get any code, it is instant verification
            showProgressBar(WAIT_TITLE, VERIFYING_MESSAGE);
            signInWithPhoneAuthCredential(phoneAuthCredential);
        }
}
    private void signInWithPhoneAuthCredential(PhoneAuthCredential credential) {
          auth.signInWithCredential(credential)
            .addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
                @Override
                public void onComplete(@NonNull Task<AuthResult> task) {
                    final FirebaseUser user = auth.getCurrentUser();

                    if (task.isSuccessful()) {//user verified}

            });
}

Внимательно читайте комментарии выше в официальных документах Google, firebase использует Мгновенную проверку без отправки номера телефона, для этого вам просто нужно проверить объект «учетные данные», писать логи, вы обнаружите, что этот метод будет выполняться при каждой отправке запрос кода подтверждения

1 голос
/ 19 февраля 2020

Вы просто пытаетесь выйти из приложения или можете сделать еще одну вещь - очистить данные приложения или переустановить.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...