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