📜 ⬆️ ⬇️

Forward notifications of incoming calls and SMS to the computer

Very often being at home behind a laptop, I missed incoming calls, since the phone was out of earshot. Then the idea was born, why not teach a laptop to show information about calls from the phone?

And if you also send incoming SMS, then you can simplify the work with online services that send one-time confirmation codes for various operations. To view this code, you will not need to search for the phone and unlock it. All information will be available immediately on the laptop screen.

In addition, this is a good opportunity to practice in the development of an android. The result is a small application that intercepts incoming calls, SMS and sends them over the local network.
')
For those who are interested in how I did it, welcome to the cat.

A small demonstration of the work to understand what it is about (I apologize for the quality, there is no normal technology for video recording)



Broadcast Receivers


The first thing we need to intercept calls and SMS is to add the appropriate permissions in AndroidManifest.xml:

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

But permissions are not enough. More need receivers broadcast messages. These are special objects that are executed when receiving any special signal. In order for the system to know that we have an object to which we need to send a message, it must be registered.

Of course, this can be done by adding the corresponding lines in AndroidManifest.xml:

 <receiver android:name="MessageReceiver" android:enabled="true"> <intent-filter> <action android:name="android.provider.Telephony.SMS_RECEIVED"/> </intent-filter> </receiver> <receiver android:name="CallReceiver"> <intent-filter> <action android:name="android.intent.action.PHONE_STATE"/> </intent-filter> </receiver> 

But if we want to be able to turn on and off our BroadcastReceiver, then we can register them dynamically:

 //     myMessageReceiver = new MessageReceiver(); //     IntentFilter e = new IntentFilter(); //     e.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); //    e.addAction("android.provider.Telephony.SMS_RECEIVED"); //      registerReceiver(myMessageReceiver, e); //        unregisterReceiver(myMessageReceiver); 

But if we create a BroadcastReceiver in the code of the main screen form (Activity), then they will work only when the application is active (not paused and not stopped). Our application should always remain in the background. For this there are background services:

 //     Notification notif = new Notification(R.drawable.ic_launcher, "Notice service started", System.currentTimeMillis()); //       (Activity) Intent intent1 = new Intent(this, NoticeSetting.class); PendingIntent pIntent = PendingIntent.getActivity(this, 0, intent1, 0); //   notif.setLatestEventInfo(this, "Notice service enabled", "For stopped it click me", pIntent); //  ,       notif.flags |= Notification.FLAG_ONGOING_EVENT ; //   startForeground(NOTIFICATION_ID, notif ); //    stopForeground(true); 

The service created in this way will in every possible way resist attempts to kill him. When launched, a notification will appear in the tray with the specified text and image. If you expand the tray and touch this notification, the specified Activity will start.

It is worth noting that the above method of creating a service notification is considered obsolete, and since version API level 11 (Android 3.0.x) you need to use the constructor Notification.Builder. But I did not use it for compatibility with older versions of Android.

It will also be useful to be able to start a background service at system startup, for this you can register another BroadcastReceiver:

 <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/> <receiver android:name=".BootBroadReceiv" android:enabled="true" android:exported="false" > <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> </intent-filter> </receiver> 

Now that we have figured out how and where to register a BroadcastReceiver, let's take a closer look at how to use them. You need to inherit your class from BroadcastReceiver and define the onReceive method in it, which is called when a broadcast message arrives:

 public class MessageReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { // -   } } 

Message preparation


Next, you need to prepare a message to send to the computer. The first thing that interests us is the phone number of the incoming call. You can get it by calling getStringExtra inside our onReceive:

 phoneNumber = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER); 

