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