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