伊人博客 一款有技术又有生活的博客

【安卓】如何使用广播和内容提供者来叫醒小伙伴(控制你的朋友)

发布时间:2017-03-09 18:19:06 标签:安卓项目 | 阅读(7504)

正文:

  试想:你会不会有这样一个时候,因为某些原因,你的朋友喜欢将手机设置为震动、甚至是静音,而且全天都是这样。然后当你有急事找你的朋友需要打电话时,发现他(她)死活不接听,其实不是他(她)不想接,很多时候是因为他没有听到而已。我就遇到过这样的问题,那天我站在寒冷的女生宿舍楼下给女友打电话,结果女友睡过头了,愣是打了半个小时电话没人接,因为她喜欢手机静音,这样的场景发生过多次,本人很是受伤,于是,我开始寻找方法能够快速准确的叫醒女朋友,最终这款小应用产生了,取名为《LoveRing》。

  为了简单有效,我首先想到了使用短信来控制对方手机(安卓),给对方手机上装上定时炸弹(LoveRing),当对方手机接收到对应的代号后,对方的手机将会想起音乐,并弹出对话框,直至对方醒来点击按钮方可终止音乐。总体设计思想就是这样。

  为了监控短信消息,我首先想到了监听短信广播(优先级问题,可能会无效,后边使用其他方法),经过简单设计后,我开始风风火火的编码:

  首先想到了最简单的方法:静态广播,然而静态广播似乎不太好用,我发现静态广播的优先级虽然也可以设置,但是经过测试发现,静态广播接收器的优先级低于动态广播接收器的优先级。下边把静态广播接收的方法写下来(我尝试过,大家也可以尝试下):

  首先添加接收,发送短信等权限:

<uses-permission android:name="android.permission.SEND_SMS"/><uses-permission android:name="android.permission.RECEIVE_SMS"/><uses-permission android:name="android.permission.READ_SMS" /><uses-permission android:name="android.permission.WRITE_SMS" />

 

  注册广播接收器,接收短信广播:

复制代码

<receiver android:name="yxxrui.com.wakeup.MyBroadcastReceiver"android:enabled="true"><intent-filter android:priority="2147483647"><action android:name="android.provider.Telephony.SMS_RECEIVED"/></intent-filter></receiver>

复制代码

   然后开始编写广播接收器类(为了方便,我在代码中写注释来帮助理解):

