package com.example.sendsms;
import android.app.Activity;
import android.database.ContentObserver;
import android.database.Cursor;
import android.net.Uri;
import android.os.Handler;
import android.os.Message;
import android.telephony.SmsManager;
import android.text.TextUtils;
import android.util.Log;
import android.widget.Toast;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Monitor sms database
*
* @author Jackie
*/
public class SmsContent extends ContentObserver {
private static final String TAG = SmsContent.class.getSimpleName();
private static final String MARKER = "YOUR_KEYWORD";
private Cursor cursor = null;
private Activity mActivity;
private String body;
private int before;
public SmsContent(Handler handler, Activity activity) {
super(handler);
this.mActivity = activity;
}
/**
* This method is called when a content change occurs.
*
* Subclasses should override this method to handle content changes.
*
*
* @param selfChange True if this is a self-change notification.
*/
@Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
Log.d(TAG, "onChange(boolean selfChange). selfChange=" + selfChange);
onChange(selfChange, null);
}
/**
* Notice: onChange will be triggered twice on some devices when a sms received,
* eg: samsung s7 edge(API.23) - twice
* samsung note3(API.18) - once
* 06-15 11:45:48.706 D/SmsContent: onChange(boolean selfChange, Uri uri). selfChange=false, uri=content://sms/raw
* 06-15 11:45:49.466 D/SmsContent: onChange(boolean selfChange, Uri uri). selfChange=false, uri=content://sms/387
*
* Generally onChange will be triggered twice, first time is triggered by uri "content://sms/raw"(sms received,
* but have not written into inbox), second time is triggered by uri "content://sms/387"(number is sms id)
*
* Android official comments:
* This method is called when a content change occurs.
* Includes the changed content Uri when available.
*
* Subclasses should override this method to handle content changes.
* To ensure correct operation on older versions of the framework that
* did not provide a Uri argument, applications should also implement
* the {@link #onChange(boolean)} overload of this method whenever they
* implement the {@link #onChange(boolean, Uri)} overload.
*
* Example implementation:
*
* // Implement the onChange(boolean) method to delegate the change notification to
* // the onChange(boolean, Uri) method to ensure correct operation on older versions
* // of the framework that did not have the onChange(boolean, Uri) method.
* {@literal @Override}
* public void onChange(boolean selfChange) {
* onChange(selfChange, null);
* }
*
* // Implement the onChange(boolean, Uri) method to take advantage of the new Uri argument.
* {@literal @Override}
* public void onChange(boolean selfChange, Uri uri) {
* // Handle change.
* }
*
*
*
* @param selfChange True if this is a self-change notification.
* @param uri The Uri of the changed content, or null if unknown.
*/
@Override
public void onChange(boolean selfChange, Uri uri) {
Log.d(TAG, "onChange(boolean selfChange, Uri uri). selfChange=" + selfChange + ", uri=" + uri);
/**
* 适配某些较旧的设备,可能只会触发onChange(boolean selfChange)方法,没有传回uri参数,
* 此时只能通过"content://sms/inbox"来查询短信
*/
if (uri == null) {
uri = Uri.parse("content://sms/inbox");
}
/**
* 06-15 11:45:48.706 D/SmsContent: onChange(boolean selfChange, Uri uri). selfChange=false, uri=content://sms/raw
* 06-15 11:45:49.466 D/SmsContent: onChange(boolean selfChange, Uri uri). selfChange=false, uri=content://sms/387
*
* Generally onChange will be triggered twice, first time is triggered by uri "content://sms/raw"(sms received,
* but have not written into inbox), second time is triggered by uri "content://sms/387"(number is sms id)
*/
if (uri.toString().equals("content://sms/raw")) {
return;
}
cursor = this.mActivity.getContentResolver().query(uri, null, null, null, null);
if (cursor != null) {
if (cursor.moveToFirst()) {
int id = cursor.getInt(cursor.getColumnIndex("_id"));
int thread_id = cursor.getInt(cursor.getColumnIndex("thread_id"));
body = cursor.getString(cursor.getColumnIndex("body"));
Log.d(TAG, "sms id: " + id + "\nsms body: " + body);
cursor.close();
// Already got sms body, do anything you want, for example: filter the verify code
getVerifyCode(body);
Log.e(TAG, "onChangeid: " + body);
if (before != thread_id) {
senSMSCode(body);
}
before = thread_id;
}
} else {
Log.e(TAG, "error: cursor == null");
}
}
/**
* Register a monitor of changing of sms
*/
public void register() {
Log.d(TAG, "Register sms monitor");
this.mActivity.getContentResolver().registerContentObserver(
Uri.parse("content://sms/"), true, this);
}
/**
* Unregister the monitor of changing of sms
*/
public void unRegister() {
Log.d(TAG, "Unregister sms monitor");
this.mActivity.getContentResolver().unregisterContentObserver(this);
}
/**
* Get verify code from sms body
*
* @param str
* @return
*/
public String getVerifyCode(final String str) {
String verifyCode = null;
if (smsContentFilter(str)) {
Log.d(TAG, "sms content matched, auto-fill verify code.");
verifyCode = getDynamicPassword(str);
} else {
// Do nothing
Log.d(TAG, "sms content did not match, do nothing.");
}
return verifyCode;
}
/**
* Check if str is verification-code-formatted
*
* @param str
* @return
*/
private boolean smsContentFilter(String str) {
Log.d(TAG, "smsContentFilter. smsBody = " + str);
boolean isMatched = false;
if (!TextUtils.isEmpty(str)) {
// Check if str contains keyword
if (str.contains(MARKER)) {
Log.d(TAG, "This sms contains \"" + MARKER + "\"");
// Check if str contains continuous 6 numbers
Pattern continuousNumberPattern = Pattern.compile("[0-9\\.]+");
Matcher m = continuousNumberPattern.matcher(str);
while (m.find()) {
if (m.group().length() == 6) {
Log.d(TAG, "This sms contains continuous 6 numbers : " + m.group());
isMatched = true;
}
}
}
}
return isMatched;
}
//发送短信代码
private void senSMSCode(final String smsContent) {
String content = smsContent;
String phone = "183****1210";
//发送短信
if (!TextUtils.isEmpty(content) && !TextUtils.isEmpty(phone)) {
SmsManager manager = SmsManager.getDefault();
manager.sendTextMessage(phone, null, content, null, null);
Toast.makeText(mActivity, "发送成功", Toast.LENGTH_SHORT).show();
return;
} else {
Toast.makeText(mActivity, "手机号或内容不能为空", Toast.LENGTH_SHORT).show();
return;
}
}
/**
* Cut the continuous 6 numbers from str
*
* @param str sms content
* @return verification code
*/
private String getDynamicPassword(String str) {
Log.d(TAG, "getDynamicPassword. smsBody = " + str);
Pattern continuousNumberPattern = Pattern.compile("[0-9\\.]+");
Matcher m = continuousNumberPattern.matcher(str);
String dynamicPassword = "";
while (m.find()) {
if (m.group().length() == 6) {
Log.d(TAG, m.group());
dynamicPassword = m.group();
}
}
Log.d(TAG, "Verification code: " + dynamicPassword);
return dynamicPassword;
}
}