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