cac04f7a14f3130d300f274bf6f4bff862fdedd8
[fonbot.git] / src / ro / ieval / fonbot / Heavy.java
1 package ro.ieval.fonbot;
2
3 import static ro.ieval.fonbot.R.string.*;
4 import static ro.ieval.fonbot.Utils.toNonNull;
5
6 import java.io.File;
7 import java.io.FileInputStream;
8 import java.io.FileOutputStream;
9 import java.io.FileNotFoundException;
10 import java.io.InputStream;
11 import java.io.IOException;
12 import java.io.OutputStream;
13 import java.lang.reflect.InvocationTargetException;
14 import java.lang.reflect.Method;
15 import java.net.InetSocketAddress;
16 import java.net.Socket;
17 import java.net.UnknownHostException;
18 import java.net.URL;
19 import java.net.URLConnection;
20 import java.nio.channels.FileChannel;
21 import java.nio.channels.SocketChannel;
22 import java.util.ArrayList;
23 import java.util.Date;
24
25 import org.eclipse.jdt.annotation.Nullable;
26
27 import ro.ieval.fonbot.Utils.Command;
28 import ro.ieval.fonbot.Utils.MessageType;
29 import ro.ieval.fonbot.Utils.OngoingEvent;
30 import ro.ieval.fonbot.Utils.RingerMode;
31 import ro.ieval.fonbot.Utils.WipeType;
32 import android.annotation.SuppressLint;
33 import android.app.AlarmManager;
34 import android.app.NotificationManager;
35 import android.app.PendingIntent;
36 import android.app.admin.DevicePolicyManager;
37 import android.bluetooth.BluetoothAdapter;
38 import android.content.ActivityNotFoundException;
39 import android.content.Context;
40 import android.content.Intent;
41 import android.content.IntentFilter;
42 import android.database.Cursor;
43 import android.graphics.ImageFormat;
44 import android.hardware.Camera;
45 import android.hardware.Camera.PictureCallback;
46 import android.location.Location;
47 import android.location.LocationListener;
48 import android.location.LocationManager;
49 import android.location.LocationProvider;
50 import android.media.AudioManager;
51 import android.media.Ringtone;
52 import android.media.RingtoneManager;
53 import android.net.ConnectivityManager;
54 import android.net.Uri;
55 import android.net.wifi.WifiManager;
56 import android.os.AsyncTask;
57 import android.os.BatteryManager;
58 import android.os.Bundle;
59 import android.os.Handler;
60 import android.os.PowerManager;
61 import android.os.Vibrator;
62 import android.preference.PreferenceManager;
63 import android.provider.BaseColumns;
64 import android.provider.CallLog.Calls;
65 import android.provider.ContactsContract.CommonDataKinds;
66 import android.provider.ContactsContract.CommonDataKinds.BaseTypes;
67 import android.provider.ContactsContract.CommonDataKinds.Phone;
68 import android.provider.ContactsContract.Contacts;
69 import android.provider.ContactsContract.Data;
70 import android.provider.Settings.Secure;
71 import android.speech.tts.TextToSpeech;
72 import android.speech.tts.TextToSpeech.OnInitListener;
73 import android.support.v4.app.NotificationCompat;
74 import android.telephony.SmsManager;
75 import android.telephony.TelephonyManager;
76 import android.util.Log;
77 import android.view.SurfaceView;
78 import android.widget.Toast;
79
80 import com.android.internal.telephony.ITelephony;
81
82 /*
83 * Copyright © 2013 Marius Gavrilescu
84 *
85 * This file is part of FonBot.
86 *
87 * FonBot is free software: you can redistribute it and/or modify
88 * it under the terms of the GNU General Public License as published by
89 * the Free Software Foundation, either version 3 of the License, or
90 * (at your option) any later version.
91 *
92 * FonBot is distributed in the hope that it will be useful,
93 * but WITHOUT ANY WARRANTY; without even the implied warranty of
94 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
95 * GNU General Public License for more details.
96 *
97 * You should have received a copy of the GNU General Public License
98 * along with FonBot. If not, see <http://www.gnu.org/licenses/>.
99 */
100
101 /**
102 * Implementation of all FonBot commands. The methods of this class do not do argument checking.
103 *
104 * @author Marius Gavrilescu <marius@ieval.ro>
105 */
106 final class Heavy {
107 /**
108 * LocationListener that sends notifications to the user.
109 *
110 * @author Marius Gavrilescu <marius@ieval.ro>
111 */
112 private static final class FonBotLocationListener implements LocationListener {
113 /** Context instance */
114 private final Context context;
115 /** Destination address for notifications */
116 private final Address replyTo;
117
118 /**
119 * Construct a FonBotLocationListener.
120 *
121 * @param context Context instance
122 * @param replyTo the reply address
123 */
124 FonBotLocationListener(final Context context, final Address replyTo) {
125 this.context=context;
126 this.replyTo=replyTo;
127 }
128
129 @Override
130 public void onLocationChanged(@Nullable final Location loc) {
131 if(loc==null)
132 return;
133 final StringBuilder sb=new StringBuilder(toNonNull(context.getString(location)));
134 sb.append(": ");
135 sb.append(toNonNull(context.getString(latitude)));
136 sb.append(": ");
137 sb.append(loc.getLatitude());
138 sb.append(", ");
139 sb.append(toNonNull(context.getString(longitude)));
140 sb.append(": ");
141 sb.append(loc.getLongitude());
142
143 if(loc.hasAccuracy()){
144 sb.append(", ");
145 sb.append(toNonNull(context.getString(accuracy)));
146 sb.append(": ");
147 sb.append(loc.getAccuracy());
148 }
149
150 if(loc.hasAltitude()){
151 sb.append(", ");
152 sb.append(toNonNull(context.getString(altitude)));
153 sb.append(": ");
154 sb.append(loc.getAltitude());
155 }
156
157 if(loc.hasBearing()){
158 sb.append(", ");
159 sb.append(toNonNull(context.getString(bearing)));
160 sb.append(": ");
161 sb.append(loc.getBearing());
162 }
163
164 if(loc.hasSpeed()){
165 sb.append(", ");
166 sb.append(toNonNull(context.getString(speed)));
167 sb.append(": ");
168 sb.append(loc.getSpeed());
169 }
170
171 final Date locationDate=new Date(loc.getTime());
172 sb.append(" ");
173 sb.append(toNonNull(context.getString(at)));
174 sb.append(" ");
175 sb.append(locationDate.toString());
176 sb.append(". ");
177
178 sb.append("http://openstreetmap.org/?zoom=15&mlat=");
179 sb.append(loc.getLatitude());
180 sb.append("&mlon=");
181 sb.append(loc.getLongitude());
182 Utils.sendMessage(toNonNull(context), toNonNull(replyTo), toNonNull(sb.toString()));
183 }
184
185 @Override
186 public void onProviderDisabled(@Nullable final String provider) {
187 Utils.sendMessage(toNonNull(context), toNonNull(replyTo), location_provider_disabled, provider);
188 }
189
190 @Override
191 public void onProviderEnabled(@Nullable final String provider) {
192 Utils.sendMessage(toNonNull(context), toNonNull(replyTo), location_provider_enabled, provider);
193 }
194
195 @Override
196 public void onStatusChanged(@Nullable final String provider, final int status, @Nullable final Bundle extras) {
197 final int state;
198 switch(status){
199 case LocationProvider.AVAILABLE:
200 state=location_provider_available;
201 break;
202 case LocationProvider.TEMPORARILY_UNAVAILABLE:
203 state=location_provider_temporary_unavailable;
204 break;
205 case LocationProvider.OUT_OF_SERVICE:
206 state=location_provider_out_of_service;
207 break;
208 default:
209 state=location_provider_unknown_state;
210 }
211 Utils.sendMessage(toNonNull(context), toNonNull(replyTo), state, provider);
212 }
213 }
214 /**
215 * Currently active FonBotLocationListener
216 */
217 private static FonBotLocationListener locationListener = null;
218
219 /**
220 * AsyncTask that sends a byte[] to a server
221 *
222 * @author Marius Gavrilescu <marius@ieval.ro>
223 *
224 */
225 private static class SendDataAsyncTask extends AsyncTask<Void, Void, Void>{
226 /**
227 * Context instance used by this class
228 */
229 private final Context context;
230 /**
231 * Server hostname
232 */
233 private final String hostname;
234 /**
235 * Server port
236 */
237 private final int port;
238 /**
239 * Data to send
240 */
241 private final byte[] data;
242 /**
243 * Address for sending back errors and success messages
244 */
245 private final Address replyTo;
246
247 /**
248 * Constructs a SendDataAsyncTasks from its parameters
249 *
250 * @param context the context
251 * @param replyTo the reply Address
252 * @param hostname the server hostname
253 * @param port the server port
254 * @param data the data to send
255 */
256 public SendDataAsyncTask(final Context context,final Address replyTo, final String hostname, final int port, final byte[] data) {//NOPMD array is immutable
257 super();
258 this.context=context;
259 this.hostname=hostname;
260 this.port=port;
261 this.data=data;
262 this.replyTo=replyTo;
263 }
264
265 @Override
266 protected @Nullable Void doInBackground(@Nullable final Void... params) {
267 final Socket sock;
268 try {
269 sock = new Socket(hostname, port);
270 try {
271 sock.getOutputStream().write(data);
272 } catch (IOException e) {
273 Utils.sendMessage(toNonNull(context), toNonNull(replyTo), error_writing_to_socket, e.getMessage());
274 return null;
275 }
276
277 try {
278 sock.close();
279 } catch (IOException e) {
280 Utils.sendMessage(toNonNull(context), toNonNull(replyTo), cannot_close_socket, e.getMessage());
281 return null;
282 }
283 } catch (UnknownHostException e) {
284 Utils.sendMessage(toNonNull(context), toNonNull(replyTo), unknown_host, hostname);
285 return null;
286 } catch (IOException e) {
287 Utils.sendMessage(toNonNull(context), toNonNull(replyTo), cannot_connect_to_host_on_port, hostname, Integer.valueOf(port));
288 return null;
289 }
290 Utils.sendMessage(toNonNull(context), toNonNull(replyTo), photo_sent);
291 return null;
292 }
293 }
294
295 /**
296 * ExecutableRunnable that uploads a file to a given host and port, netcat-style
297 *
298 * @author Marius Gavrilescu <marius@ieval.ro>
299 */
300 private static final class NcfileExecutableRunnable extends ExecutableRunnable {
301 private final Context context;
302 private final Address replyTo;
303 private final String filename;
304 private final String hostname;
305 private final int port;
306
307 /**
308 * Construct a NcfileExecutableRunnable
309 *
310 * @param context Context instance
311 * @param replyTo reply Address
312 * @param filename filename to upload
313 * @param hostname hostname to upload to
314 * @param port port to upload to
315 */
316 NcfileExecutableRunnable(final Context context, final Address replyTo, final String filename, final String hostname, final int port){
317 this.context=context;
318 this.replyTo=replyTo;
319 this.filename=filename;
320 this.hostname=hostname;
321 this.port=port;
322 }
323
324 @Override public void run(){
325 final FileChannel in;
326 final SocketChannel sock;
327 try{
328 in=new FileInputStream(filename).getChannel();
329 } catch (final FileNotFoundException e){
330 Utils.sendMessage(context, replyTo, file_not_found, filename);
331 return;
332 }
333
334 try{
335 sock = SocketChannel.open(new InetSocketAddress(hostname, port));
336 } catch (final IOException e){
337 Utils.sendMessage(context, replyTo, toNonNull(context.getString(cannot_connect_to_host_on_port, hostname, Integer.valueOf(port))));
338 try {
339 in.close();
340 } catch (IOException ex) {
341 //ignored
342 }
343 return;
344 }
345
346 try{
347 in.transferTo(0, in.size(), sock);
348 } catch (final IOException e){
349 Utils.sendMessage(context, replyTo, toNonNull(context.getString(io_error, e.getMessage())));
350 } finally {
351 try{
352 in.close();
353 } catch (IOException e){
354 //ignored
355 }
356 try{
357 sock.close();
358 } catch(IOException e){
359 //ignored
360 }
361 }
362 Utils.sendMessage(context, replyTo, file_sent);
363 }
364 }
365
366 /**
367 * ExecutableRunnable that downloads a file from a given host and port, netcat-style
368 *
369 * @author Marius Gavrilescu <marius@ieval.ro>
370 */
371 private static final class GetfileExecutableRunnable extends ExecutableRunnable {
372 private final Context context;
373 private final Address replyTo;
374 private final String filename;
375 private final String hostname;
376 private final int port;
377
378 /**
379 * Construct a GetfileExecutableRunnable
380 *
381 * @param context Context instance
382 * @param replyTo reply Address
383 * @param filename filename to save to
384 * @param hostname hostname to download from
385 * @param port port to download from
386 */
387 GetfileExecutableRunnable(final Context context, final Address replyTo, final String filename, final String hostname, final int port){
388 this.context=context;
389 this.replyTo=replyTo;
390 this.filename=filename;
391 this.hostname=hostname;
392 this.port=port;
393 }
394
395 @Override public void run(){
396 final InputStream in;
397 final FileOutputStream out;
398 final Socket sock;
399 try{
400 out=new FileOutputStream(filename);
401 } catch (final IOException e){
402 Utils.sendMessage(context, replyTo, error_opening_file, filename, e.getMessage());
403 return;
404 }
405
406 try{
407 sock = new Socket(hostname, port);
408 in = sock.getInputStream();
409 } catch (final IOException e){
410 Utils.sendMessage(context, replyTo, toNonNull(context.getString(cannot_connect_to_host_on_port, hostname, Integer.valueOf(port))));
411 try {
412 out.close();
413 } catch (IOException ex) {
414 //ignored
415 }
416 return;
417 }
418
419 try{
420 byte[] buffer=new byte[1024*1024*2];
421 int nread;
422 while((nread = in.read(buffer)) > 0)
423 out.write(buffer, 0, nread);
424 } catch (final IOException e){
425 Utils.sendMessage(context, replyTo, toNonNull(context.getString(io_error, e.getMessage())));
426 } finally {
427 try{
428 out.close();
429 } catch (IOException e){
430 //ignored
431 }
432 try{
433 sock.close();
434 } catch(IOException e){
435 //ignored
436 }
437 }
438 Utils.sendMessage(context, replyTo, file_received);
439 }
440 }
441
442 /**
443 * PictureCallback that sends the picture to a server.
444 *
445 * @author Marius Gavrilescu <marius@ieval.ro>
446 */
447 private static final class FonBotPictureCallback implements PictureCallback{
448 /** Server hostname */
449 private final String hostname;
450 /** Server port */
451 private final int port;
452 /** Context instance */
453 private final Context context;
454 /** Reply address */
455 private final Address replyTo;
456
457 /**
458 * Construct a FonBotPictureCallback.
459 *
460 * @param context Context instance
461 * @param replyTo reply Address
462 * @param hostname server hostname
463 * @param port server port
464 */
465 FonBotPictureCallback(final Context context, final Address replyTo, final String hostname, final int port) {
466 this.hostname=hostname;
467 this.port=port;
468 this.context=context;
469 this.replyTo=replyTo;
470 }
471
472 @Override
473 @SuppressWarnings("hiding")
474 public void onPictureTaken(final @Nullable byte[] data, final @Nullable Camera camera) {
475 if(camera==null || data==null)
476 return;
477 camera.stopPreview();
478 stopCamera();
479 Utils.sendMessage(toNonNull(context), toNonNull(replyTo), sending_photo);
480 new SendDataAsyncTask(toNonNull(context), toNonNull(replyTo), toNonNull(hostname), port, data).execute();
481 }
482 }
483
484 /**
485 * Runnable that takes a screen capture and stores it in a file.
486 */
487 private static final class ScreencapRunnable implements Runnable{
488 private final Context context;
489 private final Address replyTo;
490 private final String filename;
491
492 ScreencapRunnable(final Context context, final Address replyTo, final String filename){
493 this.context=context;
494 this.replyTo=replyTo;
495 this.filename=filename;
496 }
497
498 @Override
499 public void run(){
500 final int exitCode;
501 try {
502 exitCode=Runtime.getRuntime().exec(new String[]{
503 "su",
504 "-c",
505 "screencap -p \"" + filename + "\""
506 }).waitFor();
507 } catch (final Exception e){
508 e.printStackTrace();
509 Utils.sendMessage(toNonNull(context), toNonNull(replyTo), screencap_failed);
510 return;
511 }
512
513 if(exitCode == 0 && new File(filename).exists())
514 Utils.sendMessage(toNonNull(context), toNonNull(replyTo), screencap_successful);
515 else
516 Utils.sendMessage(toNonNull(context), toNonNull(replyTo), screencap_failed);
517 }
518 }
519
520 /**
521 * Get help for a particular command
522 *
523 * @param context Context instance
524 * @param replyTo reply Address
525 * @param command command to get help for
526 */
527 public static void help(final Context context, final Address replyTo, final Command command){//NOPMD method is a big switch statement. Nothing confusing.
528 switch(command){
529 case ANSWER:
530 Utils.sendMessage(context, replyTo, answer_help);
531 break;
532 case BATT:
533 Utils.sendMessage(context, replyTo, batt_help);
534 break;
535 case BLUETOOTH:
536 Utils.sendMessage(context, replyTo, bluetooth_help);
537 break;
538 case CALLLOG:
539 Utils.sendMessage(context, replyTo, calllog_help);
540 break;
541 case CONTACTS:
542 Utils.sendMessage(context, replyTo, contacts_help);
543 break;
544 case DATA:
545 Utils.sendMessage(context, replyTo, data_help);
546 break;
547 case DELNOTIFICATION:
548 Utils.sendMessage(context, replyTo, delnotification_help, Utils.join(", ", toNonNull(MessageType.values())));
549 break;
550 case DIAL:
551 Utils.sendMessage(context, replyTo, dial_help);
552 break;
553 case DIALOG:
554 Utils.sendMessage(context, replyTo, dialog_help);
555 break;
556 case DISABLE:
557 Utils.sendMessage(context, replyTo, disable_help, Utils.join(", ", toNonNull(Command.values())));
558 break;
559 case ECHO:
560 Utils.sendMessage(context, replyTo, echo_help);
561 break;
562 case ENABLE:
563 Utils.sendMessage(context, replyTo, enable_help, Utils.join(", ", toNonNull(Command.values())));
564 break;
565 case FLASH:
566 Utils.sendMessage(context, replyTo, flash_help);
567 break;
568 case GLOCATION:
569 Utils.sendMessage(context, replyTo, glocation_help);
570 break;
571 case GPS:
572 Utils.sendMessage(context, replyTo, gps_help);
573 break;
574 case HANGUP:
575 Utils.sendMessage(context, replyTo, hangup_help);
576 break;
577 case HELP:
578 Utils.sendMessage(context, replyTo, help_help, Utils.join(", ",toNonNull(Command.values())));
579 break;
580 case LAUNCH:
581 Utils.sendMessage(context, replyTo, launch_help);
582 break;
583 case LOCATION:
584 Utils.sendMessage(context, replyTo, location_help, Utils.join(", ",toNonNull(Utils.LocationProvider.values())));
585 break;
586 case LOCK:
587 Utils.sendMessage(context, replyTo, lock_help);
588 break;
589 case LS:
590 Utils.sendMessage(context, replyTo, ls_help);
591 break;
592 case NCFILE:
593 Utils.sendMessage(context, replyTo, ncfile_help);
594 break;
595 case NEXT:
596 Utils.sendMessage(context, replyTo, next_help);
597 break;
598 case NOLOCATION:
599 Utils.sendMessage(context, replyTo, nolocation_help);
600 break;
601 case PAUSE:
602 Utils.sendMessage(context, replyTo, pause_help);
603 break;
604 case PHOTO:
605 Utils.sendMessage(context, replyTo, photo_help);
606 break;
607 case PLAY:
608 Utils.sendMessage(context, replyTo, play_help);
609 break;
610 case POLL:
611 Utils.sendMessage(context, replyTo, poll_help);
612 break;
613 case PREV:
614 Utils.sendMessage(context, replyTo, prev_help);
615 break;
616 case RING:
617 Utils.sendMessage(context, replyTo, ring_help);
618 break;
619 case RINGER:
620 Utils.sendMessage(context, replyTo, ringer_help, Utils.join(", ", toNonNull(RingerMode.values())));
621 break;
622 case RM:
623 Utils.sendMessage(context, replyTo, rm_help);
624 break;
625 case SETNOTIFICATION:
626 Utils.sendMessage(context, replyTo, setnotification_help, Utils.join(", ", toNonNull(MessageType.values())));
627 break;
628 case SETPASSWORD:
629 Utils.sendMessage(context, replyTo, setpassword_help);
630 break;
631 case SMS:
632 Utils.sendMessage(context, replyTo, sms_help);
633 break;
634 case SMSLOG:
635 Utils.sendMessage(context, replyTo, smslog_help);
636 break;
637 case SPEAK:
638 Utils.sendMessage(context, replyTo, speak_help);
639 break;
640 case TOAST:
641 Utils.sendMessage(context, replyTo, toast_help);
642 break;
643 case VIBRATE:
644 Utils.sendMessage(context, replyTo, vibrate_help);
645 break;
646 case VIEW:
647 Utils.sendMessage(context, replyTo, view_help);
648 break;
649 case WIFI:
650 Utils.sendMessage(context, replyTo, wifi_help);
651 break;
652 case WIPE:
653 Utils.sendMessage(context, replyTo, wipe_help, Utils.WIPE_CONFIRM_STRING);
654 break;
655 case REBOOT:
656 Utils.sendMessage(context, replyTo, reboot_help);
657 break;
658 case NOTIFY:
659 Utils.sendMessage(context, replyTo, notify_help);
660 break;
661 case SCREENCAP:
662 Utils.sendMessage(context, replyTo, screencap_help);
663 break;
664 case TORCH:
665 Utils.sendMessage(context, replyTo, torch_help);
666 break;
667 }
668 }
669
670 /**
671 * Camera instance.
672 *
673 * @see #startCamera(Context, Address)
674 * @see #stopCamera()
675 */
676 private static Camera camera;
677 /**
678 * Ringtone used by the {@link Utils.Command#RING RING} command.
679 *
680 * @see #setupRingtone(Context)
681 */
682 private static Ringtone ringtone;
683 /**
684 * Saved ringer volume.
685 *
686 * @see #startAlarm(Context, Address)
687 * @see #stopAlarm(Context, Address)
688 */
689 private static int savedRingVolume;
690 /**
691 * Saved ringer mode.
692 *
693 * @see #startAlarm(Context, Address)
694 * @see #stopAlarm(Context, Address)
695 */
696 private static int savedRingerMode;
697
698 /** Private constructor */
699 private Heavy(){
700 //do nothing
701 }
702
703 /**
704 * Convert a phone number type to a string
705 *
706 * @param context Context instance
707 * @param type phone number type
708 * @param label name of a custom phone type
709 * @return the phone number type
710 */
711 private static @Nullable String phoneNumberType(final Context context, final int type, final @Nullable String label) {
712 switch(type){
713 case BaseTypes.TYPE_CUSTOM:
714 return label;
715 case Phone.TYPE_ASSISTANT:
716 return context.getString(phone_numer_type_assistant);
717 case Phone.TYPE_CALLBACK:
718 return context.getString(phone_number_type_callback);
719 case Phone.TYPE_CAR:
720 return context.getString(phone_number_type_car);
721 case Phone.TYPE_COMPANY_MAIN:
722 return context.getString(phone_number_type_company_main);
723 case Phone.TYPE_FAX_HOME:
724 return context.getString(phone_number_type_home_fax);
725 case Phone.TYPE_FAX_WORK:
726 return context.getString(phone_number_type_work_fax);
727 case Phone.TYPE_HOME:
728 return context.getString(phone_number_type_home);
729 case Phone.TYPE_ISDN:
730 return context.getString(phone_number_type_isdn);
731 case Phone.TYPE_MAIN:
732 return context.getString(phone_number_type_main);
733 case Phone.TYPE_MMS:
734 return context.getString(phone_number_type_mms);
735 case Phone.TYPE_MOBILE:
736 return context.getString(phone_number_type_mobile);
737 case Phone.TYPE_OTHER:
738 return context.getString(phone_number_type_other);
739 case Phone.TYPE_OTHER_FAX:
740 return context.getString(phone_number_type_other_fax);
741 case Phone.TYPE_PAGER:
742 return context.getString(phone_number_type_pager);
743 case Phone.TYPE_RADIO:
744 return context.getString(phone_number_type_radio);
745 case Phone.TYPE_TELEX:
746 return context.getString(phone_number_type_telex);
747 case Phone.TYPE_TTY_TDD:
748 return context.getString(phone_number_type_textphone);
749 case Phone.TYPE_WORK:
750 return context.getString(phone_number_type_work);
751 case Phone.TYPE_WORK_MOBILE:
752 return context.getString(phone_number_type_work_mobile);
753 case Phone.TYPE_WORK_PAGER:
754 return context.getString(phone_number_type_work_pager);
755 }
756
757 return context.getString(phone_number_type_unknown, Integer.valueOf(type));
758 }
759
760 /**
761 * Setup the ringtone used by the {@link Utils.Command#RING RING} command
762 *
763 * @param context Context
764 */
765 private static void setupRingtone(final Context context){
766 if(ringtone==null){//NOPMD not supposed to be thread-safe
767 final Uri alert=RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE);
768 ringtone=RingtoneManager.getRingtone(context, alert);
769 }
770 }
771
772 /**
773 * Make the phone start ringing. Turns up the volume and sets the ringer mode to NORMAL
774 *
775 * @param context Context instance
776 * @param replyTo reply Address
777 */
778 private static void startAlarm(final Context context, final Address replyTo){
779 Utils.registerOngoing(context, toNonNull(OngoingEvent.RING));
780 final AudioManager man=(AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
781 savedRingerMode=man.getRingerMode();
782 man.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
783 savedRingVolume=man.getStreamVolume(AudioManager.STREAM_RING);
784 man.setStreamVolume(AudioManager.STREAM_RING, man.getStreamMaxVolume(AudioManager.STREAM_RING), 0);
785 Utils.sendMessage(context, replyTo, ringing);
786 ringtone.play();
787 }
788
789 /**
790 * Get a camera instance.
791 *
792 * @param context Context instance
793 * @param replyTo reply Address
794 */
795 private static void startCamera(final Context context, final Address replyTo, final int cameraNumber){
796 if(camera!=null)
797 return;
798 try{
799 try{
800 camera=Camera.open(cameraNumber);
801 } catch (Exception ex){
802 camera=Camera.open();
803 }
804 } catch (Exception e){
805 Utils.sendMessage(context, replyTo, cannot_grab_camera);
806 }
807 }
808
809 /**
810 * Make the phone stop ringing. Restores the volume and ringer mode.
811 *
812 * @param context Context instance
813 * @param replyTo reply Address
814 */
815 private static void stopAlarm(final Context context, final Address replyTo){
816 Utils.unregisterOngoing(context, toNonNull(OngoingEvent.RING));
817 final AudioManager man=(AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
818 Utils.sendMessage(context, replyTo, no_longer_ringing);
819 ringtone.stop();
820 man.setStreamVolume(AudioManager.STREAM_RING, savedRingVolume, 0);
821 man.setRingerMode(savedRingerMode);
822 }
823
824 /**
825 * Release the previously grabbed camera instance
826 *
827 * @see #startCamera(Context, Address)
828 */
829 private static void stopCamera(){
830 if(camera==null)
831 return;
832 camera.release();
833 camera=null;
834 }
835
836 /**
837 * Send battery status information to an Address
838 *
839 * @param context Context instance
840 * @param replyTo destination Address
841 *
842 * @see #describeBatteryLevel(Context, Address, MessageType)
843 */
844 public static void batt(final Context context, final Address replyTo){
845 describeBatteryLevel(context, replyTo, null);
846 }
847
848 /**
849 * Show the bluetooth radio status.
850 *
851 * @param context Context instance
852 * @param replyTo destination Address
853 */
854 public static void bluetooth(final Context context, final Address replyTo) {
855 final BluetoothAdapter adapter=BluetoothAdapter.getDefaultAdapter();
856 if(adapter==null){
857 Utils.sendMessage(context, replyTo, no_bluetooth_adapter);
858 return;
859 }
860
861 if(adapter.isEnabled())
862 Utils.sendMessage(context, replyTo, bluetooth_on);
863 else
864 Utils.sendMessage(context, replyTo, bluetooth_off);
865 }
866
867 /**
868 * Set the bluetooth radio status.
869 *
870 * @param context Context instance
871 * @param replyTo destination Address
872 * @param on the requested radio status
873 */
874 public static void bluetooth(final Context context, final Address replyTo, final boolean on){
875 final BluetoothAdapter adapter=BluetoothAdapter.getDefaultAdapter();
876 if(adapter==null){
877 Utils.sendMessage(context, replyTo, no_bluetooth_adapter);
878 return;
879 }
880
881 if(on) {
882 adapter.enable();
883 Utils.sendMessage(context, replyTo, enabling_bluetooth);
884 }
885 else {
886 adapter.disable();
887 Utils.sendMessage(context, replyTo, disabling_bluetooth);
888 }
889 }
890
891 /**
892 * Cancel an ongoing event.
893 *
894 * @param context Context instance
895 * @param event the event to cancel
896 */
897 public static void cancelOngoing(final Context context, final OngoingEvent event){
898 switch(event){
899 case LOCATION:
900 nolocation(context, toNonNull(Address.BLACKHOLE));
901 break;
902 case RING:
903 ring(context, toNonNull(Address.BLACKHOLE), false);
904 break;
905 }
906 }
907
908 /**
909 * Send the last calls to an Address.
910 *
911 * @param context Context instance
912 * @param replyTo destination Address
913 * @param numCalls how many calls to send
914 */
915 public static void calllog(final Context context, final Address replyTo, final int numCalls) {
916 final String[] fields = {
917 Calls.TYPE, Calls.NUMBER, Calls.CACHED_NAME, Calls.DURATION, Calls.DATE
918 };
919
920 final Cursor cursor = context.getContentResolver().query(
921 Calls.CONTENT_URI,
922 fields,
923 null,
924 null,
925 Calls.DATE + " DESC"
926 );
927
928 if (cursor.moveToFirst()) {
929 do {
930 final StringBuilder sb=new StringBuilder(50);//NOPMD different strings
931 final int type=cursor.getInt(0);
932 final String from=cursor.getString(1);
933
934 switch(type){
935 case Calls.INCOMING_TYPE:
936 sb.append(context.getString(incoming_call_from, from));
937 break;
938 case Calls.MISSED_TYPE:
939 sb.append(context.getString(missed_call_from, from));
940 break;
941 case Calls.OUTGOING_TYPE:
942 sb.append(context.getString(outgoing_call_to, from));
943 break;
944 }
945
946 if (cursor.getString(2) != null)
947 sb.append('(').append(cursor.getString(2)).append(") ");
948
949 sb.append(context.getString(duration_seconds_starting_at,
950 Long.valueOf(cursor.getLong(3)),
951 new Date(cursor.getLong(4))));
952
953 Utils.sendMessage(context, replyTo, toNonNull(sb.toString()));
954 } while (cursor.moveToNext() && cursor.getPosition() < numCalls);
955 }
956
957 cursor.close();
958 }
959
960 /**
961 * Search for contacts by name/nickname and send matching entries to an Address.
962 *
963 * @param context Context instance
964 * @param replyTo destination Address
965 * @param name name/nickname part to search for
966 */
967 @SuppressLint("StringFormatMatches")
968 public static void contacts(final Context context, final Address replyTo, final String name){
969 final Cursor cursor=context.getContentResolver().query(Uri.withAppendedPath(
970 Contacts.CONTENT_FILTER_URI, name),
971 new String[]{Contacts.DISPLAY_NAME, BaseColumns._ID, Contacts.LOOKUP_KEY},
972 null, null, Contacts.DISPLAY_NAME);
973
974 if(cursor.getCount()==0)
975 Utils.sendMessage(context, replyTo, no_matching_contacts_found);
976
977 while(cursor.moveToNext()){
978 final String[] fields = {
979 CommonDataKinds.Phone.NUMBER,
980 CommonDataKinds.Phone.TYPE,
981 CommonDataKinds.Phone.LABEL,
982 };
983
984 final Cursor inCursor=context.getContentResolver().query(Data.CONTENT_URI,
985 fields,
986 Data.CONTACT_ID+" = ? AND "+Data.MIMETYPE+ " = ?",
987 new String[]{Long.toString(cursor.getLong(1)), CommonDataKinds.Phone.CONTENT_ITEM_TYPE},
988 CommonDataKinds.Phone.LABEL);
989
990 while(inCursor.moveToNext())
991 Utils.sendMessage(context, replyTo, toNonNull(context.getString(contact_info,
992 cursor.getString(0),
993 inCursor.getString(0),
994 phoneNumberType(context, inCursor.getInt(1), inCursor.getString(2)))));
995
996 inCursor.close();
997 }
998
999 cursor.close();
1000 }
1001
1002 /**
1003 * Send battery status information to an Address or as a notification
1004 *
1005 * @param context Context instance
1006 * @param replyTo Address to send the information to, if sending to a direct address. Null otherwise.
1007 * @param type Notification type, if sending as a notification. Null otherwise.
1008 */
1009 public static void describeBatteryLevel(final Context context, final @Nullable Address replyTo, final @Nullable MessageType type) {
1010 if(replyTo==null&&type==null)
1011 return;
1012 final Intent intent=context.registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
1013 if(intent==null)
1014 return;
1015 final double level=intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0);
1016 final int scale=intent.getIntExtra(BatteryManager.EXTRA_SCALE, 100);
1017 final int plugged=intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0);
1018 final int status=intent.getIntExtra(BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_UNKNOWN);
1019 final int temp=intent.getIntExtra(BatteryManager.EXTRA_TEMPERATURE, 0);
1020 final int volt=intent.getIntExtra(BatteryManager.EXTRA_VOLTAGE, 0);
1021
1022 final StringBuilder sb=new StringBuilder(100);
1023 sb.append(context.getString(battery_level, Double.valueOf(level*100/scale)));
1024
1025 switch(plugged){
1026 case 0:
1027 sb.append(context.getString(not_plugged_in));
1028 break;
1029 case BatteryManager.BATTERY_PLUGGED_AC:
1030 sb.append(context.getString(plugged_in_ac));
1031 break;
1032 case BatteryManager.BATTERY_PLUGGED_USB:
1033 sb.append(context.getString(plugged_in_usb));
1034 break;
1035 case BatteryManager.BATTERY_PLUGGED_WIRELESS:
1036 sb.append(context.getString(plugged_in_wireless));
1037 break;
1038 }
1039
1040 switch(status){
1041 case BatteryManager.BATTERY_STATUS_CHARGING:
1042 sb.append(context.getString(status_charging));
1043 break;
1044 case BatteryManager.BATTERY_STATUS_DISCHARGING:
1045 sb.append(context.getString(status_discharging));
1046 break;
1047 case BatteryManager.BATTERY_STATUS_FULL:
1048 sb.append(context.getString(status_full));
1049 break;
1050 case BatteryManager.BATTERY_STATUS_NOT_CHARGING:
1051 sb.append(context.getString(status_not_charging));
1052 break;
1053 case BatteryManager.BATTERY_STATUS_UNKNOWN:
1054 sb.append(context.getString(status_unknown));
1055 break;
1056 }
1057
1058 sb.append(context.getString(temperature, Integer.valueOf(temp)));
1059
1060 sb.append(context.getString(voltage, Integer.valueOf(volt)));
1061 if(type==null)
1062 Utils.sendMessage(context, toNonNull(replyTo), toNonNull(sb.toString()));
1063 else
1064 Utils.sendMessage(context, type, toNonNull(sb.toString()));
1065 }
1066
1067 /**
1068 * Dial a phone number.
1069 *
1070 * @param context Context instance
1071 * @param replyTo reply Address
1072 * @param nr phone number to dial
1073 */
1074 public static void dial(final Context context, final Address replyTo, final String nr){
1075 final Intent intent=new Intent(Intent.ACTION_CALL,Uri.parse("tel:"+nr));
1076 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1077 final String name=Utils.callerId(context, nr);
1078 if(name==null)
1079 Utils.sendMessage(context, replyTo, dialing, nr);
1080 else
1081 Utils.sendMessage(context, replyTo, dialing, nr+" ("+name+")");
1082 context.startActivity(intent);
1083 }
1084
1085 /**
1086 * Show a dialog with a message and a list of buttons.
1087 *
1088 * @param context Context instance
1089 * @param replyTo reply Address
1090 * @param message dialog message
1091 * @param buttons dialog buttons
1092 */
1093 public static void dialog(final Context context, final Address replyTo, final String message, final String[] buttons){
1094 final Intent intent=new Intent(context, DialogActivity.class);
1095 intent.putExtra(DialogActivity.EXTRA_MESSAGE, message);
1096 intent.putExtra(DialogActivity.EXTRA_BUTTONS, buttons);
1097 intent.putExtra(DialogActivity.EXTRA_REPLYTO, replyTo.toString());
1098 intent.addFlags(
1099 Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS|
1100 Intent.FLAG_ACTIVITY_NEW_TASK|
1101 Intent.FLAG_ACTIVITY_NO_USER_ACTION|
1102 Intent.FLAG_FROM_BACKGROUND);
1103 Utils.sendMessage(context, toNonNull(replyTo), showing_dialog);
1104 context.startActivity(intent);
1105 }
1106
1107 /**
1108 * Turns the flashlight on or off.
1109 *
1110 * @param context Context instance
1111 * @param replyTo reply Address
1112 * @param on requested flashlight state
1113 */
1114 public static void flash(final Context context, final Address replyTo, final boolean on){
1115 startCamera(context, replyTo, 0);
1116 if(camera==null)
1117 return;
1118 final Camera.Parameters parms=camera.getParameters();
1119 if(on){
1120 parms.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
1121 camera.setParameters(parms);
1122 } else {
1123 parms.setFlashMode(Camera.Parameters.FLASH_MODE_AUTO);
1124 camera.setParameters(parms);
1125 stopCamera();
1126 }
1127 }
1128
1129 /**
1130 * Start sending location updates to an Address.
1131 *
1132 * @param context Context instance
1133 * @param replyTo destination Address
1134 * @param provider LocationProvider
1135 * @param minTime minimum time between two consecutive updates (in ms)
1136 * @param minDistance minimum distance between two consecutive updates (in meters)
1137 *
1138 * @see LocationManager#requestLocationUpdates(String, long, float, LocationListener)
1139 */
1140 public static void location(final Context context, final Address replyTo, final String provider,final long minTime,final float minDistance){
1141 final LocationManager man=(LocationManager) context.getApplicationContext().getSystemService(Context.LOCATION_SERVICE);
1142 if(locationListener!=null)
1143 nolocation(context, toNonNull(Address.BLACKHOLE));
1144 Utils.registerOngoing(context, toNonNull(OngoingEvent.LOCATION));
1145 locationListener=new FonBotLocationListener(context, replyTo);
1146 man.removeUpdates(locationListener);
1147 final Location lastKnownLocation=man.getLastKnownLocation(provider);
1148 if(lastKnownLocation!=null){
1149 Utils.sendMessage(context, replyTo, last_known_location);
1150 locationListener.onLocationChanged(lastKnownLocation);
1151 }
1152 Utils.sendMessage(context, replyTo, listening_for_location_updates);
1153 man.requestLocationUpdates(provider, minTime, minDistance, locationListener);
1154 }
1155
1156 /**
1157 * Lock the phone.
1158 *
1159 * @param context Context instance
1160 * @param replyTo reply Address
1161 */
1162 public static void lock(final Context context, final Address replyTo) {
1163 final DevicePolicyManager dpm=(DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
1164 dpm.lockNow();
1165 Utils.sendMessage(context, replyTo, device_locked);
1166 }
1167
1168 /**
1169 * Send a command to a running instance of the music player
1170 *
1171 * @param context Context instance
1172 * @param replyTo reply Address
1173 * @param command command to send
1174 */
1175 public static void musicPlayerCommand(final Context context, final Address replyTo, final String command) {
1176 final Intent intent=new Intent("com.android.music.musicservicecommand");
1177 intent.putExtra("command", command);
1178 context.sendBroadcast(intent);
1179 Utils.sendMessage(context, replyTo, command_sent);
1180 }
1181
1182 /**
1183 * Send a file to a server.
1184 *
1185 * @param context Context instance
1186 * @param replyTo reply Address
1187 * @param filename file to send
1188 * @param hostname server hostname
1189 * @param port server port
1190 */
1191 public static void ncfile(final Context context, final Address replyTo, final String filename,final String hostname,final int port){
1192 new NcfileExecutableRunnable(context, replyTo, filename, hostname, port).execute();
1193 }
1194
1195 /**
1196 * Stop sending location updates.
1197 *
1198 * @param context Context instance
1199 * @param replyTo reply Address
1200 */
1201 public static void nolocation(final Context context, final Address replyTo){
1202 Utils.unregisterOngoing(context, toNonNull(OngoingEvent.LOCATION));
1203 final LocationManager man=(LocationManager) context.getApplicationContext().getSystemService(Context.LOCATION_SERVICE);
1204 man.removeUpdates(locationListener);
1205 locationListener=null;
1206 Utils.sendMessage(context, replyTo, no_longer_listening_for_location_updates);
1207 }
1208
1209 /**
1210 * Take a photo and send it to a server.
1211 *
1212 * @param context Context instance
1213 * @param replyTo reply Address
1214 * @param cameraNumber camera to take photo with
1215 * @param hostname server hostname
1216 * @param port server port
1217 */
1218 public static void photo(final Context context, final Address replyTo, final int cameraNumber, final String hostname, final int port){
1219 startCamera(context, replyTo, cameraNumber);
1220 if(camera==null)
1221 return;
1222 final Camera.Parameters parms=camera.getParameters();
1223 parms.setJpegQuality(70);
1224 parms.setPictureFormat(ImageFormat.JPEG);
1225 camera.setParameters(parms);
1226
1227 final SurfaceView fakeView=new SurfaceView(context);
1228 try {
1229 camera.setPreviewDisplay(fakeView.getHolder());
1230 } catch (IOException e) {
1231 Utils.sendMessage(context, replyTo, error_setting_preview_display);
1232 return;
1233 }
1234 camera.startPreview();
1235 final Handler handler=new Handler();
1236
1237 new Thread(new Runnable() {
1238 @Override
1239 public void run() {
1240 try {
1241 Thread.sleep(2000);
1242 } catch (InterruptedException e) {
1243 //ignored
1244 }
1245
1246 handler.post(new Runnable() {
1247 @Override
1248 public void run() {
1249 camera.takePicture(null, null, new FonBotPictureCallback(context, replyTo, hostname, port));
1250 }
1251 });
1252 }
1253 }).start();
1254 }
1255
1256 /**
1257 * Send a directory listing to an Address
1258 *
1259 * @param context Context instance
1260 * @param replyTo destination Address
1261 * @param directory directory to list
1262 */
1263 public static void ls(final Context context, final Address replyTo, final String directory) {
1264 final File[] files=new File(directory).listFiles();
1265 if(files==null){
1266 Utils.sendMessage(context, replyTo, string_is_not_a_directory, directory);
1267 return;
1268 }
1269
1270 final StringBuilder sb=new StringBuilder(context.getString(files_in_directory,directory));
1271 for(final File file : files){
1272 sb.append(file.getName());
1273 if(file.isDirectory())
1274 sb.append('/');
1275 sb.append(" ");
1276 }
1277
1278 Utils.sendMessage(context, replyTo, toNonNull(sb.toString()));
1279 }
1280
1281 /**
1282 * Make the phone start ringing if it is not ringing or stop ringing if it is.
1283 *
1284 * @param context Context instance
1285 * @param replyTo reply Address
1286 */
1287 public static void ring(final Context context, final Address replyTo){
1288 setupRingtone(context);
1289 if(ringtone==null){
1290 Utils.sendMessage(context, replyTo, no_ringtone_found);
1291 return;
1292 }
1293 if(ringtone.isPlaying())
1294 stopAlarm(context, replyTo);
1295 else
1296 startAlarm(context, replyTo);
1297 }
1298
1299 /**
1300 * Make the phone start/stop ringing.
1301 *
1302 * @param context Context instance
1303 * @param replyTo reply Address
1304 * @param on true if the phone should start ringing, false otherwise
1305 */
1306 public static void ring(final Context context, final Address replyTo, final boolean on){
1307 setupRingtone(context);
1308 if(ringtone==null){
1309 Utils.sendMessage(context, replyTo, no_ringtone_found);
1310 return;
1311 }
1312 if(on&&!ringtone.isPlaying())
1313 startAlarm(context, replyTo);
1314 else if(ringtone.isPlaying()&&!on)
1315 stopAlarm(context, replyTo);
1316 }
1317
1318 /**
1319 * Send the current ringer mode to an Address
1320 *
1321 * @param context Context instance
1322 * @param replyTo destination Address
1323 */
1324 public static void ringer(final Context context, final Address replyTo){
1325 final AudioManager man=(AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
1326 switch(man.getRingerMode()){
1327 case AudioManager.RINGER_MODE_NORMAL:
1328 Utils.sendMessage(context, replyTo, ringer_mode_normal);
1329 break;
1330 case AudioManager.RINGER_MODE_VIBRATE:
1331 Utils.sendMessage(context, replyTo, ringer_mode_vibrate);
1332 break;
1333 case AudioManager.RINGER_MODE_SILENT:
1334 Utils.sendMessage(context, replyTo, ringer_mode_silent);
1335 break;
1336 default:
1337 Utils.sendMessage(context, replyTo, unknown_ringer_mode);
1338 }
1339 }
1340
1341 /**
1342 * Set the ringer mode.
1343 *
1344 * @param context Context instance
1345 * @param replyTo reply Address
1346 * @param ringerMode requested ringer mode
1347 *
1348 * @see Utils.RingerMode
1349 */
1350 public static void ringer(final Context context, final Address replyTo, final int ringerMode){
1351 final AudioManager man=(AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
1352 man.setRingerMode(ringerMode);
1353 ringer(context, replyTo);
1354 }
1355
1356 /**
1357 * Remove a file or empty directory.
1358 *
1359 * @param context Context instance
1360 * @param replyTo reply Address
1361 * @param filename file/empty directory to delete
1362 */
1363 public static void rm(final Context context, final Address replyTo, final String filename){
1364 if(new File(filename).delete())
1365 Utils.sendMessage(context, replyTo, file_deleted);
1366 else
1367 Utils.sendMessage(context, replyTo, error_while_deleting_file);
1368 }
1369
1370 /**
1371 * Clear the keyguard password.
1372 *
1373 * @param context Context instance
1374 * @param replyTo reply Address
1375 * @throws SecurityException if FonBot does not have device administration permissions
1376 */
1377 public static void setPassword(final Context context, final Address replyTo) throws SecurityException{
1378 final DevicePolicyManager dpm=(DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
1379
1380 dpm.resetPassword("", 0);
1381 Utils.sendMessage(context, replyTo, password_cleared);
1382 }
1383
1384 /**
1385 * Change the keyguard password.
1386 *
1387 * @param context Context instance
1388 * @param replyTo reply Address
1389 * @param password new password
1390 * @throws SecurityException if FonBot does not have device administration permissions
1391 */
1392 public static void setPassword(final Context context, final Address replyTo, final String password) throws SecurityException{
1393 final DevicePolicyManager dpm=(DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
1394
1395 dpm.resetPassword(password, 0);
1396 Utils.sendMessage(context, replyTo, password_set);
1397 }
1398
1399 /**
1400 * Send a text message.
1401 *
1402 * @param context Context instance
1403 * @param replyTo reply Address
1404 * @param destination destination phone number
1405 * @param text text message contents
1406 */
1407 public static void sms(final Context context, final Address replyTo, final String destination, final String text){
1408 final SmsManager manager=SmsManager.getDefault();
1409 final ArrayList<String> messages=manager.divideMessage(text);
1410 if(messages.size() > 1)
1411 Utils.sendMessage(context, replyTo, message_was_split_into_parts, Integer.valueOf(messages.size()));
1412
1413 final ArrayList<PendingIntent> sents=new ArrayList<PendingIntent>(messages.size());
1414 final ArrayList<PendingIntent> delivereds=new ArrayList<PendingIntent>(messages.size());
1415
1416 final String name=Utils.callerId(context, destination);
1417 final String fullDestination;
1418 if(name==null)
1419 fullDestination=destination;
1420 else
1421 fullDestination=destination+" ("+name+")";
1422
1423 for(int i=0;i<messages.size();i++){
1424 final Intent sent=new Intent(context,SmsStatusReceiver.class);
1425 sent.putExtra(SmsStatusReceiver.EXTRA_DESTINATION, fullDestination);
1426 sent.putExtra(SmsStatusReceiver.EXTRA_PART, i+1);
1427 sent.putExtra(SmsStatusReceiver.EXTRA_TOTAL, messages.size());
1428 sent.putExtra(SmsStatusReceiver.EXTRA_REPLY_TO, replyTo.toString());
1429 sent.setAction(SmsStatusReceiver.SENT_ACTION+i);//actions must be unique
1430 sents.add(PendingIntent.getBroadcast(context, 0, sent, PendingIntent.FLAG_UPDATE_CURRENT));
1431
1432 final Intent delivered=new Intent(context, SmsStatusReceiver.class);
1433 delivered.putExtra(SmsStatusReceiver.EXTRA_DESTINATION, fullDestination);
1434 delivered.putExtra(SmsStatusReceiver.EXTRA_PART, i+1);
1435 delivered.putExtra(SmsStatusReceiver.EXTRA_TOTAL, messages.size());
1436 delivered.putExtra(SmsStatusReceiver.EXTRA_REPLY_TO, replyTo.toString());
1437 delivered.setAction(SmsStatusReceiver.DELIVERED_ACTION+i);//actions must be unique
1438 delivereds.add(PendingIntent.getBroadcast(context, 0, delivered, PendingIntent.FLAG_UPDATE_CURRENT));
1439 }
1440
1441 Log.d(Heavy.class.getName(), "Sending sms to "+destination);
1442 manager.sendMultipartTextMessage(destination, null, messages, sents, delivereds);
1443 }
1444
1445 /**
1446 * Send the last SMSes to an Address.
1447 *
1448 * @param context Context instance
1449 * @param replyTo destination Address
1450 * @param numSms how many SMSes to send
1451 */
1452 public static void smslog(final Context context, final Address replyTo, final int numSms) {
1453 final String[] fields = {"type","address", "body", "date"};
1454
1455 final Cursor cursor = context.getContentResolver().query (
1456 Uri.parse("content://sms"),
1457 fields,
1458 null,
1459 null,
1460 "date DESC"
1461 );
1462
1463 if (cursor.moveToFirst()) {
1464 do {
1465 final String fromNumber=cursor.getString(1);
1466 final String from;
1467 final String name=Utils.callerId(context, Utils.toNonNull(fromNumber));
1468 if(name==null)
1469 from=fromNumber;
1470 else
1471 from=fromNumber+" ("+name+')';
1472 final String message=cursor.getString(2).replace("\n", "\n ");
1473 final Date date=new Date(cursor.getLong(3));
1474
1475 if(cursor.getInt(0)==1)
1476 Utils.sendMessage(context, replyTo, incoming_message, from, message, date);
1477 else
1478 Utils.sendMessage(context, replyTo, outgoing_message, from, message, date);
1479 } while (cursor.moveToNext() && cursor.getPosition() < numSms);
1480 }
1481
1482 cursor.close();
1483 }
1484
1485 /** TTS instance, only used by {@link #speak(Context, Address, String)} */
1486 private static TextToSpeech tts;
1487
1488 /**
1489 * Speak a String using the text-to-speech engine.
1490 *
1491 * @param context Context instance
1492 * @param replyTo reply Address
1493 * @param text text to speak
1494 */
1495 public static void speak(final Context context, final Address replyTo, final String text){
1496 tts=new TextToSpeech(context, new OnInitListener() {
1497 @Override
1498 public void onInit(final int status) {
1499 if(status==TextToSpeech.SUCCESS){
1500 Utils.sendMessage(context, replyTo, speaking);
1501 tts.speak(text, TextToSpeech.QUEUE_ADD, null);
1502 } else
1503 Utils.sendMessage(context, replyTo, tts_engine_not_available);
1504 }
1505 });
1506 }
1507
1508 /**
1509 * Show a toast notification with the default duration.
1510 *
1511 * @param context Context instance
1512 * @param replyTo reply Address
1513 * @param text toast text
1514 */
1515 public static void toast(final Context context, final Address replyTo, final String text){
1516 toast(context, replyTo, text, Toast.LENGTH_SHORT);
1517 }
1518
1519 /**
1520 * Show a toast notification.
1521 *
1522 * @param context Context instance
1523 * @param replyTo reply Address
1524 * @param text toast text
1525 * @param duration toast duration
1526 */
1527 public static void toast(final Context context, final Address replyTo, final String text, final int duration){
1528 Toast.makeText(context,text,duration).show();
1529 Utils.sendMessage(context, replyTo, toast_shown);
1530 }
1531
1532 /**
1533 * Make the phone vibrate for a number of milliseconds.
1534 *
1535 * @param context Context instance
1536 * @param replyTo reply Address
1537 * @param ms vibrate duration, in milliseconds
1538 */
1539 public static void vibrate(final Context context, final Address replyTo, final long ms){
1540 final Vibrator v=(Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
1541 Utils.sendMessage(context, replyTo, vibrating);
1542 v.vibrate(ms);
1543 }
1544
1545 /**
1546 * View an URI in an appropriate activity.
1547 *
1548 * @param context Context instance
1549 * @param replyTo reply Address
1550 * @param uri URI to view
1551 */
1552 public static void view(final Context context, final Address replyTo, final Uri uri) {
1553 try{
1554 final Intent intent=new Intent(Intent.ACTION_VIEW);
1555 intent.setData(uri);
1556 intent.setFlags(Intent.FLAG_FROM_BACKGROUND|Intent.FLAG_ACTIVITY_NEW_TASK);
1557 context.startActivity(intent);
1558 Utils.sendMessage(context, replyTo, url_opened);
1559 } catch(ActivityNotFoundException e){
1560 Utils.sendMessage(context, replyTo, no_activity_found_for_this_url);
1561 } catch(Exception e){
1562 Utils.sendMessage(context, replyTo, invalid_url);
1563 }
1564 }
1565
1566 /**
1567 * Get the current WiFi state.
1568 *
1569 * @param context Context instance
1570 * @param replyTo reply Address
1571 */
1572 public static void wifi(final Context context, final Address replyTo){
1573 final WifiManager man=(WifiManager) context.getSystemService(Context.WIFI_SERVICE);
1574 if(man.isWifiEnabled())
1575 Utils.sendMessage(context, replyTo, wifi_on);
1576 else
1577 Utils.sendMessage(context, replyTo, wifi_off);
1578 }
1579
1580 /**
1581 * Set the WiFi state.
1582 *
1583 * @param context Context instance
1584 * @param replyTo reply Address
1585 * @param on the requested WiFi state
1586 */
1587 public static void wifi(final Context context, final Address replyTo, final boolean on){
1588 final WifiManager man=(WifiManager) context.getSystemService(Context.WIFI_SERVICE);
1589 man.setWifiEnabled(on);
1590 if(on)
1591 Utils.sendMessage(context, replyTo, enabling_wifi);
1592 else
1593 Utils.sendMessage(context, replyTo, disabling_wifi);
1594 }
1595
1596 /**
1597 * Factory reset the phone, optionally deleting the SD card too.
1598 *
1599 * @param context Context instance
1600 * @param type {@link Utils.WipeType} instance
1601 * @throws SecurityException if FonBot does not have device administration permissions
1602 */
1603 @SuppressLint("InlinedApi")
1604 public static void wipe(final Context context, final WipeType type) throws SecurityException{
1605 final DevicePolicyManager dpm=(DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
1606
1607 switch(type){
1608 case DATA:
1609 dpm.wipeData(0);
1610 break;
1611 case FULL:
1612 dpm.wipeData(DevicePolicyManager.WIPE_EXTERNAL_STORAGE);
1613 break;
1614 }
1615 }
1616
1617 /**
1618 * Disable a Command. The command cannot be used until enabled again with the {@link Utils.Command#ENABLE ENABLE} command.
1619 *
1620 * @param context Context instance
1621 * @param replyTo reply Address
1622 * @param command Command to disable
1623 */
1624 public static void disable(final Context context, final Address replyTo, final Command command){
1625 PreferenceManager.getDefaultSharedPreferences(context).edit()
1626 .putBoolean(command+"disabled", true).commit();
1627 Utils.sendMessage(context, replyTo, command_disabled, command);
1628 }
1629
1630 /**
1631 * Re-enable a disabled Command.
1632 *
1633 * @param context Context instance
1634 * @param replyTo reply Address
1635 * @param command Command to re-enable
1636 */
1637 public static void enable(final Context context, final Address replyTo, final Command command){
1638 PreferenceManager.getDefaultSharedPreferences(context).edit()
1639 .remove(command+"disabled").commit();
1640 Utils.sendMessage(context, replyTo, command_enabled, command);
1641
1642 }
1643
1644 /**
1645 * Check whether a Command is disabled.
1646 *
1647 * @param context Context instance
1648 * @param command Command to check
1649 * @return true if the Command is disabled, false otherwise
1650 */
1651 public static boolean isCommandDisabled(final Context context, final Command command){
1652 return PreferenceManager.getDefaultSharedPreferences(context).getBoolean(command+"disabled", false);
1653 }
1654
1655 /**
1656 * Poll the server for pending commands.
1657 *
1658 * @param context Context instance
1659 * @param replyTo reply Address
1660 */
1661 public static void poll(final Context context, final Address replyTo) {
1662 Utils.sendMessage(context, replyTo, polling_server);
1663 context.startService(new Intent(context, FonBotMainService.class));
1664 }
1665
1666 /**
1667 * Get an instance of {@link ITelephony}
1668 *
1669 * @param context Context instance
1670 * @return an instance of {@link ITelephony}
1671 * @throws NoSuchMethodException thrown by reflection
1672 * @throws IllegalArgumentException thrown by reflection
1673 * @throws IllegalAccessException thrown by reflection
1674 * @throws InvocationTargetException thrown by reflection
1675 */
1676 private static ITelephony getITelephony(final Context context) throws NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException{
1677 final TelephonyManager man=(TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
1678 final Method m=TelephonyManager.class.getDeclaredMethod("getITelephony");
1679 m.setAccessible(true);
1680 return toNonNull((ITelephony) m.invoke(man));
1681 }
1682
1683 /**
1684 * Hang up the phone.
1685 *
1686 * @param context Context instance
1687 * @param replyTo reply Address
1688 */
1689 public static void hangup(final Context context, final Address replyTo){
1690 try{
1691 getITelephony(context).endCall();
1692 } catch(Exception e){
1693 Utils.sendMessage(context, replyTo, exception_while_hanging_up_call,
1694 e.getClass().getName(), e.getMessage());
1695 }
1696 }
1697
1698 /**
1699 * Answer the phone if it is ringing.
1700 *
1701 * @param context Context instance
1702 * @param replyTo reply Address
1703 */
1704 public static void answer(final Context context, final Address replyTo){
1705 try{
1706 getITelephony(context).answerRingingCall();
1707 } catch(Exception e){
1708 Utils.sendMessage(context, replyTo, exception_while_answering_call,
1709 e.getClass().getName(), e.getMessage());
1710 }
1711 }
1712
1713 /**
1714 * Launch a package.
1715 *
1716 * @param context Context instance
1717 * @param replyTo reply Address
1718 * @param pkg name of the package to launch
1719 */
1720 public static void launch(final Context context, final Address replyTo, final String pkg){
1721 final Intent intent=context.getPackageManager().getLaunchIntentForPackage(pkg);
1722 if(intent==null){
1723 Utils.sendMessage(context, replyTo, no_such_package);
1724 return;
1725 }
1726 context.startActivity(intent);
1727 Utils.sendMessage(context, replyTo, app_launched);
1728 }
1729
1730 /**
1731 * Get the mobile data enabled status.
1732 *
1733 * @param context Context instance
1734 * @param replyTo reply Address
1735 */
1736 public static void data(final Context context, final Address replyTo){
1737 try{
1738 final ConnectivityManager man=(ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
1739 final Method m=ConnectivityManager.class.getDeclaredMethod("getMobileDataEnabled");
1740 m.setAccessible(true);
1741 if(((Boolean)m.invoke(man)).booleanValue())
1742 Utils.sendMessage(context, replyTo, data_on);
1743 else
1744 Utils.sendMessage(context, replyTo, data_off);
1745 } catch(Exception e){
1746 Utils.sendMessage(context, replyTo, exception_while_determining_data_state,
1747 e.getClass().getName(), e.getMessage());
1748 }
1749 }
1750
1751 /**
1752 * Set the mobile data enabled status.
1753 *
1754 * @param context Context instance
1755 * @param replyTo reply Address
1756 * @param enable whether to enable mobile data
1757 */
1758 public static void data(final Context context, final Address replyTo, final boolean enable) {
1759 try{
1760 if(enable){
1761 getITelephony(context).enableDataConnectivity();
1762 Utils.sendMessage(context, replyTo, enabling_data);
1763 } else {
1764 getITelephony(context).disableDataConnectivity();
1765 Utils.sendMessage(context, replyTo, disabling_data);
1766 }
1767 } catch(Exception e){
1768 Utils.sendMessage(context, replyTo, exception_while_getting_itelephony,
1769 e.getClass().getName(), e.getMessage());
1770 }
1771 }
1772
1773 /**
1774 * Get the GPS status.
1775 *
1776 * @param context Context instance
1777 * @param replyTo reply Address
1778 */
1779 public static void gps(final Context context, final Address replyTo){
1780 if(Secure.isLocationProviderEnabled(context.getContentResolver(), LocationManager.GPS_PROVIDER))
1781 Utils.sendMessage(context, replyTo, gps_on);
1782 else
1783 Utils.sendMessage(context, replyTo, gps_off);
1784 }
1785
1786 /**
1787 * Set the GPS status.
1788 *
1789 * @param context Context instance
1790 * @param replyTo reply Address
1791 * @param enabled requested GPS status
1792 */
1793 public static void gps(final Context context, final Address replyTo, final boolean enabled) {
1794 Secure.setLocationProviderEnabled(context.getContentResolver(), LocationManager.GPS_PROVIDER, enabled);
1795 if(enabled)
1796 Utils.sendMessage(context, replyTo, enabling_gps);
1797 else
1798 Utils.sendMessage(context, replyTo, disabling_gps);
1799 }
1800
1801 /**
1802 * Get the Google location (aka network location) state.
1803 *
1804 * @param context Context instance
1805 * @param replyTo reply Address
1806 */
1807 public static void glocation(final Context context, final Address replyTo){
1808 if(Secure.isLocationProviderEnabled(context.getContentResolver(), LocationManager.NETWORK_PROVIDER))
1809 Utils.sendMessage(context, replyTo, network_location_on);
1810 else
1811 Utils.sendMessage(context, replyTo, network_location_off);
1812 }
1813
1814 /**
1815 * Set the Google location (aka network location) state.
1816 *
1817 * @param context Context instance
1818 * @param replyTo reply Address
1819 * @param enabled requested Google location state
1820 */
1821 public static void glocation(final Context context, final Address replyTo, final boolean enabled) {
1822 Secure.setLocationProviderEnabled(context.getContentResolver(), LocationManager.NETWORK_PROVIDER, enabled);
1823 if(enabled)
1824 Utils.sendMessage(context, replyTo, enabling_network_location);
1825 else
1826 Utils.sendMessage(context, replyTo, disabling_network_location);
1827 }
1828
1829 /**
1830 * Reboot the phone.
1831 *
1832 * @param context Context instance
1833 * @param replyTo reply Address
1834 * @param reason reboot reason
1835 *
1836 * @see PowerManager#reboot(String)
1837 */
1838 public static void reboot(final Context context, final Address replyTo, final @Nullable String reason) {
1839 final PowerManager pm=(PowerManager) context.getSystemService(Context.POWER_SERVICE);
1840 Utils.sendMessage(context, replyTo, rebooting);
1841 pm.reboot(reason);
1842 }
1843
1844 /**
1845 * Cancel a notification.
1846 *
1847 * @param context Context instance
1848 * @param replyTo reply Address
1849 * @param id notification ID
1850 */
1851 public static void notify(final Context context, final Address replyTo, final int id) {
1852 final NotificationManager man=(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
1853 man.cancel(id);
1854 Utils.sendMessage(context, replyTo, notification_canceled);
1855 }
1856
1857 /**
1858 * Show a notification.
1859 *
1860 * @param context Context instance
1861 * @param replyTo reply Address
1862 * @param id notification ID
1863 * @param title notificationO title
1864 * @param text notification text
1865 */
1866 public static void notify(final Context context, final Address replyTo, final int id, final String title, final String text) {
1867 final NotificationManager man=(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
1868 man.notify(id, new NotificationCompat.Builder(context).
1869 setContentTitle(title).
1870 setContentText(text).
1871 setSmallIcon(android.R.drawable.stat_notify_sync_noanim).
1872 build());
1873 Utils.sendMessage(context, replyTo, notification_shown);
1874 }
1875
1876 /**
1877 * Take a screen capture. Uses the screencap utility and requires root.
1878 *
1879 * @param context Context instance
1880 * @param replyTo reply Address
1881 * @param filename capture file location
1882 */
1883 public static void screencap(final Context context, final Address replyTo, final String filename){
1884 new Thread(new ScreencapRunnable(context, replyTo, filename)).start();
1885 }
1886
1887 /**
1888 * Toggle the torch state using the Torch (net.cactii.torch2) app.
1889 *
1890 * @param context Context instance
1891 * @param replyTo reply Address
1892 */
1893 public static void torch(final Context context, final Address replyTo){
1894 context.sendBroadcast(new Intent("net.cactii.flash2.TOGGLE_FLASHLIGHT"));
1895 Utils.sendMessage(context, replyTo, toggling_torch_state);
1896 }
1897
1898 /**
1899 * Download a file from a given URL to a given filename
1900 *
1901 * @param context Context instance
1902 * @param replyTo reply Address
1903 * @param filename filename to save to
1904 * @param hostname hostname to download from
1905 * @param port port to download from
1906 */
1907 public static void getfile(final Context context, final Address replyTo, final String filename, final String hostname, final int port){
1908 new GetfileExecutableRunnable(context, replyTo, filename, hostname, port).execute();
1909 }
1910 }
This page took 0.071506 seconds and 3 git commands to generate.