Android với xác thực SMS tương tự ứng dụng WhatsApp - Phần 2
Bài đăng này đã không được cập nhật trong 8 năm
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ờ
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:
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 fileConfig.php
được tạo ở PHP project phần trước.
OTP_DELIMITER
nên đúng với giá trị được định nghĩa trong fileConfig.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) {
Log.e(TAG, "Error: " + error.getMessage());
Toast.makeText(getApplicationContext(),
error.getMessage(), Toast.LENGTH_SHORT).show();
}
}) {
@Override
protected Map<String, String> getParams() {
Map<String, String> params = new HashMap<String, String>();
params.put("otp", otp);
Log.e(TAG, "Posting params: " + params.toString());
return params;
}
};
// Adding request to request queue
MyApplication.getInstance().addToRequestQueue(strReq);
}
}
14. Bây giờ, bên trong package receiver
, tạo một lớp có tên SmsReceiver.java
là extend của lớp BroadcastReceiver
. Broadcast này sẽ kích hoạt bất cứ khi nào thiết bị nhận được SMS.
SmsReceiver.java
package info.androidhive.smsverification.receiver;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.telephony.SmsMessage;
import android.util.Log;
import info.androidhive.smsverification.app.Config;
import info.androidhive.smsverification.service.HttpService;
/**
* Created by Ravi on 09/07/15.
*/
public class SmsReceiver extends BroadcastReceiver {
private static final String TAG = SmsReceiver.class.getSimpleName();
@Override
public void onReceive(Context context, Intent intent) {
final Bundle bundle = intent.getExtras();
try {
if (bundle != null) {
Object[] pdusObj = (Object[]) bundle.get("pdus");
for (Object aPdusObj : pdusObj) {
SmsMessage currentMessage = SmsMessage.createFromPdu((byte[]) aPdusObj);
String senderAddress = currentMessage.getDisplayOriginatingAddress();
String message = currentMessage.getDisplayMessageBody();
Log.e(TAG, "Received SMS: " + message + ", Sender: " + senderAddress);
// if the SMS is not from our gateway, ignore the message
if (!senderAddress.toLowerCase().contains(Config.SMS_ORIGIN.toLowerCase())) {
return;
}
// verification code from sms
String verificationCode = getVerificationCode(message);
Log.e(TAG, "OTP received: " + verificationCode);
Intent hhtpIntent = new Intent(context, HttpService.class);
hhtpIntent.putExtra("otp", verificationCode);
context.startService(hhtpIntent);
}
}
} catch (Exception e) {
Log.e(TAG, "Exception: " + e.getMessage());
}
}
/**
* Getting the OTP from sms message body
* ':' is the separator of OTP from the message
*
* @param message
* @return
*/
private String getVerificationCode(String message) {
String code = null;
int index = message.indexOf(Config.OTP_DELIMITER);
if (index != -1) {
int start = index + 2;
int length = 6;
code = message.substring(start, start + length);
return code;
}
return code;
}
}
15 Để làm cho service và receiver hoạt động, mở file AndroidManifest.xml
và thay đổi như dưới đây:
Thêm quyền
INTERNET
,RECEIVE_SMS
vàREAD_SMS
Thêm
MyApplication
vào thẻ<application>
Cài đặt để
SmsActivity
làm Activity mặc định.Thêm
SmsReceiver
bằng cách sử dụng thẻ<receiver>
.Thêm
HttpService
bằng cách sử dụng thẻ<service>
.
Và manifest của chúng ta sẽ như sau
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="info.androidhive.smsverification">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.READ_SMS" />
<application
android:name=".app.MyApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/MyMaterialTheme">
<activity
android:name=".activity.SmsActivity"
android:label="@string/title_activity_sms">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".activity.MainActivity"
android:label="@string/app_name"
android:windowSoftInputMode="adjustResize">
</activity>
<!-- SMS Receiver -->
<receiver android:name=".receiver.SmsReceiver">
<intent-filter android:priority="99999">
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
</intent-filter>
</receiver>
<!-- Intent service -->
<service
android:name=".service.HttpService"
android:exported="false" />
</application>
</manifest>
6.2 Tạo màn hình Login
Bây giờ chúng ta đã có đầy đủ các logic cơ sở. Hãy thêm activity đầu tiên để nhập số điện thoại và OTP
16. Trong thư mục res ⇒ layout
tạo ra một layout có tên activity_sms.xml
và thêm mã dưới đây. Layout này có chứa một ViewPager với hai trang. Trong page đầu, chúng tôi yêu cầu người dùng nhập số điện thoại di động của mình. Trong page thứ hai, chúng tôi yêu cầu người dùng nhập mật khẩu OTP nếu xác thực SMS một cách tự động thất bại.
activity_sms.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/viewContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="info.androidhive.smsverification.activity.SmsActivity">
<info.androidhive.smsverification.helper.MyViewPager
android:id="@+id/viewPagerVertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<LinearLayout
android:id="@+id/layout_sms"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="@color/colorPrimary"
android:gravity="center_horizontal"
android:orientation="vertical">
<ImageView
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_gravity="center_horizontal"
android:layout_marginBottom="25dp"
android:layout_marginTop="100dp"
android:src="@mipmap/ic_launcher" />
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="25dp"
android:gravity="center_horizontal"
android:inputType="textCapWords"
android:paddingLeft="40dp"
android:paddingRight="40dp"
android:text="@string/msg_enter_mobile"
android:textColor="@android:color/white"
android:textSize="14dp" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<EditText
android:id="@+id/inputName"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="15dp"
android:background="@android:color/white"
android:fontFamily="sans-serif-light"
android:hint="@string/lbl_name"
android:padding="5dp"
android:singleLine="true"
android:textColor="@color/colorPrimary"
android:textSize="18dp" />
<EditText
android:id="@+id/inputEmail"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="15dp"
android:background="@android:color/white"
android:fontFamily="sans-serif-light"
android:hint="@string/lbl_email"
android:inputType="textEmailAddress"
android:padding="5dp"
android:textColor="@color/colorPrimary"
android:textSize="18dp" />
<EditText
android:id="@+id/inputMobile"
android:layout_width="240dp"
android:layout_height="wrap_content"
android:background="@android:color/white"
android:fontFamily="sans-serif-light"
android:hint="@string/lbl_mobile"
android:inputType="phone"
android:maxLength="10"
android:padding="5dp"
android:textColor="@color/colorPrimary"
android:textCursorDrawable="@null"
android:textSize="18dp" />
<Button
android:id="@+id/btn_request_sms"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="25dp"
android:background="@color/colorPrimaryDark"
android:text="@string/lbl_next"
android:textColor="@android:color/white"
android:textSize="14dp" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/layout_otp"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@color/colorPrimary"
android:gravity="center_horizontal"
android:orientation="vertical">
<ImageView
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_gravity="center_horizontal"
android:layout_marginBottom="25dp"
android:layout_marginTop="100dp"
android:src="@mipmap/ic_launcher" />
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="15dp"
android:gravity="center_horizontal"
android:paddingLeft="40dp"
android:paddingRight="40dp"
android:text="@string/msg_sit_back"
android:textColor="@android:color/white"
android:textSize="16dp" />
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="25dp"
android:gravity="center_horizontal"
android:paddingLeft="40dp"
android:paddingRight="40dp"
android:text="@string/msg_manual_otp"
android:textColor="@android:color/white"
android:textSize="12dp" />
<EditText
android:id="@+id/inputOtp"
android:layout_width="120dp"
android:layout_height="wrap_content"
android:background="@android:color/white"
android:fontFamily="sans-serif-light"
android:gravity="center_horizontal"
android:hint="@string/lbl_enter_otp"
android:inputType="number"
android:maxLength="6"
android:padding="10dp"
android:textCursorDrawable="@null"
android:textSize="18dp" />
<Button
android:id="@+id/btn_verify_otp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="25dp"
android:background="@color/colorPrimaryDark"
android:paddingLeft="20dp"
android:paddingRight="20dp"
android:text="@string/lbl_submit"
android:textColor="@android:color/white"
android:textSize="14dp" />
</LinearLayout>
</info.androidhive.smsverification.helper.MyViewPager>
<ProgressBar
android:id="@+id/progressBar"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_gravity="center"
android:layout_marginBottom="60dp"
android:indeterminateTint="@color/colorAccent"
android:indeterminateTintMode="src_atop"
android:visibility="gone" />
<LinearLayout
android:id="@+id/layout_edit_mobile"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginBottom="50dp"
android:gravity="center"
android:orientation="horizontal">
<TextView
android:id="@+id/txt_edit_mobile"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@android:color/white"
android:textSize="16dp" />
<ImageButton
android:id="@+id/btn_edit_mobile"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginLeft="10dp"
android:background="@null"
android:src="@drawable/ic_edit_mobile" />
</LinearLayout>
</RelativeLayout>
17. Tạo một activity có tên SmsActivity.java
trong package activity
với nội dung như bên dưới:
phương thức
requestForSMS()
sẽ gọi đến server với các tham sốname
,mobile
cho sms.phương thức
verifyOtp()
sẽ gửi thông tin OTP tới server để xác thực nó.
SmsActivity.java
package info.androidhive.smsverification.activity;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
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.R;
import info.androidhive.smsverification.app.Config;
import info.androidhive.smsverification.app.MyApplication;
import info.androidhive.smsverification.helper.PrefManager;
import info.androidhive.smsverification.service.HttpService;
public class SmsActivity extends AppCompatActivity implements View.OnClickListener {
private static String TAG = SmsActivity.class.getSimpleName();
private ViewPager viewPager;
private ViewPagerAdapter adapter;
private Button btnRequestSms, btnVerifyOtp;
private EditText inputName, inputEmail, inputMobile, inputOtp;
private ProgressBar progressBar;
private PrefManager pref;
private ImageButton btnEditMobile;
private TextView txtEditMobile;
private LinearLayout layoutEditMobile;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sms);
viewPager = (ViewPager) findViewById(R.id.viewPagerVertical);
inputName = (EditText) findViewById(R.id.inputName);
inputEmail = (EditText) findViewById(R.id.inputEmail);
inputMobile = (EditText) findViewById(R.id.inputMobile);
inputOtp = (EditText) findViewById(R.id.inputOtp);
btnRequestSms = (Button) findViewById(R.id.btn_request_sms);
btnVerifyOtp = (Button) findViewById(R.id.btn_verify_otp);
progressBar = (ProgressBar) findViewById(R.id.progressBar);
btnEditMobile = (ImageButton) findViewById(R.id.btn_edit_mobile);
txtEditMobile = (TextView) findViewById(R.id.txt_edit_mobile);
layoutEditMobile = (LinearLayout) findViewById(R.id.layout_edit_mobile);
// view click listeners
btnEditMobile.setOnClickListener(this);
btnRequestSms.setOnClickListener(this);
btnVerifyOtp.setOnClickListener(this);
// hiding the edit mobile number
layoutEditMobile.setVisibility(View.GONE);
pref = new PrefManager(this);
// Checking for user session
// if user is already logged in, take him to main activity
if (pref.isLoggedIn()) {
Intent intent = new Intent(SmsActivity.this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
finish();
}
adapter = new ViewPagerAdapter();
viewPager.setAdapter(adapter);
viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
/**
* Checking if the device is waiting for sms
* showing the user OTP screen
*/
if (pref.isWaitingForSms()) {
viewPager.setCurrentItem(1);
layoutEditMobile.setVisibility(View.VISIBLE);
}
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.btn_request_sms:
validateForm();
break;
case R.id.btn_verify_otp:
verifyOtp();
break;
case R.id.btn_edit_mobile:
viewPager.setCurrentItem(0);
layoutEditMobile.setVisibility(View.GONE);
pref.setIsWaitingForSms(false);
break;
}
}
/**
* Validating user details form
*/
private void validateForm() {
String name = inputName.getText().toString().trim();
String email = inputEmail.getText().toString().trim();
String mobile = inputMobile.getText().toString().trim();
// validating empty name and email
if (name.length() == 0 || email.length() == 0) {
Toast.makeText(getApplicationContext(), "Please enter your details", Toast.LENGTH_SHORT).show();
return;
}
// validating mobile number
// it should be of 10 digits length
if (isValidPhoneNumber(mobile)) {
// request for sms
progressBar.setVisibility(View.VISIBLE);
// saving the mobile number in shared preferences
pref.setMobileNumber(mobile);
// requesting for sms
requestForSMS(name, email, mobile);
} else {
Toast.makeText(getApplicationContext(), "Please enter valid mobile number", Toast.LENGTH_SHORT).show();
}
}
/**
* Method initiates the SMS request on the server
*
* @param name user name
* @param email user email address
* @param mobile user valid mobile number
*/
private void requestForSMS(final String name, final String email, final String mobile) {
StringRequest strReq = new StringRequest(Request.Method.POST,
Config.URL_REQUEST_SMS, 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");
// checking for error, if not error SMS is initiated
// device should receive it shortly
if (!error) {
// boolean flag saying device is waiting for sms
pref.setIsWaitingForSms(true);
// moving the screen to next pager item i.e otp screen
viewPager.setCurrentItem(1);
txtEditMobile.setText(pref.getMobileNumber());
layoutEditMobile.setVisibility(View.VISIBLE);
Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(getApplicationContext(),
"Error: " + message,
Toast.LENGTH_LONG).show();
}
// hiding the progress bar
progressBar.setVisibility(View.GONE);
} catch (JSONException e) {
Toast.makeText(getApplicationContext(),
"Error: " + e.getMessage(),
Toast.LENGTH_LONG).show();
progressBar.setVisibility(View.GONE);
}
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.e(TAG, "Error: " + error.getMessage());
Toast.makeText(getApplicationContext(),
error.getMessage(), Toast.LENGTH_SHORT).show();
progressBar.setVisibility(View.GONE);
}
}) {
/**
* Passing user parameters to our server
* @return
*/
@Override
protected Map<String, String> getParams() {
Map<String, String> params = new HashMap<String, String>();
params.put("name", name);
params.put("email", email);
params.put("mobile", mobile);
Log.e(TAG, "Posting params: " + params.toString());
return params;
}
};
// Adding request to request queue
MyApplication.getInstance().addToRequestQueue(strReq);
}
/**
* sending the OTP to server and activating the user
*/
private void verifyOtp() {
String otp = inputOtp.getText().toString().trim();
if (!otp.isEmpty()) {
Intent grapprIntent = new Intent(getApplicationContext(), HttpService.class);
grapprIntent.putExtra("otp", otp);
startService(grapprIntent);
} else {
Toast.makeText(getApplicationContext(), "Please enter the OTP", Toast.LENGTH_SHORT).show();
}
}
/**
* Regex to validate the mobile number
* mobile number should be of 10 digits length
*
* @param mobile
* @return
*/
private static boolean isValidPhoneNumber(String mobile) {
String regEx = "^[0-9]{10}$";
return mobile.matches(regEx);
}
class ViewPagerAdapter extends PagerAdapter {
@Override
public int getCount() {
return 2;
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == ((View) object);
}
public Object instantiateItem(View collection, int position) {
int resId = 0;
switch (position) {
case 0:
resId = R.id.layout_sms;
break;
case 1:
resId = R.id.layout_otp;
break;
}
return findViewById(resId);
}
}
}
Bây giờ chạy ứng dụng và thử nghiệm. Hãy chắc chắn rằng bạn có địa chỉ IP chính xác của localhost của bạn trong Config.java
6.2 Hiển thị thông tin người dùng đã đăng nhập
Hiển thị thông tin người dùng đã đăng nhập là bước tiếp theo. Với thông tin người dùng được lưu trữ trong Preferences chung trong HttpService.class
. Trong activity này, chúng ta phải đọc các thông tin từ shared preferences và hiển thị nó trên màn hình.
18. Tạo một file xml giao diện với tên toolbar.xml
trong thư mục res ⇒ layout
.
toolbar.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:local="http://schemas.android.com/apk/res-auto"
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?attr/actionBarSize"
android:background="?attr/colorPrimary"
local:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
local:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
19. Mở file giao diện cho main activity (activity_main.xml) và thay đổi nó như dưới đây. File này sẽ bao gồm một vài TextView được hiện thị để hiển thị thông tin người dùng đã đăng nhập.
activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<LinearLayout
android:id="@+id/layout_toolbar"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:orientation="vertical">
<include
android:id="@+id/toolbar"
layout="@layout/toolbar" />
</LinearLayout>
<LinearLayout
android:id="@+id/layout_mobile"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:orientation="vertical">
<TextView
android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:textSize="18dp" />
<TextView
android:id="@+id/email"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:textSize="18dp" />
<TextView
android:id="@+id/mobile"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:textSize="18dp" />
</LinearLayout>
</RelativeLayout>
20. Mở menu_main.xml
trong thư mục res ⇒ menu
và thêm các item để cung cấp thêm tùy chọn đăng xuất.
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".MainActivity">
<item
android:id="@+id/action_logout"
android:orderInCategory="100"
android:title="@string/action_logout"
app:showAsAction="always" />
</menu>
21. Mở file MainActivity.java
và thay đổi nó như dưới đây. Trong activity này, chúc ta cần đọc ra thông tin người dùng đã được lưu trữ tại shared preferences và hiển thị nó lên trên màn hình.
MainActivity.java
package info.androidhive.smsverification.activity;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.TextView;
import java.util.HashMap;
import info.androidhive.smsverification.R;
import info.androidhive.smsverification.helper.PrefManager;
public class MainActivity extends AppCompatActivity {
private Toolbar toolbar;
private PrefManager pref;
private TextView name, email, mobile;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
toolbar = (Toolbar) findViewById(R.id.toolbar);
name = (TextView) findViewById(R.id.name);
email = (TextView) findViewById(R.id.email);
mobile = (TextView) findViewById(R.id.mobile);
// enabling toolbar
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayShowHomeEnabled(true);
pref = new PrefManager(getApplicationContext());
// Checking if user session
// if not logged in, take user to sms screen
if (!pref.isLoggedIn()) {
logout();
}
// Displaying user information from shared preferences
HashMap<String, String> profile = pref.getUserDetails();
name.setText("Name: " + profile.get("name"));
email.setText("Email: " + profile.get("email"));
mobile.setText("Mobile: " + profile.get("mobile"));
}
/**
* Logging out user
* will clear all user shared preferences and navigate to
* sms activation screen
*/
private void logout() {
pref.clearSession();
Intent intent = new Intent(MainActivity.this, SmsActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
finish();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_logout) {
logout();
return true;
}
return super.onOptionsItemSelected(item);
}
}
7. Test ứng dụng
Để test ứng dụng này, hãy làm theo các bước sau.
- Chắc chắn rằng thiết bị của bạn và PC đang chạy project PHP là ở trong cùng một màng wifi.
- Cung cấp đúng
MSG91_AUTH_KEY
trongConfig.php
- Chắc chắn rằng
MSG91_SENDER_ID
trongConfig.java
vàSMS_ORIGIN
trongConfig.java
là trùng với nhau. - Đặt đúng địa chỉ ip của localhost trong
Config.java
. Trên hệ điều hành Windows chạy lệnhipconfig
ở cửa sổ CMD để lấy được địa chỉ ip
Nguồn tham khảo: Android adding SMS Verification Like WhatsApp – Part 2
All rights reserved