اذهب إلى المحتوى

السؤال

نشر (معدل)

السلام عليكم ورحمة الله وبركاته 

اسعد الله اوقاتكم جميعا يا رب

لدي استفسار لو تكرمتو 

قمت بتجربة خدمة فايربيس لتحقق من رقم الهاتف . في بداية تفعيلي للخدمه كان كل شي ولكن مع التجارب وطلب الرمز لكذا مره حدث  انقطاع في استقبل الرمز لم اعد قادر على استقبل رمز التحقق

طبعا قلت ممكن يحتاج وقت وتوقفت اكثر من 5 ساعات ولكن بعد التجربة لا تزال المشكلة مستمرة.

وقد شاهدت ايضا بانني لو قمت باضافة رقم هاتفي في نظام هواتف الاختبار بعد حذف الرقم لا يعود قادر على استقبل رسائل التحقق ايضا لا اعلم ما هو السبب

هل يوجد حل لهذا المشكلة ؟

 

هذا هو الكود الذي استخدمه :

public class MainActivitytt extends AppCompatActivity {


    private Spinner spinner;
    private EditText editText;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        spinner = findViewById(R.id.spinnerCountries);
        spinner.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_spinner_dropdown_item, CountryData.countryNames));

        editText = findViewById(R.id.editTextPhone);

        findViewById(R.id.buttonContinue).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String code = CountryData.countryAreaCodes[spinner.getSelectedItemPosition()];

                String number = editText.getText().toString().trim();

                if (number.isEmpty() || number.length() < 6) {
                    editText.setError("Valid number is required");
                    editText.requestFocus();
                    return;
                }

                String phoneNumber = "+" + code + number;

                Intent intent = new Intent(MainActivitytt.this, VerifyPhoneActivity.class);
                intent.putExtra("phonenumber", phoneNumber);
                startActivity(intent);

            }
        });
    }

    @Override
    protected void onStart() {
        super.onStart();

        if (FirebaseAuth.getInstance().getCurrentUser() != null) {
            Intent intent = new Intent(this, ProfileActivitytt.class);
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);

            startActivity(intent);
        }
    }
}
public class VerifyPhoneActivity extends AppCompatActivity {


    private String verificationId;
    private FirebaseAuth mAuth;
    private ProgressBar progressBar;
    private EditText editText;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_verify_phone);

        mAuth = FirebaseAuth.getInstance();

        progressBar = findViewById(R.id.progressbar);
        editText = findViewById(R.id.editTextCode);

        String phonenumber = getIntent().getStringExtra("phonenumber");
        sendVerificationCode(phonenumber);

        findViewById(R.id.buttonSignIn).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                String code = editText.getText().toString().trim();

                if (code.isEmpty() || code.length() < 6) {

                    editText.setError("Enter code...");
                    editText.requestFocus();
                    return;
                }
                verifyCode(code);
            }
        });

    }

    private void verifyCode(String code) {
        PhoneAuthCredential credential = PhoneAuthProvider.getCredential(verificationId, code);
        signInWithCredential(credential);
    }

    private void signInWithCredential(PhoneAuthCredential credential) {
        mAuth.signInWithCredential(credential)
                .addOnCompleteListener(new OnCompleteListener<AuthResult>() {
                    @Override
                    public void onComplete(@NonNull Task<AuthResult> task) {
                        if (task.isSuccessful()) {

                            Intent intent = new Intent(VerifyPhoneActivity.this, ProfileActivitytt.class);
                            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);

                            startActivity(intent);

                        } else {
                            Toast.makeText(VerifyPhoneActivity.this, task.getException().getMessage(), Toast.LENGTH_LONG).show();
                        }
                    }
                });
    }

    private void sendVerificationCode(String number) {
        progressBar.setVisibility(View.VISIBLE);
        PhoneAuthProvider.getInstance().verifyPhoneNumber(
                number,
                60,
                TimeUnit.SECONDS,
                TaskExecutors.MAIN_THREAD,
                mCallBack
        );

    }

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

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

        @Override
        public void onVerificationCompleted(PhoneAuthCredential phoneAuthCredential) {
            String code = phoneAuthCredential.getSmsCode();
            if (code != null) {
                editText.setText(code);
                verifyCode(code);
            }
        }

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

 

قمت بتجربة لحل  المشكلة باستخدام السطر التالي ولكن لم ينجح الامر 

 FirebaseAuth.getInstance().signOut();

 

تم التعديل في بواسطة مروان مروان3

Recommended Posts

  • 1
نشر

مرحباً أخي @مروان مروان3

هذه ليست مشكلة في إرسال الكود لكن ما يحدث هو إذا قرأت الdocs الخاص بالخدمة يوضحون بأن الميثود verifyPhoneNumber إذا قمت بإستعائها عدة مرات مثلا عند إستجعائها من الميثود onStart أو أي ميثود تعمل عند بدء تشغيل التطبيق فإن الميثود verifyPhoneNumber لن ترسل رسالة تأكيد إلا بعد إنتهاء مهلة الطلب  ومهلة الطلب أنت محددها 60 وهذا كثير لذلك جرب تغييرها إلى ١٠ هنا 

private void sendVerificationCode(String number) {
        progressBar.setVisibility(View.VISIBLE);
        PhoneAuthProvider.getInstance().verifyPhoneNumber(
                number,
                10,
                TimeUnit.SECONDS,
                TaskExecutors.MAIN_THREAD,
                mCallBack
        );

    }

 

  • 1
نشر
بتاريخ 1 دقيقة مضت قال مروان مروان3:

60 ثانية او انا غلطان ؟

 

صحيح 