复制代码

  1 public class MyBroadcastReceiver extends BroadcastReceiver {  2     private static final String strRes = "android.provider.Telephony.SMS_RECEIVED";  3     private static MediaPlayer music = null;//用来播放声音  4     private static int status = 1;//当前状态,是否已经处理过,由于后边将动态广播与静态广播放到了一起,故可能接收到两次广播,此处做处理  5     private static AudioManager am = null;  6     private static int musicVolume = 0;  7     private int maxVolum = 50;  8     @Override  9     public void onReceive(Context context, Intent intent) { 10         if(strRes.equals(intent.getAction())){ 11             //收到短信 12             Bundle bundle = intent.getExtras(); 13             if(bundle!=null){ 14                 Object[] pdus = (Object[])bundle.get("pdus"); 15                 SmsMessage[] msg = new SmsMessage[pdus.length]; 16                 for(int i = 0 ;i<pdus.length;i++){ 17                     msg[i] = SmsMessage.createFromPdu((byte[])pdus[i]); 18                 } 19                 for(SmsMessage curMsg:msg){ 20                     String address =  curMsg.getDisplayOriginatingAddress(); 21                     long time = curMsg.getTimestampMillis(); 22                     String content = curMsg.getDisplayMessageBody(); 23                     dealMsg(context, address, time, content);//处理短信 24                 } 25             } 26         }else if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)){ 27             //开机完毕 28             Intent smsIntent=new Intent(context, SmsBindService.class); 29             context.startService(smsIntent); 30              31             Intent socketIntent=new Intent(context, SocketPortBindService.class); 32             context.startService(socketIntent); 33         } 34     } 35  36     /** 37      * 为了安全,防止坏人任意唤起音乐,此处简单设计了密码,大家可以对此处做优化,加密处理 38      */ 39     private String pswStr = null;//密码 40     private String hintStr = null;//弹框中显示的内容 41     private String codeStr = null;//代号,如:#GetUp# 42     private String numberStr = null;//发短信手机号码 43     private String feedbackStr = null;//自动回复的短信内容,方便接收到信息后快速回复信息 44     private int volume = 70;//音乐声音大小 45  46     /** 47      * 处理短信,验证是否为命令 48      * @param context 上下文 49      * @param address 手机号码 50      * @param time 发送时间 51      * @param content 发送的内容 52      */ 53     public void dealMsg(Context context, String address, long time, String content){ 54         try{ 55             final String msgContent = content; 56             //基本的参数是允许用户自己修改的,保存到SharedPreferences中 57             SharedPreferences prefer = context.getSharedPreferences("yxxrui", 
 58                     context.MODE_PRIVATE); 59             pswStr = prefer.getString("psw", 
 60                     g(context,R.string.psw_content_default)); 61             codeStr = prefer.getString("code", 
 62                     g(context,R.string.code_content_default)); 63             feedbackStr = prefer.getString("feedback", 
 64                     g(context,R.string.feedback_content_default)); 65             hintStr = prefer.getString("hint", 
 66                     g(context,R.string.hint_content_default)); 67             double percent = prefer.getInt("volume", 70); 68             if(am==null){ 69                 am = (AudioManager)context.getSystemService(context.AUDIO_SERVICE); 70                 maxVolum = am.getStreamMaxVolume(AudioManager.STREAM_SYSTEM); 71             } 72             //此处需要注意:一开始我认为最大音量就是100,然后可以随意设置大小,结果翻阅文档和测试发现音量大小各手机不同 73             //其实相当于你点几次音量+键,就共有多少个档位,我的手机为7,故此处需要计算百分比 74             volume = (int) Math.floor(maxVolum * (percent/100)); 75             //此处暗藏一个命令,将对方手机开启标准模式,方便你打电话联系,哈哈哈(应该不算犯规吧!) 76             if(content.equals("#RingCall#456852")){ 77                 am.setRingerMode(AudioManager.RINGER_MODE_NORMAL); 78                 am.setStreamVolume(AudioManager.STREAM_RING, 50, 0); 79                 deleteSMS(context, msgContent); 80             }else if(content.startsWith(codeStr)){ 81                 //若短信的代号与本机所设置的代号相同的话,初步判定是命令,那么继续操作 82                 content = content.replace(codeStr, ""); 83                 //切分出密码,使用的是两个英文逗号分割 84                 String[] result = content.split(",,"); 85                 //SimpleDateFormat formatter = new SimpleDateFormat("MMddHHmm"); 86                 //Date curDate = new Date(time); 87                 //int qustion = Math.abs(12240214 - Integer.parseInt(formatter.format(curDate))); 88                 String answer = ""; 89                 if(result.length>=1){ 90                     answer = result[0]; 91                 } 92                 if(answer.equals(pswStr)/*Math.abs(qustion - answer) < 5*/){ 93                     //验证通过后,可以进行真正工作了 94                     if(status == 2){//播放过了已经 95                         return; 96                     } 97                     try{ 98                         this.abortBroadcast(); 99                     }catch(Exception ex){100                         101                     }102                     numberStr = address;103                     //此处将收到的命令删除(放心,绝对不删除其他短信)104                     deleteSMS(context, msgContent);105                     //播放音乐106                     PlayMusic(context);107                     status = 2;108                     //弹出对话框,显示温馨提示,温馨提示可以有发送者定义,默认为本机设置的提示语109                     String str = result.length >= 2 ? result[1] : hintStr;110                     showDialog(context,str);111                 }112             }113         }catch(Exception e){114             Toast.makeText(context,e.toString(),Toast.LENGTH_LONG).show();115         }116     }117     protected String g(Context context, int id){118         return context.getResources().getString(id);119     }120     private void showDialog(Context context,String content){121         AlertDialog.Builder builder = new AlertDialog.Builder(context);122         builder.setMessage(content)123             .setTitle(g(context,R.string.hint_title))124             .setCancelable(false)125             .setPositiveButton(g(context,R.string.hint_btn_not_reply), new DialogInterface.OnClickListener() {126                 public void onClick(DialogInterface dialog, int id) {127                     if(music!=null){128                         music.pause();129                         music.stop();130                     }131                     recover();132                     dialog.cancel();133                    } 
