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