بتاريخ 1 دقيقة مضت قال مروان مروان3:

لني قمت بالانتظار لكثر من 4 ساعات تقريبا والمشكلة مستمره فقط حينما اعيد تشغيل الجهاز استطيع استقبل الرمز لمرة واحده ولو حاولت من جديد لن ياتي حتى اعيد التشغيل مره اخره هذا ما وجدته بعد التجربه

يوجد لديك خطأ هنا عدل الميثود 

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

قم بعمل متغير بإسم mToken وعدل الميثود إلى 

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

ثم نقوم بإستخدام الميثود التالية لإجبار إعادة إرسال الرمز مرة أخرى 

private void resendVerificationCode(String phoneNumber,
                                    PhoneAuthProvider.ForceResendingToken token) {
    PhoneAuthProvider.getInstance().verifyPhoneNumber(
            phoneNumber,        
            60,                 
            TimeUnit.SECONDS,  
            this,               
            mCallback,         
            token);             
}

ثم قم بعمل زر مثلا ,وإستدعي الميثود عند ضغط الزر لإجبار إعادة إرسال الرمز نستعديها هكذا مع تمرير الرقم ومتغير mToken إليها 

resendVerificationCode(phonenumber,mToken);

 

  • 1
نشر
بتاريخ 4 دقائق مضت قال مروان مروان3:

ايضا كود اعادة ارسال الرمز الان بعد الضغط عليه يعود بي الى صفحة تسجيل الرقم مع تفاصيلي التي ادخلتها سابقا مثل الدوله + رقم الهاتف ولما اعمل ارسل الرمز ايضا لا يتم الارسال.

 

هنا قم بتعديل المتغير إلى نوع String

private String mToken;

وإستدل TaskExecutors.MAIN_THREAD بthis  الميثود التالية 

 private void sendVerificationCode(String number) {
        progressBar.setVisibility(View.VISIBLE);
        PhoneAuthProvider.getInstance().verifyPhoneNumber(
                number,
                10,
                TimeUnit.SECONDS,
                this,
                mCallBack
        );

    }

وبالنسبة  للخطأ هذا 

بتاريخ 8 دقائق مضت قال مروان مروان3:

ايضا كود اعادة ارسال الرمز الان بعد الضغط عليه يعود بي الى صفحة تسجيل الرقم مع تفاصيلي التي ادخلتها سابقا مثل الدوله + رقم الهاتف ولما اعمل ارسل الرمز ايضا لا يتم الارسال.

 

فهو ببساطة لأنه يحتاج إلى تسجل الدخول أولاً قبل الحصول على التوكن ,لذلك يجب أن تقوم بإستخدام تسجيل الدخول مع جوجل ثم الحصول على المستخدم وإستخدامه في الميثود signInWithCredential تفضل متوفر هنا الكود الرسمي من جوجل لهذه العملية هنا ,بعد تطبيق ذلك المفترض أن يعمل بدون مشاكل وبالنسبة لإعادة تشغيل الجهاز فهو لأن الجلسة تنتهي عند إغلاق التطبيق لكن بعد تطبيق المذكور ستحل المشكلة إن شاء الله  

  • 1
نشر
بتاريخ 12 دقائق مضت قال مروان مروان3:

فظهرت لدي المشكله التاليه :

 

اعتذر قم بإضافة متغير من نوع String بإسم mText وأعد إرجاع المتغير mToken إلى اوبجيك PhoneAuthProvider.ForceResendingToken

وقم بإسناد editText.getText().toString() إليه ثم  إستخدمه في الميثود في ال onClicklistener الخاص بال edittext  

mText = editText.getText().toString();
resendVerificationCode(mText,mToken);

 

  • 1
نشر
بتاريخ 12 دقائق مضت قال مروان مروان3:

اذا انا فاهم عدل بيكون شكل الكود كالتالي كامل:



 

تقريباً نسيت إرجاع المتغير mToken إلى قبل أي هكذا 

بتاريخ 24 دقائق مضت قال Mohamd Imran:

اعتذر قم بإضافة متغير من نوع String بإسم mText وأعد إرجاع المتغير mToken إلى اوبجيك PhoneAuthProvider.ForceResendingToken

 

أي هكذا 

    private PhoneAuthProvider.ForceResendingToken mToken;

 

  • 1
نشر

طيب شفت المشكلة، إذا تم إستخدام الرمز وتم التحقيق مع تجربتي لم يرسل رمز جديد أيضاً وهاذا عشان لا داعي، هاذا الطريق توفر sms و جميل. 

عندنا ثلاثة سيناريوهات هنا: 

١. onCodeSent هنا إذا تم إرسال رمز روح لإشاشة التحقيق مع intent

٢. onVerificationCompleted هنا إذا تم التحقيق من قبل و خرج المستخدم ب singout ولم يمر مدة كثيرة والبرنامج في نفس الهاتف، لا داعي لإرسال رمز جديد. هنا firebase تتعامل مع هاذا وعليك أن تعمل signin with credentials فقت ثم intent الذي تريد بعده

٣.onVerificationFailed هنا تعامل بالمشاكل بطريقتك الخاصة.

 

رابط للمعلومات https://firebase.google.com/docs/auth/android/phone-auth

  • 1
نشر

مش متاكد إذا كان النضام من عندهم للتوفير sms ولاكن يوفر.

نعم أذا تقوم بالتجارب فقط، عادي! ولاكن للمستخدم الحقيقي أذا تعرض لاهاذا السيناريو, إذا خرج وحاول أن يدخل مرة أخرى، عشان لا ينتصر لساعات ولا يستطيع أن يدخل البرنامج تعامل مع هاذا السيناريو. 