134                })135                .setNegativeButton(g(context,R.string.hint_btn_reply), new DialogInterface.OnClickListener() {136                     public void onClick(DialogInterface dialog, int id) {137                         //暂时这么处理,之后发送短信回复已起床138                         if(music!=null){139                             music.pause();140                             music.stop();141                         }142                         recover();143                         sendMessage(numberStr, feedbackStr);144                         dialog.cancel();145                    } 
146                });147         AlertDialog alert = builder.create(); 
148         alert.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);149         alert.show();150     }151     private void sendMessage(String number, String msg){152         SmsManager smsManager = SmsManager.getDefault();153         if(msg.length() > 70) {154             List<String> contents = smsManager.divideMessage(msg);155             for(String sms : contents) {156                 smsManager.sendTextMessage(number, null, sms, null, null);157             }158         } else {159             smsManager.sendTextMessage(number, null, msg, null, null);160         }161     }162     private Vibrator vibrator = null;163     protected void PlayMusic(final Context context) {164         vibrator = (Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE);  
165         final long [] pattern = {2000,500,150,500};   // 停止 开启 停止 开启   166         /*vibrator.vibrate(pattern,2);           //重复两次上面的pattern 如果只想震动一次,index设为-1167 */        Thread th = new Thread(new Runnable(){168             @Override169             public void run() {170                 int MusicId = R.raw.wakeup1;171                 //保存当前音量,播放完后恢复回去172                 //systemVolume = am.getStreamVolume(AudioManager.STREAM_SYSTEM);173                 musicVolume = am.getStreamVolume(AudioManager.STREAM_MUSIC);174                 //am.setStreamVolume(AudioManager.STREAM_SYSTEM, volume, AudioManager.FLAG_PLAY_SOUND);175                 am.setStreamVolume(AudioManager.STREAM_MUSIC, volume, AudioManager.FLAG_PLAY_SOUND);176                 if(music!=null&&music.isPlaying()){177                     return;178                 }179                 music = MediaPlayer.create(context, MusicId);180                 music.setLooping(true);181                 music.setOnCompletionListener(new OnCompletionListener(){182                     @Override183                     public void onCompletion(MediaPlayer mp) {184                         recover();185                     }186                 });187                 vibrator.vibrate(pattern,0);188                 music.start();189             }190         });191         th.start();192         /*Toast.makeText(context, systemVolume+"", Toast.LENGTH_LONG).show();193         Toast.makeText(context, musicVolume+"", Toast.LENGTH_LONG).show();*/194     }195     private void recover(){196         am.setStreamVolume(AudioManager.STREAM_MUSIC, musicVolume, 0);197         //am.setStreamVolume(AudioManager.STREAM_SYSTEM, systemVolume, 0);198         status = 1;//恢复原状199         vibrator.cancel();200     }201     public void deleteSMS(Context context, String smscontent)202     {203         /*所有文件夹:content://sms/all 
204            收件箱:content://sms/inbox 
205            已发送:content://sms/sent 
206            草稿:content://sms/draft 
207            发件箱:content://sms/outbox 
208            发送失败:content://sms/failed 
209            排队消息:content://sms/queued 
210            未送达:content://sms/undelivered 
211            对话:content://sms/conversations*/212         try{213             // 准备系统短信收信箱的uri地址214             Uri uri = Uri.parse("content://sms");// 全部,只要符合条件,全部删除215             // 查询收信箱里所有的短信216             ContentResolver cR = context.getContentResolver();217             Cursor isRead =    cR.query(uri, null, "read<=" + 1,null, null);218             while (isRead.moveToNext()){219                 // String phone =220                 // isRead.getString(isRead.getColumnIndex("address")).trim();//获取发信人221                 String body =222                   isRead.getString(isRead.getColumnIndex("body")).trim();// 获取信息内容223                 if (body.equals(smscontent)){224                     int id = isRead.getInt(isRead.getColumnIndex("_id"));        
225                     cR.delete(Uri.parse("content://sms"), "_id=" + id, null);226                 }227             }228         }catch (Exception e){229             Toast.makeText(context, "delete Failed! "+e.toString(), Toast.LENGTH_SHORT);230         }231     }232 }

复制代码

 

   优先级不高,经常不响,于是改成了动态广播,为了更加有效,需要设置开机监听,开机启动service,然后在service中监听广播,收到广播后依旧发送至上边写的广播接收器中,方便:

  添加开机启动广播权限

<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

  修改上边的广播注册器:在filter中添加一条开机事件:

复制代码

