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