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