12/08/2018, 13:57

Android với xác thực SMS tương tự ứng dụng WhatsApp - Phần 2

Trong phần 1 của bài viết này, chúng ta đã học được làm thế nào để tạo ra các API REST cần thiết cho ứng dụng này. Trong phần này chúng ta sẽ xem làm thế nào để xây dựng ứng dụng Android tương tác với các API để nhận được các tin nhắn SMS và xác nhận các tin nhắn đó. Dự án này sẽ sử dụng thư viện ...

Trong phần 1 của bài viết này, chúng ta đã học được làm thế nào để tạo ra các API REST cần thiết cho ứng dụng này. Trong phần này chúng ta sẽ xem làm thế nào để xây dựng ứng dụng Android tương tác với các API để nhận được các tin nhắn SMS và xác nhận các tin nhắn đó. Dự án này sẽ sử dụng thư viện Volley để thực hiện các HTTP request. Bạn nên học cách sử dụng thư viện ở đây. Ngoài ra bạn cần phải có kiến thức cơ bản về Android Service và broadcast receivers.

Chúng ta sẽ sử dụng việc truyền nhận SMS để đọc trên devices nhận nó, sau đó chúng ta sẽ sử dụng Intent Service để tạo HTTP request gửi OTP tới server để xác nhận.

VIDEO DEMO

Dưới đây là màn hình ứng dụng mà chúng ta sẽ làm ngay bây giờ

screenshot

6. Tạo ứng dụng Android

Ứng dụng này có chứa hai Activity. Một với một ViewPager gồm hai trang. Một trang là để nhập số điện thoại di động và các trang khác là nhập OTP. Activity thứ hai là để hiển thị thông tin profile của người dùng.

1. Trong Android Studio, tạo New Project bằng cách vào File ⇒ New Project sau đó điền vào tất cả các thông tin cần thiết. Khi sang màn hình lựa chọn, bạn nên chon Blank Activity để bắt đầu dự án.

2. Mở build.gradle nằm trong thư mục ứng dụng và thêm thư viện Volley bằng cách thêm com.mcxiaoke.volley:library-aar:1.0.0

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile "com.android.support:appcompat-v7:22.1.1"
    compile 'com.mcxiaoke.volley:library-aar:1.0.0'
}

6.1 Tạo ứng dụng Material

Bước này là tùy chọn, nhưng tôi khuyên bạn nên thực hiện nó để nâng cao khả năng và hiểu biết của bạn về thiết kế Material. Bạn có thể tham khảo các kiến thức về Material ở đây

3. Mở strings.xml nằm trong thư mục res ⇒ values và thêm các giá trị như dưới đây.

strings.xml
<resources>
    <string name="app_name">SMS Verification</string>
    <string name="hello_world">Hello world!</string>
    <string name="action_settings">Settings</string>
    <string name="title_activity_sms">SmsActivity</string>
    <string name="action_logout">Logout</string>
    <string name="msg_enter_mobile">Enter your mobile number to get started!</string>
    <string name="lbl_name">Name</string>
    <string name="lbl_email">Email</string>
    <string name="lbl_mobile">Mobile</string>
    <string name="lbl_next">NEXT</string>
    <string name="msg_sit_back">Sit back & Relax! while we verify your mobile number</string>
    <string name="msg_manual_otp">(Enter the OTP below in case if we fail to detect the SMS automatically)</string>
    <string name="lbl_enter_otp">Enter OTP</string>
    <string name="lbl_submit">SUBMIT</string>
</resources>

4. Mở colors.xml nằm trong thư mục res ⇒ values và thêm các giá trị như dưới đây.

colors.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="colorPrimary">#3b5bb3</color>
    <color name="colorPrimaryDark">#303F9F</color>
    <color name="textColorPrimary">#FFFFFF</color>
    <color name="windowBackground">#FFFFFF</color>
    <color name="navigationBarColor">#000000</color>
    <color name="colorAccent">#ea5d88</color>

    <color name="bg_view_sms">#ffd423</color>
    <color name="bg_view_otp">#fc6d38</color>
</resources>

5. Dưới thư mục res, tạo một thư mục tên là values-v21. Trong thư mục này tạo styles.xml và thêm mã dưới đây.

styles.xml
<resources>

    <style name="MyMaterialTheme" parent="MyMaterialTheme.Base">
        <item name="android:windowContentTransitions">true</item>
        <item name="android:windowAllowEnterTransitionOverlap">true</item>
        <item name="android:windowAllowReturnTransitionOverlap">true</item>
        <item name="android:windowSharedElementEnterTransition">@android:transition/move</item>
        <item name="android:windowSharedElementExitTransition">@android:transition/move</item>
    </style>

</resources>

6. Cuối cùng mở file AndroidManifest.xml và thêm MyMaterialTheme vào thẻ <application>.

AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="info.androidhive.smsverification">

    <application
        android:theme="@style/MyMaterialTheme">
        .
        .
    </application>