There are several ways to find the name of the caller in the phone book. I used this query:

 String Name=" "; phoneNumber = PhoneNumberUtils.stripSeparators(phoneNumber); String[] projection = new String[] { ContactsContract.Data.CONTACT_ID, ContactsContract.Contacts.LOOKUP_KEY, ContactsContract.Contacts.DISPLAY_NAME, ContactsContract.Contacts.STARRED, ContactsContract.Contacts.CONTACT_STATUS, ContactsContract.Contacts.CONTACT_PRESENCE }; String selection = "PHONE_NUMBERS_EQUAL(" + Phone.NUMBER + ",?) AND " + Data.MIMETYPE + "='" + Phone.CONTENT_ITEM_TYPE + "'"; String selectionArgs [] ={ phoneNumber }; Cursor cursor = context.getContentResolver().query(ContactsContract.Data.CONTENT_URI, projection, selection, selectionArgs, null); if(cursor.getCount()>0) { cursor.moveToFirst(); Name = cursor.getString(2) + " "; } else Name = "Unknow"; 


To work with contacts book you need to add the following resolution to AndroidManifest.xml

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

You can receive incoming SMS by placing the following code in onReceive:

 Bundle bundle = intent.getExtras(); if (bundle != null) { Object[] pdus = (Object[]) bundle.get("pdus"); SmsMessage[] msgs = new SmsMessage[pdus.length]; ArrayList<String> numbers = new ArrayList<String>(); ArrayList<String> messages = new ArrayList<String>(); //     for (int i=0; i<msgs.length; i++){ msgs[i] = SmsMessage.createFromPdu((byte[])pdus[i]); //   numbers.add(msgs[i].getOriginatingAddress()); //   messages.add(msgs[i].getMessageBody().toString()); } 

Posting a message


The phone is in the same LAN with a laptop and a computer, so we will use ordinary sockets to send the prepared message. The following code shows how to send a packet over a local network.
Attention! Sending an unencrypted message over the network with a UDP broadcast packet may not be secure.

 InetAddress serv_addr = null; serv_addr = InetAddress.getByName("255.255.255.255"); int port= 35876; DatagramSocket sock = null; sock = new DatagramSocket(); //mess -    byte [] buf = mess.getBytes(); DatagramPacket pack= new DatagramPacket(buf, buf.length,serv_addr,port); sock.send(pack); sock.close(); 

To be able to work with the network, add the resolution in AndroidManifest.xml

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

And here during the testing it turned out that the onReceive method in the BroadcastReceiver lives a very short time. If you put into it the function of determining the subscriber's name by number and the function of sending a message over the network, the system will kill it before both functions are performed. That is characteristic, in the version of Android 2.3. * This code successfully managed to work, and in versions 4. * no. Therefore, long-term operations need to be performed in a separate service launched by the startService () function.

Receive messages


To receive a message, we will write a small C program using all the same sockets:

 int main() { int z; char srvr_addr[8] = "0.0.0.0"; struct sockaddr_in adr_inet; // AF_INET struct sockaddr_in adr_clnt; // AF_INET socklen_t len_inet; int s; //    char dgram[512]; //  s = socket(AF_INET,SOCK_DGRAM,0); if ( s == -1 ) { displayError("socket()"); } memset(&adr_inet,0,sizeof(adr_inet)); adr_inet.sin_family = AF_INET; adr_inet.sin_port = htons(35876); adr_inet.sin_addr.s_addr =inet_addr(srvr_addr); if ( adr_inet.sin_addr.s_addr == INADDR_NONE ) { displayError("bad address."); } len_inet = sizeof(adr_inet); //  z = bind(s, (struct sockaddr *)&adr_inet, len_inet); if ( z == -1 ) { displayError("bind()"); } while(1) { len_inet = sizeof adr_clnt; //   z = recvfrom(s, dgram, 512, 0, (struct sockaddr *)&adr_clnt, &len_inet); if ( z < 0 ) { displayError("recvfrom(2)"); } dgram[z] = 0; // null terminate if ( !strcasecmp(dgram,"QUIT") ) { break; // Quit server } //dgram    printf("%s\n",dgram); 

But getting the message is not enough, you need to somehow show it to yourself. In linux, there is a convenient console utility notify-send, which accepts a message as an argument and displays it to the user through the kde or gnome notification panel. Using it you can display a message in a couple of lines:

 if (fork() == 0) { execl("/usr/bin/notify-send","notify-send","   :", dgram, (char *) 0); perror("exec one failed"); exit(1); } 

You can add the received application to autoload in your favorite way.

Links to the topic


Source: https://habr.com/ru/post/211921/


All Articles