طيب السلام عليكم ورحمه الله وبركاته

  • 0
نشر

لقد وجدت حل للمشكله ولكن هذا حل مواقت لا يمكن تطبيقه لدى المستخدمون فقط لدي انا 

في حالة قمت باعادة تشغيل الهاتف استطيع استقبل رمز التحقق من جديد 

كيف يمكنني حل هذا المشكله بدون الحاجه الى اعادة تشغيل للهاتف؟ من غير المنطقي ان اطلب من المستخدم اعادة تشغيل هاتفه في كل عملية تحقق غير نجاحه

 

  • 0
نشر
بتاريخ 1 دقيقة مضت قال Mohamd Imran:

مرحباً أخي @مروان مروان3

هذه ليست مشكلة في إرسال الكود لكن ما يحدث هو إذا قرأت الdocs الخاص بالخدمة يوضحون بأن الميثود verifyPhoneNumber إذا قمت بإستعادئها عدة مرات مثلا عند إستجعائها من الميثود onStart أو أي ميثود تعمل عند بدء تشغيل التطبيق فإن الميثود verifyPhoneNumber لن ترسل رسالة تأكيد إلا بعد إنتهاء مهلة الطلب  ومهلة الطلب أنت محددها 60 وهذا كثير لذلك جرب تغييرها إلى ١٠ هنا 


private void sendVerificationCode(String number) {
        progressBar.setVisibility(View.VISIBLE);
        PhoneAuthProvider.getInstance().verifyPhoneNumber(
                number,
                10,
                TimeUnit.SECONDS,
                TaskExecutors.MAIN_THREAD,
                mCallBack
        );

    }

 

هلا بيك عزيزي

60 ثانية او انا غلطان ؟

لني قمت بالانتظار لكثر من 4 ساعات تقريبا والمشكلة مستمره فقط حينما اعيد تشغيل الجهاز استطيع استقبل الرمز لمرة واحده ولو حاولت من جديد لن ياتي حتى اعيد التشغيل مره اخره هذا ما وجدته بعد التجربه

  • 0
نشر
بتاريخ 16 دقائق مضت قال Mohamd Imran:

صحيح 

يوجد لديك خطأ هنا عدل الميثود 


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

قم بعمل متغير بإسم mToken وعدل الميثود إلى 


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

ثم نقوم بإستخدام الميثود التالية لإجبار إعادة إرسال الرمز مرة أخرى 


private void resendVerificationCode(String phoneNumber,
                                    PhoneAuthProvider.ForceResendingToken token) {
    PhoneAuthProvider.getInstance().verifyPhoneNumber(
            phoneNumber,        
            60,                 
            TimeUnit.SECONDS,  
            this,               
            mCallback,         
            token);             
}

ثم قم بعمل زر مثلا ,وإستدعي الميثود عند ضغط الزر لإجبار إعادة إرسال الرمز نستعديها هكذا مع تمرير الرقم ومتغير mToken إليها 


resendVerificationCode(phonenumber,mToken);

 

 

 

 

 

هلا بيك عزيزي @Mohamd Imran

قمت بعمل الكود كامل مع تعديلك كالتالي :

public class VerifyPhoneActivity extends AppCompatActivity {
    private static final String TAG = "VerifyPhoneActivity";

    private PhoneAuthProvider.ForceResendingToken mResendToken;
    private PhoneAuthProvider.OnVerificationStateChangedCallbacks mCallbacks;

    private String verificationId;
    private FirebaseAuth mAuth;
    private ProgressBar progressBar;
    private EditText editText;
    private PhoneAuthProvider.ForceResendingToken mToken;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_verify_phone);

        mAuth = FirebaseAuth.getInstance();

        progressBar = findViewById(R.id.progressbar);
        editText = findViewById(R.id.editTextCode);
        findViewById(R.id.tt).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
              //  mAuth.getInstance().signOut();
                resendVerificationCode(editText.getText().toString(),mToken);
            }
        });

        String phonenumber = getIntent().getStringExtra("phonenumber");
        sendVerificationCode(phonenumber);

        findViewById(R.id.buttonSignIn).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                String code = editText.getText().toString().trim();

                if (code.isEmpty() || code.length() < 6) {

                    editText.setError("Enter code...");
                    editText.requestFocus();
                    return;
                }
                verifyCode(code);
            }
        });

    }

    private void verifyCode(String code) {
        PhoneAuthCredential credential = PhoneAuthProvider.getCredential(verificationId, code);
        signInWithCredential(credential);
    }

    private void signInWithCredential(PhoneAuthCredential credential) {
        mAuth.signInWithCredential(credential)
                .addOnCompleteListener(new OnCompleteListener<AuthResult>() {
                    @Override
                    public void onComplete(@NonNull Task<AuthResult> task) {
                        if (task.isSuccessful()) {

                            Intent intent = new Intent(VerifyPhoneActivity.this, ProfileActivitytt.class);
                            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);

                            startActivity(intent);

                        } else {
                            Toast.makeText(VerifyPhoneActivity.this, task.getException().getMessage(), Toast.LENGTH_LONG).show();
                        }
                    }
                });
    }

    private void sendVerificationCode(String number) {
        progressBar.setVisibility(View.VISIBLE);
        PhoneAuthProvider.getInstance().verifyPhoneNumber(
                number,
                10,
                TimeUnit.SECONDS,
                TaskExecutors.MAIN_THREAD,
                mCallBack
        );

    }

    private PhoneAuthProvider.OnVerificationStateChangedCallbacks
            mCallBack = new PhoneAuthProvider.OnVerificationStateChangedCallbacks() {
/*
        @Override
        public void onCodeSent(String s, PhoneAuthProvider.ForceResendingToken forceResendingToken) {
            super.onCodeSent(s, forceResendingToken);
            verificationId = s;
        }

 */
        @Override
        public void onCodeSent(String s, PhoneAuthProvider.ForceResendingToken forceResendingToken) {
            super.onCodeSent(s, forceResendingToken);
            verificationId = s;
            mToken = forceResendingToken;
        }
        @Override
        public void onVerificationCompleted(PhoneAuthCredential phoneAuthCredential) {
            String code = phoneAuthCredential.getSmsCode();
            if (code != null) {
                editText.setText(code);
                verifyCode(code);
            }
        }

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

    private void resendVerificationCode(String phoneNumber,
                                        PhoneAuthProvider.ForceResendingToken token) {
        PhoneAuthProvider.getInstance().verifyPhoneNumber(
                phoneNumber,
                10,
                TimeUnit.SECONDS,
                this,
                mCallBack,
                token);
    }


}

 

