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