上一篇把日期天气组件开发完了,锁屏界面的内容基本上出来了。联系人和APP应用组件就是个RecyclerView,不再赘述。
本篇讲述APP怎么通过短信实现远程配置与服务启动。
个人觉得这个办法还是挺好用的,不用后台,也不用用户系统,可以实现远程控制。
先看看实现逻辑:
图中第六步是待实现的,1-5步都已在工程里已实现。
下面是具体实现:
1:需要短信的收发权限,要在Manifest.xml中申明:
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.SEND_SMS" />
权限是首要的,而且在APP中发送短信的地方还要动态获取权限。
2:将配置Model通过Gson库转化成json字符串,塞到短信发送器中:
以日期天气组件的远程配置为例:
private void sendSMS() {
String phone = etPhone.getText().toString();
if(StringUtils.isEmpty(phone)){
ToastUtils.showToast(this,"请填写手机号");
return;
}
if(!StringUtils.isPhoneNumber(phone)){
ToastUtils.showToast(this,"请填写正确的手机号");
return;
}
SharedUtils.getInstance().putString(SHARED_SEND_SMS_PHONE,phone);
SmsManager manager = SmsManager.getDefault();
switch (type){
case SEND_WEATHER:
manager.sendTextMessage(phone,null,getWeatherStr(),null,null);
break;
case SEND_APP:
manager.sendTextMessage(phone,null,getAppStr(),null,null);
break;
case SEND_CONTACT:
manager.sendTextMessage(phone,null,getContactStr(),null,null);
break;
case SEND_ALL:
manager.sendTextMessage(phone,null,getWeatherStr(),null,null);
manager.sendTextMessage(phone,null,getAppStr(),null,null);
manager.sendTextMessage(phone,null,getContactStr(),null,null);
break;
case SEND_CMD:
manager.sendTextMessage(phone,null,getCMDStr(),null,null);
break;
}
ToastUtils.showToast(this,"发送成功");
finish();
}
private String getWeatherStr(){
StringBuffer smsBuffer = new StringBuffer();
smsBuffer.append(LockerService.SMS_FLAG);
smsBuffer.append(SmsReceiver.SMS_TYPE_WEATHER);
WeatherConfig weatherConfig = SharedUtils.getInstance().getWeatherConfig();
smsBuffer.append(new Gson().toJson(weatherConfig));
return smsBuffer.toString();
}
3:注册短信拦截接收器,使用全局广播:
<receiver
android:name=".receiver.SmsReceiver"
android:enabled="true"
android:exported="true">
<intent-filter android:priority="1000">
<action android:name="android.provider.Telephony.SMS_RECEIVED"/>
</intent-filter>
</receiver>
4:申明SmsReceiver类:
public class SmsReceiver extends BroadcastReceiver {
public static final String SMS_FLAG = "[easycall]";
public static final String SMS_TYPE_WEATHER = "[weather]";
public static final String SMS_TYPE_APP = "[app]";
public static final String SMS_TYPE_CONTACT = "[contact]";
public static final String SMS_TYPE_CMD = "[cmd]";
private static final int RECEIVERED_MSG = 1;
Context context;
@Override
public void onReceive(Context context, Intent intent) {
this.context = context;
dealMessage(intent);
}
void dealMessage(Intent intent){
Object[] object=(Object[]) intent.getExtras().get("pdus");
SMSModel model = new SMSModel();
for (Object pdus : object) {
byte[] pdusMsg=(byte[]) pdus;
SmsMessage sms= SmsMessage.createFromPdu(pdusMsg);
String mobile=sms.getOriginatingAddress();//发送短信的手机号
String content=sms.getMessageBody();//短信内容
model.setPhone(mobile);
model.setContent(content);
break;
}
Message msg=new Message();
msg.what = RECEIVERED_MSG;
msg.obj = model;
handler.sendMessage(msg);
}
private Handler handler = new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
SMSModel model = (SMSModel) msg.obj;
String phone = model.getPhone();
String content = model.getContent();
PhoneModel bindPhoneModel = SharedUtils.getInstance().getPhoneModel();
if(bindPhoneModel == null){
return;
}
String bindPhone = bindPhoneModel.getPhone();
if(StringUtils.isEmpty(bindPhone)){
return;
}
bindPhone = bindPhone.replace(" ", "");// 去掉空格
bindPhone = bindPhone.replace("+86","");// 去掉+86
// 去掉86
phone = phone.replace("+86","");
if(phone.startsWith("86")){
phone = phone.substring(2);
}
//只处理绑定手机发来的短信
if(bindPhone.contains(phone)){
if(!StringUtils.isEmpty(content) && content.startsWith(SMS_FLAG)){
// 配置文件
String configStr = content.replace(SMS_FLAG,"");
if(configStr.contains(SMS_TYPE_WEATHER)){
configStr = configStr.replace(SMS_TYPE_WEATHER,"");
WeatherConfig config = new Gson().fromJson(configStr,WeatherConfig.class);
SharedUtils.getInstance().setWeatherConfig(config);
} else if(configStr.contains(SMS_TYPE_APP)){
configStr = configStr.replace(SMS_TYPE_APP,"");
AppConfig config = new Gson().fromJson(configStr,AppConfig.class);
SharedUtils.getInstance().setAppConfig(config);
} else if(configStr.contains(SMS_TYPE_CONTACT)){
configStr = configStr.replace(SMS_TYPE_CONTACT,"");
ContactConfig config = new Gson().fromJson(configStr,ContactConfig.class);
SharedUtils.getInstance().setContactConfig(config);
} else if(configStr.contains(SMS_TYPE_CMD)){
configStr = configStr.replace(SMS_TYPE_CMD,"");
// 启动服务
if(CMDModel.CMD_START_SERVICE.equals(configStr)){
// 远程启动关闭时不做启动
if(!SharedUtils.getInstance().getRemoteStart()){
return;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(new Intent(context, LockerService.class));
} else {
context.startService(new Intent(context, LockerService.class));
}
}
}
}
}
}
};
}
这里把整个SmsReceiver类贴出来了,里面包含了各类配置文件解析,服务启动的处理。
使用短信通信有一些弊端:
1:会产生垃圾短信,虽然现在手机的短信功能基本是一个通知作用,很少再有用户用短信来做社交。这个后期可以通过清理功能来处理;
2:目前是明文短信,配置文件直接转成json字符串裸露。后期可以通过加密方式处理;
3:发送短信时系统会有个弹出APP正在使用产生资费的功能提示框需要用户确认,这个不太好处理。因此上图逻辑中的第六步暂时没实现,在配置成功发反馈短信时需要老人做这个确认不太可能。