ولكن المشكلة لا تزال مستمره في حالة المرة الاولى للمستخدم يستطيع استقبل الكود ولكن لو عمل للمره الثانية لا يستطيع ذلك لا يستقبل الكود الا اذا قمت باعادة تشغيل هاتفي 

مع العلم انني حتى من جدول المستخدمون قمت بحذفه ولكن لم تحل المشكلة 

ايضا كود اعادة ارسال الرمز الان بعد الضغط عليه يعود بي الى صفحة تسجيل الرقم مع تفاصيلي التي ادخلتها سابقا مثل الدوله + رقم الهاتف ولما اعمل ارسل الرمز ايضا لا يتم الارسال.

 

لا اعلم ما هيا العلاقه بين اعادة تشغيل الهاتف واستقبل الرمز بهذا الطريقة

 

 

ارفقت ملف المشروع كامل ليكون اوضح

MyApplication3.rar

  • 0
نشر
بتاريخ 3 دقائق مضت قال Mohamd Imran:

هنا قم بتعديل المتغير إلى نوع String


private String mToken;

وإستدل TaskExecutors.MAIN_THREAD بthis  الميثود التالية 


 private void sendVerificationCode(String number) {
        progressBar.setVisibility(View.VISIBLE);
        PhoneAuthProvider.getInstance().verifyPhoneNumber(
                number,
                10,
                TimeUnit.SECONDS,
                this,
                mCallBack
        );

    }

وبالنسبة  للخطأ هذا 

فهو ببساطة لأنه يحتاج إلى تسجل الدخول أولاً قبل الحصول على التوكن ,لذلك يجب أن تقوم بإستخدام تسجيل الدخول مع جوجل ثم الحصول على المستخدم وإستخدامه في الميثود signInWithCredential تفضل متوفر هنا الكود الرسمي من جوجل لهذه العملية هنا ,بعد تطبيق ذلك المفترض أن يعمل بدون مشاكل وبالنسبة لإعادة تشغيل الجهاز فهو لأن الجلسة تنتهي عند إغلاق التطبيق لكن بعد تطبيق المذكور ستحل المشكلة إن شاء الله  

قمت بتعديل التالي:

private String mToken;

فظهرت لدي المشكله التاليه :

problem1.thumb.png.df4be0332db2343bd8b58a211aced28c.png

'resendVerificationCode(java.lang.String, com.google.firebase.auth.PhoneAuthProvider.ForceResendingToken)' in 'com.example.myapplication.VerifyPhoneActivity' cannot be applied to '(java.lang.String, java.lang.String)'

problem2.thumb.png.10d4fc39a72d6881d20ca55199b799c3.png

Incompatible types. Found: 'com.google.firebase.auth.PhoneAuthProvider.ForceResendingToken', required: 'java.lang.String'

 

 

 

  • 0
نشر
بتاريخ 9 دقائق مضت قال Mohamd Imran:

اعتذر قم بإضافة متغير من نوع String بإسم mText وأعد إرجاع المتغير mToken إلى اوبجيك PhoneAuthProvider.ForceResendingToken

وقم بإسناد editText.getText().toString() إليه ثم  إستخدمه في الميثود في ال onClicklistener الخاص بال edittext  


mText = editText.getText().toString();

resendVerificationCode(mText,mToken);

 

اذا انا فاهم عدل بيكون شكل الكود كالتالي كامل:

public class VerifyPhoneActivity extends AppCompatActivity {
    private static final String TAG = "VerifyPhoneActivity";

    private PhoneAuthProvider.ForceResendingToken mResendToken;
    private PhoneAuthProvider.OnVerificationStateChangedCallbacks mCallbacks;