</manifest>

Bây giờ nếu bạn chạy ứng dụng, bạn có thể nhìn thấy thanh thông báo thay đổi màu sắc, điều đó có nghĩa là các chủ đề thiết kế vật liệu được áp dụng.

7. Bây giờ bạn tạo 5 package với các tên activity, app, helper, receiver và service. Các package này sẽ làm cho project bạn được rõ ràng hơn.

Dưới đây là cấu trúc của dự án chúng ta:

project_struct

8. Trong package app, tạo một lớp có tên Config.java. Lớp này chứa những thông tin cấu hình quan trọng của dự án.

  • URL_REQUEST_SMS và URL_VERIFY_OTP nên chính xác. Địa chỉ IP nên chính xác là localhost của máy tính bạn.

  • SMS_ORIGIN nên đúng với giá trị được định nghĩa trong file Config.php được tạo ở PHP project phần trước.

  • OTP_DELIMITER nên đúng với giá trị được định nghĩa trong file Config.php được tạo ở PHP project phần trước.

Config.java
package info.androidhive.smsverification.app;

/**
 * Created by Ravi on 08/07/15.
 */
public class Config {
    // server URL configuration
    public static final String URL_REQUEST_SMS = "http://192.168.0.101/android_sms/msg91/request_sms.php";
    public static final String URL_VERIFY_OTP = "http://192.168.0.101/android_sms/msg91/verify_otp.php";

    // SMS provider identification
    // It should match with your SMS gateway origin
    // You can use  MSGIND, TESTER and ALERTS as sender ID
    // If you want custom sender Id, approve MSG91 to get one
    public static final String SMS_ORIGIN = "ANHIVE";

    // special character to prefix the otp. Make sure this character appears only once in the sms
    public static final String OTP_DELIMITER = ":";
}

9. Dưới package app, tạo một lớp có tên MyApplication.java. Lớp này khởi tạo đối tượng Volley. Lớp này extends từ lớp Application đồng thời chúng ta sẽ thêm lớp này trong trong thẻ <application> trong file AndroidManifest.xml.

MyApplication.java
package info.androidhive.smsverification.app;

import android.app.Application;
import android.text.TextUtils;

import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.toolbox.Volley;

/**
 * Created by Ravi on 13/05/15.
 */

public class MyApplication extends Application {

    public static final String TAG = MyApplication.class
            .getSimpleName();

    private RequestQueue mRequestQueue;

    private static MyApplication mInstance;

    @Override
    public void onCreate() {
        super.onCreate();
        mInstance = this;
    }

    public static synchronized MyApplication getInstance() {
        return mInstance;
    }

    public RequestQueue getRequestQueue() {
        if (mRequestQueue == null) {
            mRequestQueue = Volley.newRequestQueue(getApplicationContext());
        }

        return mRequestQueue;
    }

    public <T> void addToRequestQueue(Request<T> req, String tag) {
        req.setTag(TextUtils.isEmpty(tag) ? TAG : tag);
        getRequestQueue().add(req);
    }

    public <T> void addToRequestQueue(Request<T> req) {
        req.setTag(TAG);
        getRequestQueue().add(req);
    }

    public void cancelPendingRequests(Object tag) {
        if (mRequestQueue != null) {
            mRequestQueue.cancelAll(tag);
        }
    }
}

10. Mở file AndroidManifest.xml và thêm MyApplication vào thẻ <application>.

AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="info.androidhive.smsverification">

    <application
        android:name=".app.MyApplication" ..>
        .
        .

    </application>

</manifest>

11. Trong package help, tạo một lớp có tên MyViewPager.java. Đây là một custom của lớp ViewPager và ở đây chúng ta sẽ vô hiệu hóa chắc năng swipe của nó.

MyViewPager
package info.androidhive.smsverification.helper;

import android.content.Context;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.view.MotionEvent;

/**
 * Created by Ravi on 08/07/15.
 */
public class MyViewPager extends ViewPager {

    public MyViewPager(Context context) {
        super(context);
    }

    public MyViewPager(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        // Never allow swiping to switch between pages
        return false;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        // Never allow swiping to switch between pages
        return false;
    }
}

12. Tạo một lớp với tên PrefManager.java trong package help. Lớp này bao gồm những phương thức lưu trữ thông tin người dùng trong Shared Preferences.

PrefManager.java
package info.androidhive.smsverification.helper;

import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;

import java.util.HashMap;

/**
 * Created by Ravi on 08/07/15.
 */
public class PrefManager {
    // Shared Preferences
    SharedPreferences pref;

    // Editor for Shared preferences
    Editor editor;

    // Context
    Context _context;

    // Shared pref mode
    int PRIVATE_MODE = 0;

    // Shared preferences file name
    private static final String PREF_NAME = "AndroidHive";