<receiverandroid:name="yxxrui.com.wakeup.MyBroadcastReceiver"android:enabled="true"><intent-filter android:priority="2147483647"><action android:name="android.intent.action.BOOT_COMPLETED" /><action android:name="android.provider.Telephony.SMS_RECEIVED"/></intent-filter></receiver>

复制代码

 

  其实上边的广播接收类中已经写好了开机事件,只是上边的事件中需要启动一个service,而service还没有写上去:这里贴上:

  首先,注册服务:

<service android:name="yxxrui.com.wakeup.SmsBindService"android:priority="1000"></service>

 

  然后,简单粗暴,贴service代码:

复制代码

 1 public class SmsBindService extends Service{ 2     private MyBroadcastReceiver receiver; 3     @Override 4     public IBinder onBind(Intent intent) { 5         return null; 6     } 7      8     @Override 9     public void onCreate(){10         super.onCreate();11         IntentFilter filter = new IntentFilter("android.provider.Telephony.SMS_RECEIVED");12         filter.setPriority(2147483647);13         receiver =new MyBroadcastReceiver();14         registerReceiver(receiver, filter);       
15     }16         @Override17     public int onStartCommand(Intent intent,int flags, int startId){18         flags = START_STICKY;19         return super.onStartCommand(intent, flags, startId);20     }21     22     @Override23     public void onDestroy(){24         super.onDestroy();25         //Toast.makeText(this, "WakeUp service has been stoped", Toast.LENGTH_SHORT).show();26         Intent localIntent = new Intent();27         localIntent.setClass(this, SmsBindService.class); //销毁时重新启动Service28         this.startService(localIntent);29         unregisterReceiver(receiver);30     }31 }

复制代码

  当然,主界面 很水,代码就不再粘贴,这里只是对一些参数进行设置而已,没有什么技术含量:

到此,动态,静态均已完成,但是这样写好以后真的可以实现所有短信都能检测到吗?不,不可能,因为有一些大佬的短信广播永远比你厉害,这样的应用会经常失效,如何才能解决这个问题呢?其实就是题目中说的内容提供者,我这里直接叠加到上边的代码中,也就是一个应用使用三种检测短信的方式:静态广播,动态广播,内容提供者。

 

  现在在SmsBindService 类中添加一个内部类,用于检测内容提供者的数据库改变事件,当数据库的内容发生改变时,系统会自动回调这个onChange() 方法,让后再onChange()方法中获取未读信息,并验证是否为命令,再做处理,我在这里的处理方法仍然是使用上边写好的短信处理方法

 

class MyContentObserver extends ContentObserver{

        public MyContentObserver(Handler handler) {

            super(handler);

        }         

        //当被监听的内容发生改变时回调

        @Override

        public void onChange(boolean selfChange) {

            if(selfChange){

                return;

            }

            Uri uri = Uri.parse("content://sms/inbox");  //收件箱uri

            //查看发件箱内容

            ContentResolver resolver = getContentResolver();

            Cursor cursor = resolver.query(uri, 

                    new String[]{"address","date","body"}, 

                    "read <=" + 1, null, "date desc");

            if(cursor!=null && cursor.getCount()>0){

                long now = System.currentTimeMillis();

                String address;

                long time;

                String content;

                //只看前5条,2分钟内的短信

                int n = 0;

                while(n<5 && cursor.moveToNext()){

                    address = cursor.getString(0);

                    time = cursor.getInt(1);

                    content = cursor.getString(2);

                    if(Math.abs(now-time) > 5*60*1000){

                        receiver.dealMsg(SmsBindService.this, address, time, content);

                    }

                    n++;

                }

                cursor.close();

            }

        }

    }

 

  最后修改上边SmsBindService 的代码,在onCreate()方法末尾添加以下代码即可:

 

ContentResolver resolver = getContentResolver();

//注册一个内容观察者观察短信数据库

resolver.registerContentObserver(

                Uri.parse("content://sms/"), true, new MyContentObserver(new Handler()));

 

  有问题可以联系我,我的邮箱是:yxxrui@163.com,我的网址是:http://www.yxxrui.cn

原创文章如转载,请注明出处“伊人博客

评论列表+
  • 2017-03-15 金华市网友:10个6,SDF是打错了
  • 2017-03-15 金华市网友:SDF
站内搜索
手机上看
广而告之

文章归档
热门推荐
随机推荐
冷门晾晒
热门资讯