    private String verificationId;
    private FirebaseAuth mAuth;
    private ProgressBar progressBar;
    private EditText editText;
    private String mToken;
   String mText;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_verify_phone);

        mAuth = FirebaseAuth.getInstance();

        progressBar = findViewById(R.id.progressbar);
        editText = findViewById(R.id.editTextCode);
        findViewById(R.id.tt).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
              //  mAuth.getInstance().signOut();
                resendVerificationCode(mText,mToken);
             //   resendVerificationCode(editText.getText().toString(),mToken);
            }
        });

        String phonenumber = getIntent().getStringExtra("phonenumber");
        sendVerificationCode(phonenumber);

        findViewById(R.id.buttonSignIn).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mText = editText.getText().toString();
                String code = editText.getText().toString().trim();

                if (code.isEmpty() || code.length() < 6) {

                    editText.setError("Enter code...");
                    editText.requestFocus();
                    return;
                }
                verifyCode(code);
            }
        });

    }

    private void verifyCode(String code) {
        PhoneAuthCredential credential = PhoneAuthProvider.getCredential(verificationId, code);
        signInWithCredential(credential);
    }

    private void signInWithCredential(PhoneAuthCredential credential) {
        mAuth.signInWithCredential(credential)
                .addOnCompleteListener(new OnCompleteListener<AuthResult>() {
                    @Override
                    public void onComplete(@NonNull Task<AuthResult> task) {
                        if (task.isSuccessful()) {

                            Intent intent = new Intent(VerifyPhoneActivity.this, ProfileActivitytt.class);
                            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);

                            startActivity(intent);

                        } else {
                            Toast.makeText(VerifyPhoneActivity.this, task.getException().getMessage(), Toast.LENGTH_LONG).show();
                        }
                    }
                });
    }

    private void sendVerificationCode(String number) {
        progressBar.setVisibility(View.VISIBLE);
        PhoneAuthProvider.getInstance().verifyPhoneNumber(
                number,
                10,
                TimeUnit.SECONDS,
                this,
                mCallBack
        );

    }

    private PhoneAuthProvider.OnVerificationStateChangedCallbacks
            mCallBack = new PhoneAuthProvider.OnVerificationStateChangedCallbacks() {
/*
        @Override
        public void onCodeSent(String s, PhoneAuthProvider.ForceResendingToken forceResendingToken) {
            super.onCodeSent(s, forceResendingToken);
            verificationId = s;
        }

 */
        @Override
        public void onCodeSent(String s, PhoneAuthProvider.ForceResendingToken forceResendingToken ) {
            super.onCodeSent(s, forceResendingToken);
            verificationId = s;
           // mToken = forceResendingToken;
            mText = editText.getText().toString().trim();
        }
        @Override
        public void onVerificationCompleted(PhoneAuthCredential phoneAuthCredential) {
            String code = phoneAuthCredential.getSmsCode();
            if (code != null) {
                editText.setText(code);
                verifyCode(code);
            }
        }

        @Override
        public void onVerificationFailed(FirebaseException e) {

          Toast.makeText(VerifyPhoneActivity.this, e.getMessage(), Toast.LENGTH_LONG).show();
        }
    };

    private void resendVerificationCode(String phoneNumber,
                                        PhoneAuthProvider.ForceResendingToken token) {
        PhoneAuthProvider.getInstance().verifyPhoneNumber(
                phoneNumber,
                10,
                TimeUnit.SECONDS,
                this,
                mCallBack,
                token);
    }


}

 

 

problem3.thumb.png.885fa8244919ef3c5a2b1f9f011faadd.png

  • 0
نشر

هلا بيك عزيزي اعتذر ادري تعبتك معي كثير السموحه

نفس المشكله مثل ما قمنا بعمل الكود في اول مره لا يتم استقبل الرمز للمره الثانية حتى انني قمت بتجربة رقم هاتف مختلف والمشكله نفسها مستمره 

  • 0
نشر

يبدو ان الامر يحصل فقط في حالة ان المستخدم قد تحقق من رمز OTP لقد قمت بتجربة التاليه

قمت بطلب OTP ولكن لم اعمل تحقق وقد تم ارساله لي وقمت بنفس الخطوه مره ثانيه وثالثه والامر ناجح اذن المشكلة فقط لو المستخدم عمل تحقق فلن يستطيع طلبه من جديد قبل اعادة التشغيل

على هذا النحو لا بأس بالامر شكرا لك عزيزي كثير يمشي الحال في الوقت الحالي لا مشكلة

  • 0
نشر (معدل)
بتاريخ 49 دقائق مضت قال عبدالرشيد احمد:

السلام عليكم ورحمه الله وبركاته، 

حبيبي هل مشكلتك عندما يجيك الرمز وإستخدمته، ثم خرجت من البرنامج وحاولت أن تدخل مرة أخرى لم يصلك رمز جديد؟

هلا بيك عزيزي

نعم وانا احاول برمجة المشروع كنت اجرب وواجهتني هذا المشكله بضبط مثل ما ذكرت انت

 

اما لو وصلني الرمز ولم استخدمه لا توجد اي مشكله بامكاني ان اطلب الرمز اكثر من 4 مرات خلف بعض بدون اي مشاكل 

 

هيا تحدث فقط في حالة استخدامي لرمز لا استطيع بعدها طلب رمز جديد 

تم التعديل في بواسطة مروان مروان3
  • 0
نشر
بتاريخ 3 دقائق مضت قال عبدالرشيد احمد:

طيب شفت المشكلة، إذا تم إستخدام الرمز وتم التحقيق مع تجربتي لم يرسل رمز جديد أيضاً وهاذا عشان لا داعي، هاذا الطريق توفر sms و جميل. 

عندنا ثلاثة سيناريوهات هنا: 

١. onCodeSent هنا إذا تم إرسال رمز روح لإشاشة التحقيق مع intent

٢. onVerificationCompleted هنا إذا تم التحقيق من قبل و خرج المستخدم ب singout ولم يمر مدة كثيرة والبرنامج في نفس الهاتف، لا داعي لإرسال رمز جديد. هنا firebase تتعامل مع هاذا وعليك أن تعمل signin with credentials فقت ثم intent الذي تريد بعده

٣.onVerificationFailed هنا تعامل بالمشاكل بطريقتك الخاصة.

 

رابط للمعلومات https://firebase.google.com/docs/auth/android/phone-auth

اذن كل المشكلة هيا ان نظام firebase يعمل كذا من اجل توفير sms او الباقه ..