    // All Shared Preferences Keys
    private static final String KEY_IS_WAITING_FOR_SMS = "IsWaitingForSms";
    private static final String KEY_MOBILE_NUMBER = "mobile_number";
    private static final String KEY_IS_LOGGED_IN = "isLoggedIn";
    private static final String KEY_NAME = "name";
    private static final String KEY_EMAIL = "email";
    private static final String KEY_MOBILE = "mobile";

    public PrefManager(Context context) {
        this._context = context;
        pref = _context.getSharedPreferences(PREF_NAME, PRIVATE_MODE);
        editor = pref.edit();
    }

    public void setIsWaitingForSms(boolean isWaiting) {
        editor.putBoolean(KEY_IS_WAITING_FOR_SMS, isWaiting);
        editor.commit();
    }

    public boolean isWaitingForSms() {
        return pref.getBoolean(KEY_IS_WAITING_FOR_SMS, false);
    }

    public void setMobileNumber(String mobileNumber) {
        editor.putString(KEY_MOBILE_NUMBER, mobileNumber);
        editor.commit();
    }

    public String getMobileNumber() {
        return pref.getString(KEY_MOBILE_NUMBER, null);
    }

    public void createLogin(String name, String email, String mobile) {
        editor.putString(KEY_NAME, name);
        editor.putString(KEY_EMAIL, email);
        editor.putString(KEY_MOBILE, mobile);
        editor.putBoolean(KEY_IS_LOGGED_IN, true);
        editor.commit();
    }

    public boolean isLoggedIn() {
        return pref.getBoolean(KEY_IS_LOGGED_IN, false);
    }

    public void clearSession() {
        editor.clear();
        editor.commit();
    }

    public HashMap<String, String> getUserDetails() {
        HashMap<String, String> profile = new HashMap<>();
        profile.put("name", pref.getString(KEY_NAME, null));
        profile.put("email", pref.getString(KEY_EMAIL, null));
        profile.put("mobile", pref.getString(KEY_MOBILE, null));
        return profile;
    }
}

6.2 Tạo SMS Receiver

Bây giờ chúng ta sẽ xem làm thế nào để thêm một receiver sẽ được kích hoạt bất cứ khi nào điện thoại nhận tin nhắn SMS. Ngoài ra chúng tôi sẽ thêm một Intent Service để gọi đến các HTTP request khi ứng dụng không được chạy.

13. Trong package service tạo một lớp có tên HttpService.java được extends từ lớp IntentService, service này sẽ là cần thiết khi ứng dụng đã bị đóng ở background. Chúng ta sẽ dùng IntentService để gửi OTP tới server nếu nhưng ứng dụng bị đóng trước khi nhận được SMS.

HttpService.java
package info.androidhive.smsverification.service;

import android.app.IntentService;
import android.content.Intent;
import android.util.Log;
import android.widget.Toast;

import com.android.volley.Request;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.StringRequest;

import org.json.JSONException;
import org.json.JSONObject;

import java.util.HashMap;
import java.util.Map;

import info.androidhive.smsverification.activity.MainActivity;
import info.androidhive.smsverification.app.Config;
import info.androidhive.smsverification.app.MyApplication;
import info.androidhive.smsverification.helper.PrefManager;

/**
 * Created by Ravi on 04/04/15.
 */
public class HttpService extends IntentService {

    private static String TAG = HttpService.class.getSimpleName();

    public HttpService() {
        super(HttpService.class.getSimpleName());
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        if (intent != null) {
            String otp = intent.getStringExtra("otp");
            verifyOtp(otp);
        }
    }

    /**
     * Posting the OTP to server and activating the user
     *
     * @param otp otp received in the SMS
     */
    private void verifyOtp(final String otp) {
        StringRequest strReq = new StringRequest(Request.Method.POST,
                Config.URL_VERIFY_OTP, new Response.Listener<String>() {

            @Override
            public void onResponse(String response) {
                Log.d(TAG, response.toString());

                try {

                    JSONObject responseObj = new JSONObject(response);

                    // Parsing json object response
                    // response will be a json object
                    boolean error = responseObj.getBoolean("error");
                    String message = responseObj.getString("message");

                    if (!error) {
                        // parsing the user profile information
                        JSONObject profileObj = responseObj.getJSONObject("profile");

                        String name = profileObj.getString("name");
                        String email = profileObj.getString("email");
                        String mobile = profileObj.getString("mobile");

                        PrefManager pref = new PrefManager(getApplicationContext());
                        pref.createLogin(name, email, mobile);

                        Intent intent = new Intent(HttpService.this, MainActivity.class);
                        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                        startActivity(intent);

                        Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show();

                    } else {
                        Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show();
                    }

                } catch (JSONException e) {
                    Toast.makeText(getApplicationContext(),
                            "Error: " + e.getMessage(),
                            Toast.LENGTH_LONG).show();
                }

            }
        }, new Response.ErrorListener() {

            @Override
            public void onErrorResponse(VolleyError error
            
            
            
         
0