8fa72677a31461728dbd49e0dd6e437634f11d8c
[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, toNonNull(context.getString(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, toNonNull(context.getString(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, toNonNull(context.getString(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, toNonNull(context.getString(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 SMSLOG:
638 Utils.sendMessage(context, replyTo, smslog_help);
639 break;
640 case SPEAK:
641 Utils.sendMessage(context, replyTo, speak_help);
642 break;
643 case TOAST:
644 Utils.sendMessage(context, replyTo, toast_help);
645 break;
646 case VIBRATE:
647 Utils.sendMessage(context, replyTo, vibrate_help);
648 break;
649 case VIEW:
650 Utils.sendMessage(context, replyTo, view_help);
651 break;
652 case WIFI:
653 Utils.sendMessage(context, replyTo, wifi_help);
654 break;
655 case WIPE:
656 Utils.sendMessage(context, replyTo, wipe_help, Utils.WIPE_CONFIRM_STRING);
657 break;
658 case REBOOT:
659 Utils.sendMessage(context, replyTo, reboot_help);
660 break;
661 case NOTIFY:
662 Utils.sendMessage(context, replyTo, notify_help);
663 break;
664 case SCREENCAP:
665 Utils.sendMessage(context, replyTo, screencap_help);
666 break;
667 case TORCH:
668 Utils.sendMessage(context, replyTo, torch_help);
669 break;
670 case GETFILE:
671 Utils.sendMessage(context, replyTo, getfile_help);
672 break;
673 case SH:
674 Utils.sendMessage(context, replyTo, sh_help);
675 break;
676 case ROOTSH:
677 Utils.sendMessage(context, replyTo, rootsh_help);
678 break;
679 default:
680 Utils.sendMessage(context, replyTo, command_not_documented);
681 }
682 }
683
684 /**
685 * Camera instance.
686 *
687 * @see #startCamera(Context, Address)
688 * @see #stopCamera()
689 */
690 private static Camera camera;
691 /**
692 * Ringtone used by the {@link Utils.Command#RING RING} command.
693 *
694 * @see #setupRingtone(Context)
695 */
696 private static Ringtone ringtone;
697 /**
698 * Saved ringer volume.
699 *
700 * @see #startAlarm(Context, Address)
701 * @see #stopAlarm(Context, Address)
702 */
703 private static int savedRingVolume;
704 /**
705 * Saved ringer mode.
706 *
707 * @see #startAlarm(Context, Address)
708 * @see #stopAlarm(Context, Address)
709 */
710 private static int savedRingerMode;
711
712 /** Private constructor */
713 private Heavy(){
714 //do nothing
715 }
716
717 /**
718 * Convert a phone number type to a string
719 *
720 * @param context Context instance
721 * @param type phone number type
722 * @param label name of a custom phone type
723 * @return the phone number type
724 */
725 private static @Nullable String phoneNumberType(final Context context, final int type, final @Nullable String label) {
726 switch(type){
727 case BaseTypes.TYPE_CUSTOM:
728 return label;
729 case Phone.TYPE_ASSISTANT:
730 return context.getString(phone_numer_type_assistant);
731 case Phone.TYPE_CALLBACK:
732 return context.getString(phone_number_type_callback);
733 case Phone.TYPE_CAR:
734 return context.getString(phone_number_type_car);
735 case Phone.TYPE_COMPANY_MAIN:
736 return context.getString(phone_number_type_company_main);
737 case Phone.TYPE_FAX_HOME:
738 return context.getString(phone_number_type_home_fax);
739 case Phone.TYPE_FAX_WORK:
740 return context.getString(phone_number_type_work_fax);
741 case Phone.TYPE_HOME:
742 return context.getString(phone_number_type_home);
743 case Phone.TYPE_ISDN:
744 return context.getString(phone_number_type_isdn);
745 case Phone.TYPE_MAIN:
746 return context.getString(phone_number_type_main);
747 case Phone.TYPE_MMS:
748 return context.getString(phone_number_type_mms);
749 case Phone.TYPE_MOBILE:
750 return context.getString(phone_number_type_mobile);
751 case Phone.TYPE_OTHER:
752 return context.getString(phone_number_type_other);
753 case Phone.TYPE_OTHER_FAX:
754 return context.getString(phone_number_type_other_fax);
755 case Phone.TYPE_PAGER:
756 return context.getString(phone_number_type_pager);
757 case Phone.TYPE_RADIO:
758 return context.getString(phone_number_type_radio);
759 case Phone.TYPE_TELEX:
760 return context.getString(phone_number_type_telex);
761 case Phone.TYPE_TTY_TDD:
762 return context.getString(phone_number_type_textphone);
763 case Phone.TYPE_WORK:
764 return context.getString(phone_number_type_work);
765 case Phone.TYPE_WORK_MOBILE:
766 return context.getString(phone_number_type_work_mobile);
767 case Phone.TYPE_WORK_PAGER:
768 return context.getString(phone_number_type_work_pager);
769 }
770
771 return context.getString(phone_number_type_unknown, Integer.valueOf(type));
772 }
773
774 /**
775 * Setup the ringtone used by the {@link Utils.Command#RING RING} command
776 *
777 * @param context Context
778 */
779 private static void setupRingtone(final Context context){
780 if(ringtone==null){//NOPMD not supposed to be thread-safe
781 final Uri alert=RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE);
782 ringtone=RingtoneManager.getRingtone(context, alert);
783 }
784 }
785
786 /**
787 * Make the phone start ringing. Turns up the volume and sets the ringer mode to NORMAL
788 *
789 * @param context Context instance
790 * @param replyTo reply Address
791 */
792 private static void startAlarm(final Context context, final Address replyTo){
793 Utils.registerOngoing(context, toNonNull(OngoingEvent.RING));
794 final AudioManager man=(AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
795 savedRingerMode=man.getRingerMode();
796 man.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
797 savedRingVolume=man.getStreamVolume(AudioManager.STREAM_RING);
798 man.setStreamVolume(AudioManager.STREAM_RING, man.getStreamMaxVolume(AudioManager.STREAM_RING), 0);
799 Utils.sendMessage(context, replyTo, ringing);
800 ringtone.play();
801 }
802
803 /**
804 * Get a camera instance.
805 *
806 * @param context Context instance
807 * @param replyTo reply Address
808 */
809 private static void startCamera(final Context context, final Address replyTo, final int cameraNumber){
810 if(camera!=null)
811 return;
812 try{
813 try{
814 camera=Camera.open(cameraNumber);
815 } catch (Exception ex){
816 camera=Camera.open();
817 }
818 } catch (Exception e){
819 Utils.sendMessage(context, replyTo, cannot_grab_camera);
820 }
821 }
822
823 /**
824 * Make the phone stop ringing. Restores the volume and ringer mode.
825 *
826 * @param context Context instance
827 * @param replyTo reply Address
828 */
829 private static void stopAlarm(final Context context, final Address replyTo){
830 Utils.unregisterOngoing(context, toNonNull(OngoingEvent.RING));
831 final AudioManager man=(AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
832 Utils.sendMessage(context, replyTo, no_longer_ringing);
833 ringtone.stop();
834 man.setStreamVolume(AudioManager.STREAM_RING, savedRingVolume, 0);
835 man.setRingerMode(savedRingerMode);
836 }
837
838 /**
839 * Release the previously grabbed camera instance
840 *
841 * @see #startCamera(Context, Address)
842 */
843 private static void stopCamera(){
844 if(camera==null)
845 return;
846 camera.release();
847 camera=null;
848 }
849
850 /**
851 * Send battery status information to an Address
852 *
853 * @param context Context instance
854 * @param replyTo destination Address
855 *
856 * @see #describeBatteryLevel(Context, Address, MessageType)
857 */
858 public static void batt(final Context context, final Address replyTo){
859 describeBatteryLevel(context, replyTo, null);
860 }
861
862 /**
863 * Show the bluetooth radio status.
864 *
865 * @param context Context instance
866 * @param replyTo destination Address
867 */
868 public static void bluetooth(final Context context, final Address replyTo) {
869 final BluetoothAdapter adapter=BluetoothAdapter.getDefaultAdapter();
870 if(adapter==null){
871 Utils.sendMessage(context, replyTo, no_bluetooth_adapter);
872 return;
873 }
874
875 if(adapter.isEnabled())
876 Utils.sendMessage(context, replyTo, bluetooth_on);
877 else
878 Utils.sendMessage(context, replyTo, bluetooth_off);
879 }
880
881 /**
882 * Set the bluetooth radio status.
883 *
884 * @param context Context instance
885 * @param replyTo destination Address
886 * @param on the requested radio status
887 */
888 public static void bluetooth(final Context context, final Address replyTo, final boolean on){
889 final BluetoothAdapter adapter=BluetoothAdapter.getDefaultAdapter();
890 if(adapter==null){
891 Utils.sendMessage(context, replyTo, no_bluetooth_adapter);
892 return;
893 }
894
895 if(on) {
896 adapter.enable();
897 Utils.sendMessage(context, replyTo, enabling_bluetooth);
898 }
899 else {
900 adapter.disable();
901 Utils.sendMessage(context, replyTo, disabling_bluetooth);
902 }
903 }
904
905 /**
906 * Cancel an ongoing event.
907 *
908 * @param context Context instance
909 * @param event the event to cancel
910 */
911 public static void cancelOngoing(final Context context, final OngoingEvent event){
912 switch(event){
913 case LOCATION:
914 nolocation(context, toNonNull(Address.BLACKHOLE));
915 break;
916 case RING:
917 ring(context, toNonNull(Address.BLACKHOLE), false);
918 break;
919 }
920 }
921
922 /**
923 * Send the last calls to an Address.
924 *
925 * @param context Context instance
926 * @param replyTo destination Address
927 * @param numCalls how many calls to send
928 */
929 public static void calllog(final Context context, final Address replyTo, final int numCalls) {
930 final String[] fields = {
931 Calls.TYPE, Calls.NUMBER, Calls.CACHED_NAME, Calls.DURATION, Calls.DATE
932 };
933
934 final Cursor cursor = context.getContentResolver().query(
935 Calls.CONTENT_URI,
936 fields,
937 null,
938 null,
939 Calls.DATE + " DESC"
940 );
941
942 if (cursor.moveToFirst()) {
943 do {
944 final StringBuilder sb=new StringBuilder(50);//NOPMD different strings
945 final int type=cursor.getInt(0);
946 final String from=cursor.getString(1);
947
948 switch(type){
949 case Calls.INCOMING_TYPE:
950 sb.append(context.getString(incoming_call_from, from));
951 break;
952 case Calls.MISSED_TYPE:
953 sb.append(context.getString(missed_call_from, from));
954 break;
955 case Calls.OUTGOING_TYPE:
956 sb.append(context.getString(outgoing_call_to, from));
957 break;
958 }
959
960 if (cursor.getString(2) != null)
961 sb.append('(').append(cursor.getString(2)).append(") ");
962
963 sb.append(context.getString(duration_seconds_starting_at,
964 Long.valueOf(cursor.getLong(3)),
965 new Date(cursor.getLong(4))));
966
967 Utils.sendMessage(context, replyTo, toNonNull(sb.toString()));
968 } while (cursor.moveToNext() && cursor.getPosition() < numCalls);
969 }
970
971 cursor.close();
972 }
973
974 /**
975 * Search for contacts by name/nickname and send matching entries to an Address.
976 *
977 * @param context Context instance
978 * @param replyTo destination Address
979 * @param name name/nickname part to search for
980 */
981 @SuppressLint("StringFormatMatches")
982 public static void contacts(final Context context, final Address replyTo, final String name){
983 final Cursor cursor=context.getContentResolver().query(Uri.withAppendedPath(
984 Contacts.CONTENT_FILTER_URI, name),
985 new String[]{Contacts.DISPLAY_NAME, BaseColumns._ID, Contacts.LOOKUP_KEY},
986 null, null, Contacts.DISPLAY_NAME);
987
988 if(cursor.getCount()==0)
989 Utils.sendMessage(context, replyTo, no_matching_contacts_found);
990
991 while(cursor.moveToNext()){
992 final String[] fields = {
993 CommonDataKinds.Phone.NUMBER,
994 CommonDataKinds.Phone.TYPE,
995 CommonDataKinds.Phone.LABEL,
996 };
997
998 final Cursor inCursor=context.getContentResolver().query(Data.CONTENT_URI,
999 fields,
1000 Data.CONTACT_ID+" = ? AND "+Data.MIMETYPE+ " = ?",
1001 new String[]{Long.toString(cursor.getLong(1)), CommonDataKinds.Phone.CONTENT_ITEM_TYPE},
1002 CommonDataKinds.Phone.LABEL);
1003
1004 while(inCursor.moveToNext())
1005 Utils.sendMessage(context, replyTo, toNonNull(context.getString(contact_info,
1006 cursor.getString(0),
1007 inCursor.getString(0),
1008 phoneNumberType(context, inCursor.getInt(1), inCursor.getString(2)))));
1009
1010 inCursor.close();
1011 }
1012
1013 cursor.close();
1014 }
1015
1016 /**
1017 * Send battery status information to an Address or as a notification
1018 *
1019 * @param context Context instance
1020 * @param replyTo Address to send the information to, if sending to a direct address. Null otherwise.
1021 * @param type Notification type, if sending as a notification. Null otherwise.
1022 */
1023 public static void describeBatteryLevel(final Context context, final @Nullable Address replyTo, final @Nullable MessageType type) {
1024 if(replyTo==null&&type==null)
1025 return;
1026 final Intent intent=context.registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
1027 if(intent==null)
1028 return;
1029 final double level=intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0);
1030 final int scale=intent.getIntExtra(BatteryManager.EXTRA_SCALE, 100);
1031 final int plugged=intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0);
1032 final int status=intent.getIntExtra(BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_UNKNOWN);
1033 final int temp=intent.getIntExtra(BatteryManager.EXTRA_TEMPERATURE, 0);
1034 final int volt=intent.getIntExtra(BatteryManager.EXTRA_VOLTAGE, 0);
1035
1036 final StringBuilder sb=new StringBuilder(100);
1037 sb.append(context.getString(battery_level, Double.valueOf(level*100/scale)));
1038
1039 switch(plugged){
1040 case 0:
1041 sb.append(context.getString(not_plugged_in));
1042 break;
1043 case BatteryManager.BATTERY_PLUGGED_AC:
1044 sb.append(context.getString(plugged_in_ac));
1045 break;
1046 case BatteryManager.BATTERY_PLUGGED_USB:
1047 sb.append(context.getString(plugged_in_usb));
1048 break;
1049 case BatteryManager.BATTERY_PLUGGED_WIRELESS:
1050 sb.append(context.getString(plugged_in_wireless));
1051 break;
1052 }
1053
1054 switch(status){
1055 case BatteryManager.BATTERY_STATUS_CHARGING:
1056 sb.append(context.getString(status_charging));
1057 break;
1058 case BatteryManager.BATTERY_STATUS_DISCHARGING:
1059 sb.append(context.getString(status_discharging));
1060 break;
1061 case BatteryManager.BATTERY_STATUS_FULL:
1062 sb.append(context.getString(status_full));
1063 break;
1064 case BatteryManager.BATTERY_STATUS_NOT_CHARGING:
1065 sb.append(context.getString(status_not_charging));
1066 break;
1067 case BatteryManager.BATTERY_STATUS_UNKNOWN:
1068 sb.append(context.getString(status_unknown));
1069 break;
1070 }
1071
1072 sb.append(context.getString(temperature, Integer.valueOf(temp)));
1073
1074 sb.append(context.getString(voltage, Integer.valueOf(volt)));
1075 if(type==null)
1076 Utils.sendMessage(context, toNonNull(replyTo), toNonNull(sb.toString()));
1077 else
1078 Utils.sendMessage(context, type, toNonNull(sb.toString()));
1079 }
1080
1081 /**
1082 * Dial a phone number.
1083 *
1084 * @param context Context instance
1085 * @param replyTo reply Address
1086 * @param nr phone number to dial
1087 */
1088 public static void dial(final Context context, final Address replyTo, final String nr){
1089 final Intent intent=new Intent(Intent.ACTION_CALL,Uri.parse("tel:"+nr));
1090 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1091 final String name=Utils.callerId(context, nr);
1092 if(name==null)
1093 Utils.sendMessage(context, replyTo, dialing, nr);
1094 else
1095 Utils.sendMessage(context, replyTo, dialing, nr+" ("+name+")");
1096 context.startActivity(intent);
1097 }
1098
1099 /**
1100 * Show a dialog with a message and a list of buttons.
1101 *
1102 * @param context Context instance
1103 * @param replyTo reply Address
1104 * @param message dialog message
1105 * @param buttons dialog buttons
1106 */
1107 public static void dialog(final Context context, final Address replyTo, final String message, final String[] buttons){
1108 final Intent intent=new Intent(context, DialogActivity.class);
1109 intent.putExtra(DialogActivity.EXTRA_MESSAGE, message);
1110 intent.putExtra(DialogActivity.EXTRA_BUTTONS, buttons);
1111 intent.putExtra(DialogActivity.EXTRA_REPLYTO, replyTo.toString());
1112 intent.addFlags(
1113 Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS|
1114 Intent.FLAG_ACTIVITY_NEW_TASK|
1115 Intent.FLAG_ACTIVITY_NO_USER_ACTION|
1116 Intent.FLAG_FROM_BACKGROUND);
1117 Utils.sendMessage(context, toNonNull(replyTo), showing_dialog);
1118 context.startActivity(intent);
1119 }
1120
1121 /**
1122 * Turns the flashlight on or off.
1123 *
1124 * @param context Context instance
1125 * @param replyTo reply Address
1126 * @param on requested flashlight state
1127 */
1128 public static void flash(final Context context, final Address replyTo, final boolean on){
1129 startCamera(context, replyTo, 0);
1130 if(camera==null)
1131 return;
1132 final Camera.Parameters parms=camera.getParameters();
1133 if(on){
1134 parms.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
1135 camera.setParameters(parms);
1136 } else {
1137 parms.setFlashMode(Camera.Parameters.FLASH_MODE_AUTO);
1138 camera.setParameters(parms);
1139 stopCamera();
1140 }
1141 }
1142
1143 /**
1144 * Start sending location updates to an Address.
1145 *
1146 * @param context Context instance
1147 * @param replyTo destination Address
1148 * @param provider LocationProvider
1149 * @param minTime minimum time between two consecutive updates (in ms)
1150 * @param minDistance minimum distance between two consecutive updates (in meters)
1151 *
1152 * @see LocationManager#requestLocationUpdates(String, long, float, LocationListener)
1153 */
1154 public static void location(final Context context, final Address replyTo, final String provider,final long minTime,final float minDistance){
1155 final LocationManager man=(LocationManager) context.getApplicationContext().getSystemService(Context.LOCATION_SERVICE);
1156 if(locationListener!=null)
1157 nolocation(context, toNonNull(Address.BLACKHOLE));
1158 Utils.registerOngoing(context, toNonNull(OngoingEvent.LOCATION));
1159 locationListener=new FonBotLocationListener(context, replyTo);
1160 man.removeUpdates(locationListener);
1161 final Location lastKnownLocation=man.getLastKnownLocation(provider);
1162 if(lastKnownLocation!=null){
1163 Utils.sendMessage(context, replyTo, last_known_location);
1164 locationListener.onLocationChanged(lastKnownLocation);
1165 }
1166 Utils.sendMessage(context, replyTo, listening_for_location_updates);
1167 man.requestLocationUpdates(provider, minTime, minDistance, locationListener);
1168 }
1169
1170 /**
1171 * Lock the phone.
1172 *
1173 * @param context Context instance
1174 * @param replyTo reply Address
1175 */
1176 public static void lock(final Context context, final Address replyTo) {
1177 final DevicePolicyManager dpm=(DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
1178 dpm.lockNow();
1179 Utils.sendMessage(context, replyTo, device_locked);
1180 }
1181
1182 /**
1183 * Send a command to a running instance of the music player
1184 *
1185 * @param context Context instance
1186 * @param replyTo reply Address
1187 * @param command command to send
1188 */
1189 public static void musicPlayerCommand(final Context context, final Address replyTo, final String command) {
1190 final Intent intent=new Intent("com.android.music.musicservicecommand");
1191 intent.putExtra("command", command);
1192 context.sendBroadcast(intent);
1193 Utils.sendMessage(context, replyTo, command_sent);
1194 }
1195
1196 /**
1197 * Send a file to a server.
1198 *
1199 * @param context Context instance
1200 * @param replyTo reply Address
1201 * @param filename file to send
1202 * @param hostname server hostname
1203 * @param port server port
1204 */
1205 public static void ncfile(final Context context, final Address replyTo, final String filename,final String hostname,final int port){
1206 new NcfileExecutableRunnable(context, replyTo, filename, hostname, port).execute();
1207 }
1208
1209 /**
1210 * Stop sending location updates.
1211 *
1212 * @param context Context instance
1213 * @param replyTo reply Address
1214 */
1215 public static void nolocation(final Context context, final Address replyTo){
1216 Utils.unregisterOngoing(context, toNonNull(OngoingEvent.LOCATION));
1217 final LocationManager man=(LocationManager) context.getApplicationContext().getSystemService(Context.LOCATION_SERVICE);
1218 man.removeUpdates(locationListener);
1219 locationListener=null;
1220 Utils.sendMessage(context, replyTo, no_longer_listening_for_location_updates);
1221 }
1222
1223 /**
1224 * Take a photo and send it to a server.
1225 *
1226 * @param context Context instance
1227 * @param replyTo reply Address
1228 * @param cameraNumber camera to take photo with
1229 * @param hostname server hostname
1230 * @param port server port
1231 */
1232 public static void photo(final Context context, final Address replyTo, final int cameraNumber, final String hostname, final int port){
1233 startCamera(context, replyTo, cameraNumber);
1234 if(camera==null)
1235 return;
1236 final Camera.Parameters parms=camera.getParameters();
1237 parms.setJpegQuality(70);
1238 parms.setPictureFormat(ImageFormat.JPEG);
1239 camera.setParameters(parms);
1240
1241 final SurfaceView fakeView=new SurfaceView(context);
1242 try {
1243 camera.setPreviewDisplay(fakeView.getHolder());
1244 } catch (IOException e) {
1245 Utils.sendMessage(context, replyTo, error_setting_preview_display);
1246 return;
1247 }
1248 camera.startPreview();
1249 final Handler handler=new Handler();
1250
1251 new Thread(new Runnable() {
1252 @Override
1253 public void run() {
1254 SystemClock.sleep(2000);
1255
1256 handler.post(new Runnable() {
1257 @Override
1258 public void run() {
1259 try {
1260 camera.takePicture(null, null, new FonBotPictureCallback(context, replyTo, hostname, port));
1261 } catch(Exception e){
1262 Utils.sendMessage(context, replyTo, error_while_processing_command, e.getClass().getName(), e.getMessage());
1263 }
1264 }
1265 });
1266 }
1267 }).start();
1268 }
1269
1270 /**
1271 * Send a directory listing to an Address
1272 *
1273 * @param context Context instance
1274 * @param replyTo destination Address
1275 * @param directory directory to list
1276 */
1277 public static void ls(final Context context, final Address replyTo, final String directory) {
1278 final File[] files=new File(directory).listFiles();
1279 if(files==null){
1280 Utils.sendMessage(context, replyTo, string_is_not_a_directory, directory);
1281 return;
1282 }
1283
1284 final StringBuilder sb=new StringBuilder(context.getString(files_in_directory,directory));
1285 for(final File file : files){
1286 sb.append(file.getName());
1287 if(file.isDirectory())
1288 sb.append('/');
1289 sb.append(" ");
1290 }
1291
1292 Utils.sendMessage(context, replyTo, toNonNull(sb.toString()));
1293 }
1294
1295 /**
1296 * Make the phone start ringing if it is not ringing or stop ringing if it is.
1297 *
1298 * @param context Context instance
1299 * @param replyTo reply Address
1300 */
1301 public static void ring(final Context context, final Address replyTo){
1302 setupRingtone(context);
1303 if(ringtone==null){
1304 Utils.sendMessage(context, replyTo, no_ringtone_found);
1305 return;
1306 }
1307 if(ringtone.isPlaying())
1308 stopAlarm(context, replyTo);
1309 else
1310 startAlarm(context, replyTo);
1311 }
1312
1313 /**
1314 * Make the phone start/stop ringing.
1315 *
1316 * @param context Context instance
1317 * @param replyTo reply Address
1318 * @param on true if the phone should start ringing, false otherwise
1319 */
1320 public static void ring(final Context context, final Address replyTo, final boolean on){
1321 setupRingtone(context);
1322 if(ringtone==null){
1323 Utils.sendMessage(context, replyTo, no_ringtone_found);
1324 return;
1325 }
1326 if(on&&!ringtone.isPlaying())
1327 startAlarm(context, replyTo);
1328 else if(ringtone.isPlaying()&&!on)
1329 stopAlarm(context, replyTo);
1330 }
1331
1332 /**
1333 * Send the current ringer mode to an Address
1334 *
1335 * @param context Context instance
1336 * @param replyTo destination Address
1337 */
1338 public static void ringer(final Context context, final Address replyTo){
1339 final AudioManager man=(AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
1340 switch(man.getRingerMode()){
1341 case AudioManager.RINGER_MODE_NORMAL:
1342 Utils.sendMessage(context, replyTo, ringer_mode_normal);
1343 break;
1344 case AudioManager.RINGER_MODE_VIBRATE:
1345 Utils.sendMessage(context, replyTo, ringer_mode_vibrate);
1346 break;
1347 case AudioManager.RINGER_MODE_SILENT:
1348 Utils.sendMessage(context, replyTo, ringer_mode_silent);
1349 break;
1350 default:
1351 Utils.sendMessage(context, replyTo, unknown_ringer_mode);
1352 }
1353 }
1354
1355 /**
1356 * Set the ringer mode.
1357 *
1358 * @param context Context instance
1359 * @param replyTo reply Address
1360 * @param ringerMode requested ringer mode
1361 *
1362 * @see Utils.RingerMode
1363 */
1364 public static void ringer(final Context context, final Address replyTo, final int ringerMode){
1365 final AudioManager man=(AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
1366 man.setRingerMode(ringerMode);
1367 ringer(context, replyTo);
1368 }
1369
1370 /**
1371 * Remove a file or empty directory.
1372 *
1373 * @param context Context instance
1374 * @param replyTo reply Address
1375 * @param filename file/empty directory to delete
1376 */
1377 public static void rm(final Context context, final Address replyTo, final String filename){
1378 if(new File(filename).delete())
1379 Utils.sendMessage(context, replyTo, file_deleted);
1380 else
1381 Utils.sendMessage(context, replyTo, error_while_deleting_file);
1382 }
1383
1384 /**
1385 * Clear the keyguard password.
1386 *
1387 * @param context Context instance
1388 * @param replyTo reply Address
1389 * @throws SecurityException if FonBot does not have device administration permissions
1390 */
1391 public static void setPassword(final Context context, final Address replyTo) throws SecurityException{
1392 final DevicePolicyManager dpm=(DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
1393
1394 dpm.resetPassword("", 0);
1395 Utils.sendMessage(context, replyTo, password_cleared);
1396 }
1397
1398 /**
1399 * Change the keyguard password.
1400 *
1401 * @param context Context instance
1402 * @param replyTo reply Address
1403 * @param password new password
1404 * @throws SecurityException if FonBot does not have device administration permissions
1405 */
1406 public static void setPassword(final Context context, final Address replyTo, final String password) throws SecurityException{
1407 final DevicePolicyManager dpm=(DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
1408
1409 dpm.resetPassword(password, 0);
1410 Utils.sendMessage(context, replyTo, password_set);
1411 }
1412
1413 /**
1414 * Send a text message.
1415 *
1416 * @param context Context instance
1417 * @param replyTo reply Address
1418 * @param destination destination phone number
1419 * @param text text message contents
1420 */
1421 public static void sms(final Context context, final Address replyTo, final String destination, final String text){
1422 final SmsManager manager=SmsManager.getDefault();
1423 final ArrayList<String> messages=manager.divideMessage(text);
1424 if(messages.size() > 1)
1425 Utils.sendMessage(context, replyTo, message_was_split_into_parts, Integer.valueOf(messages.size()));
1426
1427 final ArrayList<PendingIntent> sents=new ArrayList<PendingIntent>(messages.size());
1428 final ArrayList<PendingIntent> delivereds=new ArrayList<PendingIntent>(messages.size());
1429
1430 final String name=Utils.callerId(context, destination);
1431 final String fullDestination;
1432 if(name==null)
1433 fullDestination=destination;
1434 else
1435 fullDestination=destination+" ("+name+")";
1436
1437 for(int i=0;i<messages.size();i++){
1438 final Intent sent=new Intent(context,SmsStatusReceiver.class);
1439 sent.putExtra(SmsStatusReceiver.EXTRA_DESTINATION, fullDestination);
1440 sent.putExtra(SmsStatusReceiver.EXTRA_PART, i+1);
1441 sent.putExtra(SmsStatusReceiver.EXTRA_TOTAL, messages.size());
1442 sent.putExtra(SmsStatusReceiver.EXTRA_REPLY_TO, replyTo.toString());
1443 sent.setAction(SmsStatusReceiver.SENT_ACTION+i+System.currentTimeMillis());//actions must be unique
1444 sents.add(PendingIntent.getBroadcast(context, 0, sent, PendingIntent.FLAG_UPDATE_CURRENT));
1445
1446 final Intent delivered=new Intent(context, SmsStatusReceiver.class);
1447 delivered.putExtra(SmsStatusReceiver.EXTRA_DESTINATION, fullDestination);
1448 delivered.putExtra(SmsStatusReceiver.EXTRA_PART, i+1);
1449 delivered.putExtra(SmsStatusReceiver.EXTRA_TOTAL, messages.size());
1450 delivered.putExtra(SmsStatusReceiver.EXTRA_REPLY_TO, replyTo.toString());
1451 delivered.setAction(SmsStatusReceiver.DELIVERED_ACTION+i+System.currentTimeMillis());//actions must be unique
1452 delivereds.add(PendingIntent.getBroadcast(context, 0, delivered, PendingIntent.FLAG_UPDATE_CURRENT));
1453 }
1454
1455 Log.d(Heavy.class.getName(), "Sending sms to "+destination);
1456 manager.sendMultipartTextMessage(destination, null, messages, sents, delivereds);
1457 }
1458
1459 /**
1460 * Send the last SMSes to an Address.
1461 *
1462 * @param context Context instance
1463 * @param replyTo destination Address
1464 * @param numSms how many SMSes to send
1465 */
1466 public static void smslog(final Context context, final Address replyTo, final int numSms) {
1467 final String[] fields = {"type","address", "body", "date"};
1468
1469 final Cursor cursor = context.getContentResolver().query (
1470 Uri.parse("content://sms"),
1471 fields,
1472 null,
1473 null,
1474 "date DESC"
1475 );
1476
1477 if (cursor.moveToFirst()) {
1478 do {
1479 final String fromNumber=cursor.getString(1);
1480 final String from;
1481 if(fromNumber == null)
1482 from = null;
1483 else {
1484 final String name=Utils.callerId(context, Utils.toNonNull(fromNumber));
1485 if(name==null)
1486 from=fromNumber;
1487 else
1488 from=fromNumber+" ("+name+')';
1489 }
1490
1491 final String message=cursor.getString(2).replace("\n", "\n ");
1492 final Date date=new Date(cursor.getLong(3));
1493
1494 if(cursor.getInt(0)==1)
1495 Utils.sendMessage(context, replyTo, incoming_message, from, message, date);
1496 else
1497 Utils.sendMessage(context, replyTo, outgoing_message, from, message, date);
1498 } while (cursor.moveToNext() && cursor.getPosition() < numSms);
1499 }
1500
1501 cursor.close();
1502 }
1503
1504 /** TTS instance, only used by {@link #speak(Context, Address, String)} */
1505 private static TextToSpeech tts;
1506
1507 /**
1508 * Speak a String using the text-to-speech engine.
1509 *
1510 * @param context Context instance
1511 * @param replyTo reply Address
1512 * @param text text to speak
1513 */
1514 public static void speak(final Context context, final Address replyTo, final String text){
1515 tts=new TextToSpeech(context, new OnInitListener() {
1516 @Override
1517 public void onInit(final int status) {
1518 if(status==TextToSpeech.SUCCESS){
1519 Utils.sendMessage(context, replyTo, speaking);
1520 tts.speak(text, TextToSpeech.QUEUE_ADD, null);
1521 } else
1522 Utils.sendMessage(context, replyTo, tts_engine_not_available);
1523 }
1524 });
1525 }
1526
1527 /**
1528 * Show a toast notification with the default duration.
1529 *
1530 * @param context Context instance
1531 * @param replyTo reply Address
1532 * @param text toast text
1533 */
1534 public static void toast(final Context context, final Address replyTo, final String text){
1535 toast(context, replyTo, text, Toast.LENGTH_SHORT);
1536 }
1537
1538 /**
1539 * Show a toast notification.
1540 *
1541 * @param context Context instance
1542 * @param replyTo reply Address
1543 * @param text toast text
1544 * @param duration toast duration
1545 */
1546 public static void toast(final Context context, final Address replyTo, final String text, final int duration){
1547 Toast.makeText(context,text,duration).show();
1548 Utils.sendMessage(context, replyTo, toast_shown);
1549 }
1550
1551 /**
1552 * Make the phone vibrate for a number of milliseconds.
1553 *
1554 * @param context Context instance
1555 * @param replyTo reply Address
1556 * @param ms vibrate duration, in milliseconds
1557 */
1558 public static void vibrate(final Context context, final Address replyTo, final long ms){
1559 final Vibrator v=(Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
1560 Utils.sendMessage(context, replyTo, vibrating);
1561 v.vibrate(ms);
1562 }
1563
1564 /**
1565 * View an URI in an appropriate activity.
1566 *
1567 * @param context Context instance
1568 * @param replyTo reply Address
1569 * @param uri URI to view
1570 */
1571 public static void view(final Context context, final Address replyTo, final Uri uri) {
1572 try{
1573 final Intent intent=new Intent(Intent.ACTION_VIEW);
1574 intent.setData(uri);
1575 intent.setFlags(Intent.FLAG_FROM_BACKGROUND|Intent.FLAG_ACTIVITY_NEW_TASK);
1576 context.startActivity(intent);
1577 Utils.sendMessage(context, replyTo, url_opened);
1578 } catch(ActivityNotFoundException e){
1579 Utils.sendMessage(context, replyTo, no_activity_found_for_this_url);
1580 } catch(Exception e){
1581 Utils.sendMessage(context, replyTo, invalid_url);
1582 }
1583 }
1584
1585 /**
1586 * Get the current WiFi state.
1587 *
1588 * @param context Context instance
1589 * @param replyTo reply Address
1590 */
1591 public static void wifi(final Context context, final Address replyTo){
1592 final WifiManager man=(WifiManager) context.getSystemService(Context.WIFI_SERVICE);
1593 if(man.isWifiEnabled())
1594 Utils.sendMessage(context, replyTo, wifi_on);
1595 else
1596 Utils.sendMessage(context, replyTo, wifi_off);
1597 }
1598
1599 /**
1600 * Set the WiFi state.
1601 *
1602 * @param context Context instance
1603 * @param replyTo reply Address
1604 * @param on the requested WiFi state
1605 */
1606 public static void wifi(final Context context, final Address replyTo, final boolean on){
1607 final WifiManager man=(WifiManager) context.getSystemService(Context.WIFI_SERVICE);
1608 man.setWifiEnabled(on);
1609 if(on)
1610 Utils.sendMessage(context, replyTo, enabling_wifi);
1611 else
1612 Utils.sendMessage(context, replyTo, disabling_wifi);
1613 }
1614
1615 /**
1616 * Factory reset the phone, optionally deleting the SD card too.
1617 *
1618 * @param context Context instance
1619 * @param type {@link Utils.WipeType} instance
1620 * @throws SecurityException if FonBot does not have device administration permissions
1621 */
1622 @SuppressLint("InlinedApi")
1623 public static void wipe(final Context context, final WipeType type) throws SecurityException{
1624 final DevicePolicyManager dpm=(DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
1625
1626 switch(type){
1627 case DATA:
1628 dpm.wipeData(0);
1629 break;
1630 case FULL:
1631 dpm.wipeData(DevicePolicyManager.WIPE_EXTERNAL_STORAGE);
1632 break;
1633 }
1634 }
1635
1636 /**
1637 * Disable a Command. The command cannot be used until enabled again with the {@link Utils.Command#ENABLE ENABLE} command.
1638 *
1639 * @param context Context instance
1640 * @param replyTo reply Address
1641 * @param command Command to disable
1642 */
1643 public static void disable(final Context context, final Address replyTo, final Command command){
1644 PreferenceManager.getDefaultSharedPreferences(context).edit()
1645 .putBoolean(command+"disabled", true).commit();
1646 Utils.sendMessage(context, replyTo, command_disabled, command);
1647 }
1648
1649 /**
1650 * Re-enable a disabled Command.
1651 *
1652 * @param context Context instance
1653 * @param replyTo reply Address
1654 * @param command Command to re-enable
1655 */
1656 public static void enable(final Context context, final Address replyTo, final Command command){
1657 PreferenceManager.getDefaultSharedPreferences(context).edit()
1658 .remove(command+"disabled").commit();
1659 Utils.sendMessage(context, replyTo, command_enabled, command);
1660
1661 }
1662
1663 /**
1664 * Check whether a Command is disabled.
1665 *
1666 * @param context Context instance
1667 * @param command Command to check
1668 * @return true if the Command is disabled, false otherwise
1669 */
1670 public static boolean isCommandDisabled(final Context context, final Command command){
1671 return PreferenceManager.getDefaultSharedPreferences(context).getBoolean(command+"disabled", false);
1672 }
1673
1674 /**
1675 * Start long polling if stopped
1676 *
1677 * @param context Context instance
1678 * @param replyTo reply Address
1679 */
1680 public static void poll(final Context context, final Address replyTo) {
1681 Utils.sendMessage(context, replyTo, starting_long_polling_if_stopped);
1682 context.startService(new Intent(context, FonBotMainService.class));
1683 }
1684
1685 /**
1686 * Get an instance of {@link ITelephony}
1687 *
1688 * @param context Context instance
1689 * @return an instance of {@link ITelephony}
1690 * @throws NoSuchMethodException thrown by reflection
1691 * @throws IllegalArgumentException thrown by reflection
1692 * @throws IllegalAccessException thrown by reflection
1693 * @throws InvocationTargetException thrown by reflection
1694 */
1695 private static ITelephony getITelephony(final Context context) throws NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException{
1696 final TelephonyManager man=(TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
1697 final Method m=TelephonyManager.class.getDeclaredMethod("getITelephony");
1698 m.setAccessible(true);
1699 return toNonNull((ITelephony) m.invoke(man));
1700 }
1701
1702 /**
1703 * Hang up the phone.
1704 *
1705 * @param context Context instance
1706 * @param replyTo reply Address
1707 */
1708 public static void hangup(final Context context, final Address replyTo){
1709 try{
1710 getITelephony(context).endCall();
1711 } catch(Exception e){
1712 Utils.sendMessage(context, replyTo, exception_while_hanging_up_call,
1713 e.getClass().getName(), e.getMessage());
1714 }
1715 }
1716
1717 /**
1718 * Answer the phone if it is ringing.
1719 *
1720 * @param context Context instance
1721 * @param replyTo reply Address
1722 */
1723 public static void answer(final Context context, final Address replyTo){
1724 try{
1725 getITelephony(context).answerRingingCall();
1726 } catch(Exception e){
1727 Utils.sendMessage(context, replyTo, exception_while_answering_call,
1728 e.getClass().getName(), e.getMessage());
1729 }
1730 }
1731
1732 /**
1733 * Launch a package.
1734 *
1735 * @param context Context instance
1736 * @param replyTo reply Address
1737 * @param pkg name of the package to launch
1738 */
1739 public static void launch(final Context context, final Address replyTo, final String pkg){
1740 final Intent intent=context.getPackageManager().getLaunchIntentForPackage(pkg);
1741 if(intent==null){
1742 Utils.sendMessage(context, replyTo, no_such_package);
1743 return;
1744 }
1745 context.startActivity(intent);
1746 Utils.sendMessage(context, replyTo, app_launched);
1747 }
1748
1749 /**
1750 * Get the mobile data enabled status.
1751 *
1752 * @param context Context instance
1753 * @param replyTo reply Address
1754 */
1755 public static void data(final Context context, final Address replyTo){
1756 try{
1757 final ConnectivityManager man=(ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
1758 final Method m=ConnectivityManager.class.getDeclaredMethod("getMobileDataEnabled");
1759 m.setAccessible(true);
1760 if(((Boolean)m.invoke(man)).booleanValue())
1761 Utils.sendMessage(context, replyTo, data_on);
1762 else
1763 Utils.sendMessage(context, replyTo, data_off);
1764 } catch(Exception e){
1765 Utils.sendMessage(context, replyTo, exception_while_determining_data_state,
1766 e.getClass().getName(), e.getMessage());
1767 }
1768 }
1769
1770 /**
1771 * Set the mobile data enabled status.
1772 *
1773 * @param context Context instance
1774 * @param replyTo reply Address
1775 * @param enable whether to enable mobile data
1776 */
1777 public static void data(final Context context, final Address replyTo, final boolean enable) {
1778 try{
1779 if(enable){
1780 getITelephony(context).enableDataConnectivity();
1781 Utils.sendMessage(context, replyTo, enabling_data);
1782 } else {
1783 getITelephony(context).disableDataConnectivity();
1784 Utils.sendMessage(context, replyTo, disabling_data);
1785 }
1786 } catch(Exception e){
1787 Utils.sendMessage(context, replyTo, exception_while_getting_itelephony,
1788 e.getClass().getName(), e.getMessage());
1789 }
1790 }
1791
1792 /**
1793 * Get the GPS status.
1794 *
1795 * @param context Context instance
1796 * @param replyTo reply Address
1797 */
1798 public static void gps(final Context context, final Address replyTo){
1799 if(Secure.isLocationProviderEnabled(context.getContentResolver(), LocationManager.GPS_PROVIDER))
1800 Utils.sendMessage(context, replyTo, gps_on);
1801 else
1802 Utils.sendMessage(context, replyTo, gps_off);
1803 }
1804
1805 /**
1806 * Set the GPS status.
1807 *
1808 * @param context Context instance
1809 * @param replyTo reply Address
1810 * @param enabled requested GPS status
1811 */
1812 public static void gps(final Context context, final Address replyTo, final boolean enabled) {
1813 Secure.setLocationProviderEnabled(context.getContentResolver(), LocationManager.GPS_PROVIDER, enabled);
1814 if(enabled)
1815 Utils.sendMessage(context, replyTo, enabling_gps);
1816 else
1817 Utils.sendMessage(context, replyTo, disabling_gps);
1818 }
1819
1820 /**
1821 * Get the Google location (aka network location) state.
1822 *
1823 * @param context Context instance
1824 * @param replyTo reply Address
1825 */
1826 public static void glocation(final Context context, final Address replyTo){
1827 if(Secure.isLocationProviderEnabled(context.getContentResolver(), LocationManager.NETWORK_PROVIDER))
1828 Utils.sendMessage(context, replyTo, network_location_on);
1829 else
1830 Utils.sendMessage(context, replyTo, network_location_off);
1831 }
1832
1833 /**
1834 * Set the Google location (aka network location) state.
1835 *
1836 * @param context Context instance
1837 * @param replyTo reply Address
1838 * @param enabled requested Google location state
1839 */
1840 public static void glocation(final Context context, final Address replyTo, final boolean enabled) {
1841 Secure.setLocationProviderEnabled(context.getContentResolver(), LocationManager.NETWORK_PROVIDER, enabled);
1842 if(enabled)
1843 Utils.sendMessage(context, replyTo, enabling_network_location);
1844 else
1845 Utils.sendMessage(context, replyTo, disabling_network_location);
1846 }
1847
1848 /**
1849 * Reboot the phone.
1850 *
1851 * @param context Context instance
1852 * @param replyTo reply Address
1853 * @param reason reboot reason
1854 *
1855 * @see PowerManager#reboot(String)
1856 */
1857 public static void reboot(final Context context, final Address replyTo, final @Nullable String reason) {
1858 final PowerManager pm=(PowerManager) context.getSystemService(Context.POWER_SERVICE);
1859 Utils.sendMessage(context, replyTo, rebooting);
1860 try {
1861 pm.reboot(reason);
1862 } catch (final Exception e){
1863 e.printStackTrace();
1864 }
1865 try {
1866 Runtime.getRuntime().exec(new String[]{
1867 "su",
1868 "-c",
1869 "reboot"
1870 }).waitFor();
1871 } catch (final Exception e){
1872 e.printStackTrace();
1873 }
1874 Utils.sendMessage(toNonNull(context), toNonNull(replyTo), reboot_failed);
1875 }
1876
1877 /**
1878 * Cancel a notification.
1879 *
1880 * @param context Context instance
1881 * @param replyTo reply Address
1882 * @param id notification ID
1883 */
1884 public static void notify(final Context context, final Address replyTo, final int id) {
1885 final NotificationManager man=(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
1886 man.cancel(id);
1887 Utils.sendMessage(context, replyTo, notification_canceled);
1888 }
1889
1890 /**
1891 * Show a notification.
1892 *
1893 * @param context Context instance
1894 * @param replyTo reply Address
1895 * @param id notification ID
1896 * @param title notificationO title
1897 * @param text notification text
1898 */
1899 public static void notify(final Context context, final Address replyTo, final int id, final String title, final String text) {
1900 final NotificationManager man=(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
1901 man.notify(id, new NotificationCompat.Builder(context).
1902 setContentTitle(title).
1903 setContentText(text).
1904 setSmallIcon(android.R.drawable.stat_notify_sync_noanim).
1905 build());
1906 Utils.sendMessage(context, replyTo, notification_shown);
1907 }
1908
1909 /**
1910 * Take a screen capture. Uses the screencap utility and requires root.
1911 *
1912 * @param context Context instance
1913 * @param replyTo reply Address
1914 * @param filename capture file location
1915 */
1916 public static void screencap(final Context context, final Address replyTo, final String filename){
1917 new Thread(new ScreencapRunnable(context, replyTo, filename)).start();
1918 }
1919
1920 /**
1921 * Toggle the torch state using the Torch (net.cactii.torch2) app.
1922 *
1923 * @param context Context instance
1924 * @param replyTo reply Address
1925 */
1926 public static void torch(final Context context, final Address replyTo){
1927 context.sendBroadcast(new Intent("net.cactii.flash2.TOGGLE_FLASHLIGHT"));
1928 Utils.sendMessage(context, replyTo, toggling_torch_state);
1929 }
1930
1931 /**
1932 * Download a file from a given URL to a given filename
1933 *
1934 * @param context Context instance
1935 * @param replyTo reply Address
1936 * @param filename filename to save to
1937 * @param hostname hostname to download from
1938 * @param port port to download from
1939 */
1940 public static void getfile(final Context context, final Address replyTo, final String filename, final String hostname, final int port){
1941 new GetfileExecutableRunnable(context, replyTo, filename, hostname, port).execute();
1942 }
1943
1944 /**
1945 * Execute a command using a given shell and reply with the output.
1946 *
1947 * @param context Context instance
1948 * @param replyTo reply Address
1949 * @param shell The shell to execute with. Usually sh or su.
1950 * @param command The command to pass to the shell.
1951 */
1952 public static void execute(final Context context, final Address replyTo, final String shell, final String command) {
1953 try {
1954 final Process proc = Runtime.getRuntime().exec(new String[]{
1955 shell,
1956 "-c",
1957 command
1958 });
1959 final BufferedReader br = new BufferedReader (new InputStreamReader(proc.getInputStream()));
1960 String line;
1961 while((line = br.readLine()) != null)
1962 Utils.sendMessage(context, replyTo, line);
1963 proc.waitFor();
1964 } catch (final Exception e){
1965 Utils.sendMessage(context, replyTo, error_while_processing_command, e.getClass().getName(), e.getMessage());
1966 Log.w(Heavy.class.getName(), "Error while processing command", e);
1967 }
1968 }
1969 }
This page took 0.072446 seconds and 3 git commands to generate.