لا باس اهم نقطة الرمز يصل ويتكرار وصوله في حالة المستخدم لم يعمل تاكيد على الرمز لقد تركت الامر بهذا الطريقة لا باس بذلك

 

كل الشكر لك ي غالي على الافاده وتوضيح ربي يعطيك الف عافيه

  • 0
نشر
بتاريخ 9 دقائق مضت قال عبدالرشيد احمد:

مش متاكد إذا كان النضام من عندهم للتوفير sms ولاكن يوفر.

نعم أذا تقوم بالتجارب فقط، عادي! ولاكن للمستخدم الحقيقي أذا تعرض لاهاذا السيناريو, إذا خرج وحاول أن يدخل مرة أخرى، عشان لا ينتصر لساعات ولا يستطيع أن يدخل البرنامج تعامل مع هاذا السيناريو. 

طيب السلام عليكم ورحمه الله وبركاته

هل فيه حل ممكن نعمله طيب يحل هذا المشكله؟

  • 0
نشر
بتاريخ 48 دقائق مضت قال عبدالرشيد احمد:

نعم، بعد إدخال رقم ألهاتف،  إذا رسل رمز روح لإشاشة التحقيق أو إذا تم التحقيق من قبل تخطى شاشة التحقيق.

حدقق كودك وأشوف لك حل إلا بكرة أو بعدها إنشاء الله.

طيب عزيزي

كل الشكر لك وربي يجزيك الف عافيه

  • 0
نشر

عدلت الكود. جرب هاذا. اذا تريد ان يقرا الكود من الsms اوتوماتك، عدل زيادا. الحين يجب ان تدخل الكود من الsms بالكيبورد.

 

MainActivitytt.java

public class MainActivitytt extends AppCompatActivity {


    private Spinner spinner;
    private EditText editText;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        spinner = findViewById(R.id.spinnerCountries);
        spinner.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_spinner_dropdown_item, CountryData.countryNames));

        editText = findViewById(R.id.editTextPhone);

        findViewById(R.id.buttonContinue).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String code = CountryData.countryAreaCodes[spinner.getSelectedItemPosition()];

                String number = editText.getText().toString().trim();

                if (number.isEmpty() || number.length() < 6) {
                    editText.setError("Valid number is required");
                    editText.requestFocus();
                    return;
                }

                String phoneNumber = "+" + code + number;

                PhoneAuthProvider.getInstance().verifyPhoneNumber(
                phoneNumber,
                60,
                TimeUnit.SECONDS,
                TaskExecutors.MAIN_THREAD,
                mCallBack
             );

            }
        });

        mCallBack = new PhoneAuthProvider.OnVerificationStateChangedCallbacks() {

            @Override
            public void onCodeSent(String s, PhoneAuthProvider.ForceResendingToken forceResendingToken) {
                super.onCodeSent(s, forceResendingToken);
                
                new android.os.Handler().postDelayed(
                    new Runnable(){
                        public void run(){

                        }
                    },
                10000);

                Intent intent = new Intent(MainActivitytt.this, VerifyPhoneActivity.class);
                intent.putExtra("AuthCredentials", s);
                startActivity(intent);
                finish();
            }
    
            @Override
            public void onVerificationCompleted(PhoneAuthCredential phoneAuthCredential) {
                signInWithCredential(phoneAuthCredential);
            }
    
            @Override
            public void onVerificationFailed(FirebaseException e) {
                Toast.makeText(VerifyPhoneActivity.this, e.getMessage(), Toast.LENGTH_LONG).show();
            }
        };
    }

    private void signInWithCredential(PhoneAuthCredential credential) {
        mAuth.signInWithCredential(credential)
                .addOnCompleteListener(new OnCompleteListener<AuthResult>() {
                    @Override
                    public void onComplete(@NonNull Task<AuthResult> task) {
                        if (task.isSuccessful()) {

                            Intent intent = new Intent(VerifyPhoneActivity.this, ProfileActivitytt.class);
                            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);

                            startActivity(intent);

                        } else {
                            Toast.makeText(VerifyPhoneActivity.this, task.getException().getMessage(), Toast.LENGTH_LONG).show();
                        }
                    }
                });
    }
    @Override
    protected void onStart() {
        super.onStart();

        if (FirebaseAuth.getInstance().getCurrentUser() != null) {
            Intent intent = new Intent(this, ProfileActivitytt.class);
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);

            startActivity(intent);
        }
    }
}

 

VerifyPhoneActivity.java

public class VerifyPhoneActivity extends AppCompatActivity {


    private String verificationId;
    private FirebaseAuth mAuth;
    private ProgressBar progressBar;
    private EditText editText;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_verify_phone);

        mAuth = FirebaseAuth.getInstance();

        progressBar = findViewById(R.id.progressbar);
        editText = findViewById(R.id.editTextCode);

        verificationId = getIntent().getStringExtra("AuthCredentials");

        findViewById(R.id.buttonSignIn).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                String code = editText.getText().toString().trim();

                if (code.isEmpty() || code.length() < 6) {

                    editText.setError("Enter code...");
                    editText.requestFocus();
                    return;
                }
                verifyCode(code);
            }
        });

    }

    private void verifyCode(String code) {
        PhoneAuthCredential credential = PhoneAuthProvider.getCredential(verificationId, code);
        signInWithCredential(credential);
    }

    private void signInWithCredential(PhoneAuthCredential credential){
        mAuth.signInWithCredential(credential)
                .addOnCompleteListener(VerifyPhoneActivity.this, new OnCompleteListener<AuthResult>(){
                    public void onComplete(@NonNull Task<AuthResult> task){
                        if(task.isSuccessful()){
                        } else {
                            if(task.getException() instanceof FirebaseAuthInvalidCredentialsException){
                                
                            }
                        }
                    }
                });
        }

}

 

  • 0
