短信找手机有两种方式, 短信找手机 (声音 / 灯光) 与短信找手机(手机定位), 分别对应两种不同的情景: 一种是在某个地方手机乱放找不到, 另一种是手机丢失(比如掉了, 被偷之类的)...
注意: 该功能需要另一台手机的辅助, 才能找到手机(后面即将介绍的语音找手机则不需要另一台手机辅助)
1 短信找手机(声音 / 灯光)
短信找手机 (声音 / 灯光) 是确定手机在某个区域后的, 定位手机的准确位置, 比如在家里手机乱丢找不到, 在办公室忘记放在哪个同事座位上等...
1.1 原理
该功能在 App 中预设短信找手机密码, 另一台手机发送短信, 丢失的手机通过监听短信广播, 如果短信内容是预设的密码, 则 App 会播放相应的提示音乐或者打开手电筒功能(手机摄像头闪光灯), 方便用户快速, 准确的找到手机.
1.2 短信监听
监听短信内容, 根据不同短信密码进入服务, 传入不同的参数进行处理
- public void onReceive(Context context, Intent intent) {
- Object[] pdus = (Object[]) intent.getExtras().get("pdus");
- for (Object pdu : pdus) {
- SmsMessage smsMessage = SmsMessage.createFromPdu((byte[]) pdu);
- String content = smsMessage.getMessageBody(); // 获取短信内容
- String msgPwd = ""; // 这里读取 App 设置的短信找手机 (声音 / 灯光) 密码
- String msgLatlngPwd = ""; // 这里读取 App 设置的短信找手机 (手机定位) 密码
- if (content == null || content.length() <6) { // 由于密码设置必须多于 6 位, 短信内容小于 6 位不进行后续判断
- continue;
- }
- if (content.trim().equals(msgPwd)) {
- // 短信找手机(声音 / 灯光), 具体内容到服务中进行处理
- startService(context, E7Service.FROM_SMS_RECEIVER, smsMessage.getOriginatingAddress());
- abortBroadcast();
- return;
- } else if (content.trim().equals(msgLatlngPwd)) {
- // 短信找手机(手机定位), 具体内容到服务中进行处理
- startService(context, E7Service.FROM_SMS_RECEIVER_LATLNG, smsMessage.getOriginatingAddress());
- abortBroadcast(); // 表示该广播已经处理完毕, 该短信不会显示到系统短信列表中(部分手机该操作无效)
- return;
- }
- }
- }
- private void startService(Context context, int from, String phoneNum) {
- Intent intentE7Service = new Intent(context, E7Service.class);
- // 类别, 是短信找手机 (声音 / 灯光), 短信找手机(手机定位) 或者其他
- intentE7Service.putExtra(E7Service.FROM, from);
- // 经纬度找手机, 需要回复信息, 所以将发送方的手机号传到服务中
- intentE7Service.putExtra("phoneNum", phoneNum);//getDisplayOriginatingAddress());
- ServiceUtil.startService(context, intentE7Service);
- }
短信监听需要广播静态注册, 并设置优先级, 增加相应权限
- <!-- start: 找手机权限 -->
- <uses-permission Android:name="android.permission.SEND_SMS" />
- <uses-permission Android:name="android.permission.READ_SMS" />
- <!--<uses-permission android:name="android.permission.SEND_RESPOND_VIA_MESSAGE" />
- <uses-permission android:name="android.permission.BROADCAST_SMS" />-->
- <uses-permission Android:name="android.permission.SMS_DELIVER_ACTION" />
- <uses-permission Android:name="android.permission.RECEIVE_SMS"/>
- <uses-permission Android:name="android.permission.ACCESS_COARSE_LOCATION" />
- <uses-permission Android:name="android.permission.ACCESS_FINE_LOCATION" />
- <uses-permission Android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
- <!-- end: 找手机权限 -->
- <receiver Android:name=".receiver.SmsReceiver"
- Android:process=":remote">
- <intent-filter Android:priority="1000">
- <action Android:name="android.provider.Telephony.SMS_RECEIVED"/>
- </intent-filter>
- </receiver>
1.3 服务
正常启动一个粘性 (START_STICKY) 服务, 如果根据 FROM 进行不同的操作, 播放声音, 打开手电筒或进行定位回复短信...
另外, 这里其实还有一个操作, 进程保活, 因为 service 在后台可能被杀掉, 如何降低被杀掉的风险, 如果被杀掉如何重新拉起... 之前写过一篇, 可以参考: Android 进程保活(黑白手段让 App 活下去)(写的时间久了点, 可能有点过时, 欢迎补充...)
因为保活太难, 我尝试了多种方式, 发了多个版本, 最终始终有人反馈某些手机后台服务被杀掉了(即该功能无法使用), 于是在 Android 0 以上版本, 改为开启前台服务:
- ServiceUtil.startForeground(this, ServiceUtil.E7ServiceNotifyId, this.getApplicationContext(), 0, 0, 0, 0 );
- public static void startForeground(Service service, int id, Context context, int textResId, int titleResId, int smallIconResId, int largeIconResId) {
- if (Build.VERSION.SDK_INT>= Build.VERSION_CODES.O) {
- service.startForeground(id, getNotification(context, textResId, titleResId, smallIconResId, largeIconResId)); // 这个 id 不要和应用内的其他通知 id 一样, 不行就写 int.maxValue() //context.startForeground(SERVICE_ID, builder.getNotification());
- }
- }
1.4 声音(预设音频播放)
- /**
- * 初始化音效
- */
- public void initMusic() {
- soundp = new SoundPool(50, AudioManager.STREAM_MUSIC, 100);
- soundm = new HashMap<String, Integer>();
- soundm.put(SOUND_PHONE_IS_HERE, soundp.load(this, R.raw.findphone, 1));
- }
- /**
- * 播放音效
- */
- public void playMusic(String str) {
- // // MobclickAgent.onEvent(E7App.mApp, Constant.Umeng.Send_play_music);
- if (soundp == null || soundm == null) {
- initMusic();
- }
- AudioManager am = (AudioManager) getSystemService(AUDIO_SERVICE);
- int ringMode = am.getRingerMode();
- if (ringMode == AudioManager.RINGER_MODE_VIBRATE || ringMode == AudioManager.RINGER_MODE_SILENT)
- am.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
- int now = am.getStreamVolume(AudioManager.STREAM_MUSIC);// 得到听筒模式的当前值
- int max = /**now;//*/am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
- am.setStreamVolume(AudioManager.STREAM_MUSIC, max, AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE);
- soundp.play(soundm.get(str), 1, 1, 0, 0, 1f);
- try {
- // 5s 后将声音设置还原(播放完毕)
- Thread.sleep(5000);
- } catch (Exception e) {
- e.printStackTrace();
- }
- am.setStreamVolume(AudioManager.STREAM_MUSIC, now, AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE);
- // init(this);
- }
- 1.5 手电筒(灯光提醒)
- 参考之前文章: 8.[小萌伴 Android] 手电筒功能及其实现
- 2 短信找手机(手机定位)
- 短信找手机 (手机定位) 用于完全不能确定手机的位置, 定位手机的大概位置,(如手机掉在外面, 手机被偷等...)该功能可以配合上面的功能使用, 从而准确找到手机.
- 2.1 原理
- 该功能在 App 中预设短信找手机密码, 另一台手机发送短信, 丢失的手机通过监听短信广播, 如果短信内容是预设的密码, 则 App 会调用手机定位功能确定一个经纬度 (位置), 并将该经纬度通过短信回复到短信发送方. 比如: A 找不到手机, 通过 B 手机发送密码(123456) 到 A 手机, A 手机接收到后, 会自动进行定位, 并把位置以短信的方式返回给 B,B 可以直接点击短信中的 url 打开百度地图看到 A 手机位置, 或者从短信中拷贝经纬度, 到电脑进行查询位置.
- 2.2 短信监听
- 与上面的短信监听一致, 仅密码不同
- 2.3 后台服务
- 与上面的后台服务一致
- 2.4 位置(百度定位)
- 百度定位不做太多介绍, 根据百度官方文档进行开发即可... 这里主要是监听定位回调, 得到结果后发送短信
- public class MyLocationListener implements BDLocationListener {
- @Override
- public void onReceiveLocation(BDLocation location) {
- if (location != null) {
- int locType = location.getLocType();
- switch (locType) {
- case BDLocation.TypeNetWorkLocation:
- case BDLocation.TypeGpsLocation:
- case BDLocation.TypeOffLineLocation:
- Loc.getInstance(mLoc).stopLocation();
- sendMsg(location.getLatitude(), location.getLongitude());
- break;
- }
- }
- }
- }
- 2.5 短信回复(包含百度 url)
- 短信发送并监听发送结果
- private String phoneNum;
- private void sendMsg(double lat, double lng) {
- // 拼接百度地图经纬度解析 url, 方便对方收到短信回复后直接点击进入地图查看位置
- String message = "http://api.map.baidu.com/geocoder?" +
- "location=" + lat + "," + lng +
- /*"&coord_type=bd0911" +*/ //gcj02""&output=html" +
- "&src=e7yoo[" + OsUtil.getAppName(this) + "]";
- synchronized (phoneNumObj) {
- if(phoneNum == null) {
- // CrashReport.postCatchedException(new Exception("phoneNum == null"));
- return;
- }
- String phoneNum = this.phoneNum;
- sendMsg(phoneNum, message);
- this.phoneNum = null;
- }
- // MobclickAgent.onEvent(E7App.mApp, Constant.Umeng.Send_sms_latlng);
- }
- private void sendMsg(String phoneNum, String message) {
- if(phoneNum == null) {
- // // MobclickAgent.onEvent(E7App.mApp, Constant.Umeng.Send_sms_latlng_phoneisnull);
- return;
- }
- String SENT = "sms_sent";
- String DELIVERED = "sms_delivered";
- PendingIntent sentPI = PendingIntent.getActivity(this, 0, new Intent(SENT), 0);
- PendingIntent deliveredPI = PendingIntent.getActivity(this, 0, new Intent(DELIVERED), 0);
- registerReceiver(new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- switch (getResultCode()) {
- case Activity.RESULT_OK:
- // // MobclickAgent.onEvent(E7App.mApp, Constant.Umeng.Send_sms_latlng_success);
- break;
- case SmsManager.RESULT_ERROR_GENERIC_FAILURE:
- case SmsManager.RESULT_ERROR_NO_SERVICE:
- case SmsManager.RESULT_ERROR_NULL_PDU:
- case SmsManager.RESULT_ERROR_RADIO_OFF:
- default:
- // // CrashReport.postCatchedException(new Throwable(getResultData() + getResultCode()));
- break;
- }
- }
- }, new IntentFilter(SENT));
- registerReceiver(new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- switch (getResultCode()) {
- case Activity.RESULT_OK:
- // // MobclickAgent.onEvent(E7App.mApp, Constant.Umeng.Send_sms_latlng_success);
- break;
- case Activity.RESULT_CANCELED:
- default:
- // // CrashReport.postCatchedException(new Throwable(getResultData() + getResultCode()));
- break;
- }
- }
- }, new IntentFilter(DELIVERED));
- SmsManager smsm = SmsManager.getDefault();
- if(Build.VERSION.SDK_INT>= 22) {
- try {
- List<SubscriptionInfo> mSubInfoList = SubscriptionManager.from(this).getActiveSubscriptionInfoList();
- int mSubCount = (mSubInfoList != null && !mSubInfoList.isEmpty()) ? mSubInfoList.size() : 0;
- int subId = -1;
- if (mSubCount != 0) {
- subId = (int) mSubInfoList.get(0).getSubscriptionId();
- }
- smsm = SmsManager.getSmsManagerForSubscriptionId(subId);
- } catch (Throwable e) {
- e.printStackTrace();
- // // CrashReport.postCatchedException(e);
- }
- }
- try {
- if (message.length()> 70) {
- ArrayList<String> msgs = smsm.divideMessage(message);
- ArrayList<PendingIntent> sentIntents = new ArrayList<PendingIntent>();
- for(int i = 0;i<msgs.size();i++){
- sentIntents.add(sentPI);
- }
- smsm.sendMultipartTextMessage(phoneNum, null, msgs, sentIntents, null);
- } else {
- smsm.sendTextMessage(phoneNum, null, message, sentPI, deliveredPI);
- }
- // smsm.sendTextMessage(phoneNum, null, message, sentPI, deliveredPI);
- } catch (Throwable e) {
- System.out.println("-------------" + e.getMessage());
- e.printStackTrace();
- // // CrashReport.postCatchedException(e);
- }
- }
参考之前文章: 8.[小萌伴 Android] 手电筒功能及其实现
注意: 该功能需要另一台手机的辅助, 才能找到手机(后面即将介绍的语音找手机则不需要另一台手机辅助)
简书: ThinkinLiu 博客: IT 老五
IT 老五(it-lao5): 关注公众号, 一起源创, 一起学习!
相关内容:
来源: http://www.jianshu.com/p/c659c7df01c0