Changeset View
Changeset View
Standalone View
Standalone View
src/org/kde/kdeconnect/Plugins/TelephonyPlugin/TelephonyPlugin.java
Show All 22 Lines | |||||
23 | import android.Manifest; | 23 | import android.Manifest; | ||
24 | import android.content.BroadcastReceiver; | 24 | import android.content.BroadcastReceiver; | ||
25 | import android.content.Context; | 25 | import android.content.Context; | ||
26 | import android.content.Intent; | 26 | import android.content.Intent; | ||
27 | import android.content.IntentFilter; | 27 | import android.content.IntentFilter; | ||
28 | import android.content.SharedPreferences; | 28 | import android.content.SharedPreferences; | ||
29 | import android.content.pm.PackageManager; | 29 | import android.content.pm.PackageManager; | ||
30 | import android.media.AudioManager; | 30 | import android.media.AudioManager; | ||
31 | import android.net.Network; | ||||
31 | import android.os.Build; | 32 | import android.os.Build; | ||
32 | import android.os.Bundle; | 33 | import android.os.Bundle; | ||
33 | import android.preference.PreferenceManager; | 34 | import android.preference.PreferenceManager; | ||
34 | import android.support.v4.content.ContextCompat; | 35 | import android.support.v4.content.ContextCompat; | ||
35 | import android.telephony.PhoneNumberUtils; | 36 | import android.telephony.PhoneNumberUtils; | ||
36 | import android.telephony.SmsMessage; | 37 | import android.telephony.SmsMessage; | ||
37 | import android.telephony.TelephonyManager; | 38 | import android.telephony.TelephonyManager; | ||
38 | import android.util.Log; | 39 | import android.util.Log; | ||
39 | 40 | | |||
41 | import org.json.JSONArray; | ||||
42 | import org.json.JSONException; | ||||
43 | import org.json.JSONObject; | ||||
40 | import org.kde.kdeconnect.Helpers.ContactsHelper; | 44 | import org.kde.kdeconnect.Helpers.ContactsHelper; | ||
45 | import org.kde.kdeconnect.Helpers.SMSHelper; | ||||
46 | import org.kde.kdeconnect.Helpers.SMSHelper.ThreadID; | ||||
47 | import org.kde.kdeconnect.Helpers.SMSHelper.Message; | ||||
41 | import org.kde.kdeconnect.NetworkPacket; | 48 | import org.kde.kdeconnect.NetworkPacket; | ||
42 | import org.kde.kdeconnect.Plugins.Plugin; | 49 | import org.kde.kdeconnect.Plugins.Plugin; | ||
43 | import org.kde.kdeconnect_tp.BuildConfig; | 50 | import org.kde.kdeconnect_tp.BuildConfig; | ||
44 | import org.kde.kdeconnect_tp.R; | 51 | import org.kde.kdeconnect_tp.R; | ||
45 | 52 | | |||
46 | import java.util.ArrayList; | 53 | import java.util.ArrayList; | ||
54 | import java.util.List; | ||||
47 | import java.util.Map; | 55 | import java.util.Map; | ||
48 | import java.util.Timer; | 56 | import java.util.Timer; | ||
49 | import java.util.TimerTask; | 57 | import java.util.TimerTask; | ||
50 | 58 | | |||
51 | public class TelephonyPlugin extends Plugin { | 59 | public class TelephonyPlugin extends Plugin { | ||
52 | 60 | | |||
61 | /** | ||||
62 | * Packet used to indicate a batch of messages has been pushed from the remote device | ||||
63 | * | ||||
64 | * The body should contain the key "messages" mapping to an array of messages | ||||
65 | * | ||||
66 | * For example: | ||||
67 | * { "messages" : [ | ||||
68 | * { "event" : "sms", | ||||
69 | * "messageBody" : "Hello", | ||||
70 | * "phoneNumber" : "2021234567", | ||||
71 | * "messageDate" : "1518846484880", | ||||
72 | * "messageType" : "2", | ||||
73 | * "threadID" : "132" | ||||
74 | * }, | ||||
75 | * { ... }, | ||||
76 | * ... | ||||
77 | * ] | ||||
78 | */ | ||||
79 | private final static String PACKET_TYPE_TELEPHONY_MESSAGE = "kdeconnect.telephony.message"; | ||||
80 | | ||||
81 | /** | ||||
82 | * Packet used for simple telephony events | ||||
83 | * | ||||
84 | * It contains the key "event" which maps to a string indicating the type of event: | ||||
85 | * - "ringing" - A phone call is incoming | ||||
86 | * - "missedCall" - An incoming call was not answered | ||||
87 | * - "sms" - An incoming SMS message | ||||
88 | * - Note: As of this writing (15 May 2018) the SMS interface is being improved and this type of event | ||||
89 | * is no longer the preferred way of retrieving SMS. Use PACKET_TYPE_TELEPHONY_MESSAGE instead. | ||||
90 | * | ||||
91 | * Depending on the event, other fields may be defined | ||||
92 | */ | ||||
53 | private final static String PACKET_TYPE_TELEPHONY = "kdeconnect.telephony"; | 93 | private final static String PACKET_TYPE_TELEPHONY = "kdeconnect.telephony"; | ||
54 | public final static String PACKET_TYPE_TELEPHONY_REQUEST = "kdeconnect.telephony.request"; | 94 | public final static String PACKET_TYPE_TELEPHONY_REQUEST = "kdeconnect.telephony.request"; | ||
55 | private static final String KEY_PREF_BLOCKED_NUMBERS = "telephony_blocked_numbers"; | 95 | private static final String KEY_PREF_BLOCKED_NUMBERS = "telephony_blocked_numbers"; | ||
56 | 96 | | |||
97 | /** | ||||
98 | * Packet sent to request all conversations | ||||
99 | * | ||||
100 | * The request packet shall contain no body | ||||
101 | */ | ||||
102 | public final static String PACKET_TYPE_TELEPHONY_REQUEST_CONVERSATIONS = "kdeconnect.telephony.request_conversations"; | ||||
103 | | ||||
104 | /** | ||||
105 | * Packet sent to request all the messages in a particular conversation | ||||
106 | * | ||||
107 | * The body should contain the key "threadID" mapping to the threadID (as a string) being requested | ||||
108 | * For example: | ||||
109 | * { "threadID": 203 } | ||||
110 | */ | ||||
111 | public final static String PACKET_TYPE_TELEPHONY_REQUEST_CONVERSATION = "kdeconnect.telephony.request_conversation"; | ||||
112 | | ||||
57 | private int lastState = TelephonyManager.CALL_STATE_IDLE; | 113 | private int lastState = TelephonyManager.CALL_STATE_IDLE; | ||
58 | private NetworkPacket lastPacket = null; | 114 | private NetworkPacket lastPacket = null; | ||
59 | private boolean isMuted = false; | 115 | private boolean isMuted = false; | ||
60 | 116 | | |||
61 | private final BroadcastReceiver receiver = new BroadcastReceiver() { | 117 | private final BroadcastReceiver receiver = new BroadcastReceiver() { | ||
62 | @Override | 118 | @Override | ||
63 | public void onReceive(Context context, Intent intent) { | 119 | public void onReceive(Context context, Intent intent) { | ||
64 | 120 | | |||
▲ Show 20 Lines • Show All 214 Lines • ▼ Show 20 Line(s) | |||||
279 | 335 | | |||
280 | @Override | 336 | @Override | ||
281 | public void onDestroy() { | 337 | public void onDestroy() { | ||
282 | context.unregisterReceiver(receiver); | 338 | context.unregisterReceiver(receiver); | ||
283 | } | 339 | } | ||
284 | 340 | | |||
285 | @Override | 341 | @Override | ||
286 | public boolean onPacketReceived(NetworkPacket np) { | 342 | public boolean onPacketReceived(NetworkPacket np) { | ||
343 | if (np.getType().equals(PACKET_TYPE_TELEPHONY_REQUEST_CONVERSATIONS)) { | ||||
344 | return this.handleRequestConversations(np); | ||||
345 | } | ||||
346 | else if (np.getType().equals(PACKET_TYPE_TELEPHONY_REQUEST_CONVERSATION)) { | ||||
347 | return this.handleRequestConversation(np); | ||||
348 | } | ||||
287 | if (np.getString("action").equals("mute")) { | 349 | if (np.getString("action").equals("mute")) { | ||
288 | if (!isMuted) { | 350 | if (!isMuted) { | ||
289 | AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); | 351 | AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); | ||
290 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { | 352 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { | ||
291 | am.setStreamVolume(AudioManager.STREAM_RING, AudioManager.ADJUST_MUTE, 0); | 353 | am.setStreamVolume(AudioManager.STREAM_RING, AudioManager.ADJUST_MUTE, 0); | ||
292 | } else { | 354 | } else { | ||
293 | am.setStreamMute(AudioManager.STREAM_RING, true); | 355 | am.setStreamMute(AudioManager.STREAM_RING, true); | ||
294 | } | 356 | } | ||
Show All 11 Lines | 364 | private boolean isNumberBlocked(String number) { | |||
306 | for (String s : blockedNumbers) { | 368 | for (String s : blockedNumbers) { | ||
307 | if (PhoneNumberUtils.compare(number, s)) | 369 | if (PhoneNumberUtils.compare(number, s)) | ||
308 | return true; | 370 | return true; | ||
309 | } | 371 | } | ||
310 | 372 | | |||
311 | return false; | 373 | return false; | ||
312 | } | 374 | } | ||
313 | 375 | | |||
376 | /** | ||||
377 | * Respond to a request for all conversations | ||||
378 | * | ||||
379 | * Send one packet of type PACKET_TYPE_TELEPHONY_MESSAGE with the first message in all conversations | ||||
380 | */ | ||||
381 | protected boolean handleRequestConversations(NetworkPacket packet) { | ||||
382 | Map<ThreadID, Message> conversations = SMSHelper.getConversations(this.context); | ||||
383 | | ||||
384 | NetworkPacket reply = new NetworkPacket(PACKET_TYPE_TELEPHONY_MESSAGE); | ||||
385 | | ||||
386 | JSONArray messages = new JSONArray(); | ||||
387 | | ||||
388 | for (Message message : conversations.values()) { | ||||
389 | try { | ||||
390 | JSONObject json = message.toJSONObject(); | ||||
391 | | ||||
392 | json.put("event", "sms"); | ||||
393 | | ||||
394 | messages.put(json); | ||||
395 | } catch (JSONException e) | ||||
396 | { | ||||
397 | Log.e("Conversations", "Error serializing message"); | ||||
398 | } | ||||
399 | } | ||||
400 | | ||||
401 | reply.set("messages", messages); | ||||
402 | reply.set("event", "batch_messages"); // Not really necessary, since this is implied by PACKET_TYPE_TELEPHONY_MESSAGE, but good for readability | ||||
403 | | ||||
404 | device.sendPacket(reply); | ||||
405 | | ||||
406 | return true; | ||||
407 | } | ||||
408 | | ||||
409 | protected boolean handleRequestConversation(NetworkPacket packet) { | ||||
410 | ThreadID threadID = new ThreadID(packet.getInt("threadID")); | ||||
411 | | ||||
412 | List<Message> conversation = SMSHelper.getMessagesInThread(this.context, threadID); | ||||
413 | | ||||
414 | NetworkPacket reply = new NetworkPacket(PACKET_TYPE_TELEPHONY_MESSAGE); | ||||
415 | | ||||
416 | JSONArray messages = new JSONArray(); | ||||
417 | | ||||
418 | for (Message message : conversation) { | ||||
419 | try { | ||||
420 | JSONObject json = message.toJSONObject(); | ||||
421 | | ||||
422 | json.put("event", "sms"); | ||||
423 | | ||||
424 | messages.put(json); | ||||
425 | } catch (JSONException e) | ||||
426 | { | ||||
427 | Log.e("Conversations", "Error serializing message"); | ||||
428 | } | ||||
429 | } | ||||
430 | | ||||
431 | reply.set("messages", messages); | ||||
432 | reply.set("event", "batch_messages"); | ||||
433 | | ||||
434 | device.sendPacket(reply); | ||||
435 | | ||||
436 | return true; | ||||
437 | } | ||||
438 | | ||||
314 | @Override | 439 | @Override | ||
315 | public String[] getSupportedPacketTypes() { | 440 | public String[] getSupportedPacketTypes() { | ||
316 | return new String[]{PACKET_TYPE_TELEPHONY_REQUEST}; | 441 | return new String[]{ | ||
442 | PACKET_TYPE_TELEPHONY_REQUEST, | ||||
443 | PACKET_TYPE_TELEPHONY_REQUEST_CONVERSATIONS, | ||||
444 | PACKET_TYPE_TELEPHONY_REQUEST_CONVERSATION, | ||||
445 | }; | ||||
317 | } | 446 | } | ||
318 | 447 | | |||
319 | @Override | 448 | @Override | ||
320 | public String[] getOutgoingPacketTypes() { | 449 | public String[] getOutgoingPacketTypes() { | ||
321 | return new String[]{PACKET_TYPE_TELEPHONY}; | 450 | return new String[]{ | ||
451 | PACKET_TYPE_TELEPHONY, | ||||
452 | PACKET_TYPE_TELEPHONY_MESSAGE, | ||||
453 | }; | ||||
322 | } | 454 | } | ||
323 | 455 | | |||
324 | @Override | 456 | @Override | ||
325 | public String[] getRequiredPermissions() { | 457 | public String[] getRequiredPermissions() { | ||
326 | return new String[]{Manifest.permission.READ_PHONE_STATE, Manifest.permission.READ_SMS}; | 458 | return new String[]{Manifest.permission.READ_PHONE_STATE, Manifest.permission.READ_SMS}; | ||
327 | } | 459 | } | ||
328 | 460 | | |||
329 | @Override | 461 | @Override | ||
Show All 9 Lines |