نشر (معدل)

اسف، خطا صغير في اجابة الاولة جرب هاذا. اذا تريد ان يقرا الكود من الsms اوتوماتك، عدل زيادا. الحين يجب ان تدخل الكود من الsms بالكيبورد.

 

MainActivitytt.java

public class MainActivitytt extends AppCompatActivity {


    private Spinner spinner;
    private EditText editText;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        spinner = findViewById(R.id.spinnerCountries);
        spinner.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_spinner_dropdown_item, CountryData.countryNames));

        editText = findViewById(R.id.editTextPhone);

        findViewById(R.id.buttonContinue).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String code = CountryData.countryAreaCodes[spinner.getSelectedItemPosition()];

                String number = editText.getText().toString().trim();

                if (number.isEmpty() || number.length() < 6) {
                    editText.setError("Valid number is required");
                    editText.requestFocus();
                    return;
                }

                String phoneNumber = "+" + code + number;

                PhoneAuthProvider.getInstance().verifyPhoneNumber(
                phoneNumber,
                60,
                TimeUnit.SECONDS,
                TaskExecutors.MAIN_THREAD,
                mCallBack
             );

            }
        });

        mCallBack = new PhoneAuthProvider.OnVerificationStateChangedCallbacks() {

            @Override
            public void onCodeSent(String s, PhoneAuthProvider.ForceResendingToken forceResendingToken) {
                super.onCodeSent(s, forceResendingToken);
                
                new android.os.Handler().postDelayed(
                    new Runnable(){
                        public void run(){

                        }
                    },
                10000);

                Intent intent = new Intent(MainActivitytt.this, VerifyPhoneActivity.class);
                intent.putExtra("AuthCredentials", s);
                startActivity(intent);
                finish();
            }
    
            @Override
            public void onVerificationCompleted(PhoneAuthCredential phoneAuthCredential) {
                signInWithCredential(phoneAuthCredential);
            }
    
            @Override
            public void onVerificationFailed(FirebaseException e) {
                Toast.makeText(VerifyPhoneActivity.this, e.getMessage(), Toast.LENGTH_LONG).show();
            }
        };
    }

    private void signInWithCredential(PhoneAuthCredential credential) {
        mAuth.signInWithCredential(credential)
                .addOnCompleteListener(new OnCompleteListener<AuthResult>() {
                    @Override
                    public void onComplete(@NonNull Task<AuthResult> task) {
                        if (task.isSuccessful()) {

                            Intent intent = new Intent(MainActivitytt.this, ProfileActivitytt.class);
                            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);

                            startActivity(intent);

                        } else {
                            Toast.makeText(VerifyPhoneActivity.this, task.getException().getMessage(), Toast.LENGTH_LONG).show();
                        }
                    }
                });
    }
    @Override
    protected void onStart() {
        super.onStart();

        if (FirebaseAuth.getInstance().getCurrentUser() != null) {
            Intent intent = new Intent(this, ProfileActivitytt.class);
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);

            startActivity(intent);
        }
    }
}

 

VerifyPhoneActivity.java

public class VerifyPhoneActivity extends AppCompatActivity {


    private String verificationId;
    private FirebaseAuth mAuth;
    private ProgressBar progressBar;
    private EditText editText;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_verify_phone);

        mAuth = FirebaseAuth.getInstance();

        progressBar = findViewById(R.id.progressbar);
        editText = findViewById(R.id.editTextCode);

        verificationId = getIntent().getStringExtra("AuthCredentials");

        findViewById(R.id.buttonSignIn).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                String code = editText.getText().toString().trim();

                if (code.isEmpty() || code.length() < 6) {

                    editText.setError("Enter code...");
                    editText.requestFocus();
                    return;
                }
                verifyCode(code);
            }
        });

    }

    private void verifyCode(String code) {
        PhoneAuthCredential credential = PhoneAuthProvider.getCredential(verificationId, code);
        signInWithCredential(credential);
    }

    private void signInWithCredential(PhoneAuthCredential credential){
        mAuth.signInWithCredential(credential)
                .addOnCompleteListener(VerifyPhoneActivity.this, new OnCompleteListener<AuthResult>(){
                    public void onComplete(@NonNull Task<AuthResult> task){
                        if(task.isSuccessful()){
                        } else {
                            if(task.getException() instanceof FirebaseAuthInvalidCredentialsException){
                                
                            }
                        }
                    }
                });
        }

}

 

تم التعديل في بواسطة عبدالرشيد احمد
خطا في اجابة الاولة
  • 0
نشر

@عبدالرشيد احمد

السلام عليكم ورحمة الله وبركاته

هلا بيك ي غالي

اعذرني اول شي على تاخر ردي كنت شغال ب كود صفحة مختلفة السموحه منك

جاري تجريب الكود ورح اوافيك ب النتيجة باذن الله بعد دقائق 

@عبدالرشيد احمد

هلا بيك عزيزي

قمت بتطبيق الكود المرفق منك اخوي

اول من اعمل طلب لرمز يصير كراش ل تطبيق وهذا نتيجة Logcat

java.lang.NullPointerException: Attempt to invoke virtual method 'com.google.android.gms.tasks.Task com.google.firebase.auth.FirebaseAuth.signInWithCredential(com.google.firebase.auth.AuthCredential)' on a null object reference
        at com.example.myapplication.MainActivitytt.signInWithCredential(MainActivitytt.java:101)
        at com.example.myapplication.MainActivitytt.access$300(MainActivitytt.java:26)
        at com.example.myapplication.MainActivitytt$2.onVerificationCompleted(MainActivitytt.java:90)
        at com.google.firebase.auth.api.internal.zzer.zza(Unknown Source)
        at com.google.firebase.auth.api.internal.zzeu.run(Unknown Source)
        at android.os.Handler.handleCallback(Handler.java:751)
        at android.os.Handler.dispatchMessage(Handler.java:95)
        at android.os.Looper.loop(Looper.java:154)
        at android.app.ActivityThread.main(ActivityThread.java:6682)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1520)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1410)

مع العلم ان  الرقم يصل او رمز التفعيل استقبله ولكن الكراش موجود

  • 0
نشر
بتاريخ 3 ساعات قال عبدالرشيد احمد:

هل الكراش بعد شاشة ادخال الرقم؟ هل يصل الى شاشة التحقيق او البروفايل؟

بعد الانتقال الى شاشة تاكيد الرقم يحدث لكراش بمعنى في شاشة ادخال الرقم

  • 0
نشر (معدل)

طيب، جرب هاذي الحين:

MainActivitytt.java

public class MainActivitytt extends AppCompatActivity {


    private Spinner spinner;
    private EditText editText;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        spinner = findViewById(R.id.spinnerCountries);
        spinner.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_spinner_dropdown_item, CountryData.countryNames));

        editText = findViewById(R.id.editTextPhone);

        findViewById(R.id.buttonContinue).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String code = CountryData.countryAreaCodes[spinner.getSelectedItemPosition()];

                String number = editText.getText().toString().trim();

                if (number.isEmpty() || number.length() < 6) {
                    editText.setError("Valid number is required");
                    editText.requestFocus();
                    return;
                }

                String phoneNumber = "+" + code + number;

                PhoneAuthProvider.getInstance().verifyPhoneNumber(
                phoneNumber,
                60,
                TimeUnit.SECONDS,
                TaskExecutors.MAIN_THREAD,
                mCallBack
             );

            }
        });

        mCallBack = new PhoneAuthProvider.OnVerificationStateChangedCallbacks() {

            @Override
            public void onCodeSent(String s, PhoneAuthProvider.ForceResendingToken forceResendingToken) {
                super.onCodeSent(s, forceResendingToken);
                
                new android.os.Handler().postDelayed(
                    new Runnable(){
                        public void run(){

                        }
                    },
                10000);

                Intent intent = new Intent(MainActivitytt.this, VerifyPhoneActivity.class);
                intent.putExtra("AuthCredentials", s);
                startActivity(intent);
                finish();
            }
    
            @Override
            public void onVerificationCompleted(PhoneAuthCredential phoneAuthCredential) {
                signInWithCredential(phoneAuthCredential);
            }
    
            @Override
            public void onVerificationFailed(FirebaseException e) {
                Toast.makeText(VerifyPhoneActivity.this, e.getMessage(), Toast.LENGTH_LONG).show();
            }
        };
    }

    private void signInWithCredential(PhoneAuthCredential credential) {
        mAuth.signInWithCredential(credential)
                .addOnCompleteListener(new OnCompleteListener<AuthResult>() {
                    @Override
                    public void onComplete(@NonNull Task<AuthResult> task) {
                        if (task.isSuccessful()) {

                            Intent intent = new Intent(MainActivitytt.this, ProfileActivitytt.class);
                            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);

                            startActivity(intent);

                        } else {
                            Toast.makeText(MainActivitytt.this, task.getException().getMessage(), Toast.LENGTH_LONG).show();
                        }
                    }
                });
    }
    @Override
    protected void onStart() {
        super.onStart();

        if (FirebaseAuth.getInstance().getCurrentUser() != null) {
            Intent intent = new Intent(this, ProfileActivitytt.class);
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
            startActivity(intent);
        }
    }
}

 

VerifyPhoneActivity.java

public class VerifyPhoneActivity extends AppCompatActivity {


    private String verificationId;
    private FirebaseAuth mAuth;
    private ProgressBar progressBar;
    private EditText editText;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_verify_phone);

        mAuth = FirebaseAuth.getInstance();

        progressBar = findViewById(R.id.progressbar);
        editText = findViewById(R.id.editTextCode);

        verificationId = getIntent().getStringExtra("AuthCredentials");

        findViewById(R.id.buttonSignIn).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                String code = editText.getText().toString().trim();

                if (code.isEmpty() || code.length() < 6) {

                    editText.setError("Enter code...");
                    editText.requestFocus();
                    return;
                }
                PhoneAuthCredential credential = PhoneAuthProvider.getCredential(verificationId, code);
                signInWithCredential(credential);
            }
        });

    }


    private void signInWithCredential(PhoneAuthCredential credential){
        mAuth.signInWithCredential(credential)
                .addOnCompleteListener(VerifyPhoneActivity.this, new OnCompleteListener<AuthResult>(){
                    public void onComplete(@NonNull Task<AuthResult> task){
                        if(task.isSuccessful()){
                                     
                        } else {
                            if(task.getException() instanceof FirebaseAuthInvalidCredentialsException){
                                
                            }
                        }
                    }
                });
        }

}

 

تم التعديل في بواسطة عبدالرشيد احمد

انضم إلى النقاش

يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.

زائر
أجب على هذا السؤال...

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   جرى استعادة المحتوى السابق..   امسح المحرر

×   You cannot paste images directly. Upload or insert images from URL.

  • إعلانات

  • تابعنا على



×
×
  • أضف...