1 package ro
.ieval
.fonbot
;
3 import static ro
.ieval
.fonbot
.R
.string
.*;
4 import static ro
.ieval
.fonbot
.Utils
.toNonNull
;
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
;
20 import org
.eclipse
.jdt
.annotation
.Nullable
;
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
;
75 import com
.android
.internal
.telephony
.ITelephony
;
78 * Copyright © 2013 Marius Gavrilescu
80 * This file is part of FonBot.
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.
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.
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/>.
97 * Implementation of all FonBot commands. The methods of this class do not do argument checking.
99 * @author Marius Gavrilescu <marius@ieval.ro>
103 * LocationListener that sends notifications to the user.
105 * @author Marius Gavrilescu <marius@ieval.ro>
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
;
114 * Construct a FonBotLocationListener.
116 * @param context Context instance
117 * @param replyTo the reply address
119 FonBotLocationListener(final Context context
, final Address replyTo
) {
120 this.context
=context
;
121 this.replyTo
=replyTo
;
125 public void onLocationChanged(@Nullable final Location loc
) {
128 final StringBuilder sb
=new StringBuilder(toNonNull(context
.getString(location
)));
130 sb
.append(toNonNull(context
.getString(latitude
)));
132 sb
.append(loc
.getLatitude());
134 sb
.append(toNonNull(context
.getString(longitude
)));
136 sb
.append(loc
.getLongitude());
138 if(loc
.hasAccuracy()){
140 sb
.append(toNonNull(context
.getString(accuracy
)));
142 sb
.append(loc
.getAccuracy());
145 if(loc
.hasAltitude()){
147 sb
.append(toNonNull(context
.getString(altitude
)));
149 sb
.append(loc
.getAltitude());
152 if(loc
.hasBearing()){
154 sb
.append(toNonNull(context
.getString(bearing
)));
156 sb
.append(loc
.getBearing());
161 sb
.append(toNonNull(context
.getString(speed
)));
163 sb
.append(loc
.getSpeed());
166 final Date locationDate
=new Date(loc
.getTime());
168 sb
.append(toNonNull(context
.getString(at
)));
170 sb
.append(locationDate
.toString());
171 Utils
.sendMessage(toNonNull(context
), toNonNull(replyTo
), toNonNull(sb
.toString()));
175 public void onProviderDisabled(@Nullable final String provider
) {
176 Utils
.sendMessage(toNonNull(context
), toNonNull(replyTo
), location_provider_disabled
, provider
);
180 public void onProviderEnabled(@Nullable final String provider
) {
181 Utils
.sendMessage(toNonNull(context
), toNonNull(replyTo
), location_provider_enabled
, provider
);
185 public void onStatusChanged(@Nullable final String provider
, final int status
, @Nullable final Bundle extras
) {
188 case LocationProvider
.AVAILABLE
:
189 state
=location_provider_available
;
191 case LocationProvider
.TEMPORARILY_UNAVAILABLE
:
192 state
=location_provider_temporary_unavailable
;
194 case LocationProvider
.OUT_OF_SERVICE
:
195 state
=location_provider_out_of_service
;
198 state
=location_provider_unknown_state
;
200 Utils
.sendMessage(toNonNull(context
), toNonNull(replyTo
), state
, provider
);
204 * Currently active FonBotLocationListener
206 private static FonBotLocationListener locationListener
= null;
209 * AsyncTask that sends a byte[] to a server
211 * @author Marius Gavrilescu <marius@ieval.ro>
214 private static class SendDataAsyncTask
extends AsyncTask
<Void
, Void
, Void
>{
216 * Context instance used by this class
218 private final Context context
;
222 private final String hostname
;
226 private final int port
;
230 private final byte[] data
;
232 * Address for sending back errors and success messages
234 private final Address replyTo
;
237 * Constructs a SendDataAsyncTasks from its parameters
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
245 public SendDataAsyncTask(final Context context
,final Address replyTo
, final String hostname
, final int port
, final byte[] data
) {//NOPMD array is immutable
247 this.context
=context
;
248 this.hostname
=hostname
;
251 this.replyTo
=replyTo
;
255 protected @Nullable Void
doInBackground(@Nullable final Void
... params
) {
258 sock
= new Socket(hostname
, port
);
260 sock
.getOutputStream().write(data
);
261 } catch (IOException e
) {
262 Utils
.sendMessage(toNonNull(context
), toNonNull(replyTo
), error_writing_to_socket
, e
.getMessage());
268 } catch (IOException e
) {
269 Utils
.sendMessage(toNonNull(context
), toNonNull(replyTo
), cannot_close_socket
, e
.getMessage());
272 } catch (UnknownHostException e
) {
273 Utils
.sendMessage(toNonNull(context
), toNonNull(replyTo
), unknown_host
, hostname
);
275 } catch (IOException e
) {
276 Utils
.sendMessage(toNonNull(context
), toNonNull(replyTo
), cannot_connect_to_host_on_port
, hostname
, Integer
.valueOf(port
));
279 Utils
.sendMessage(toNonNull(context
), toNonNull(replyTo
), photo_sent
);
285 * PictureCallback that sends the picture to a server.
287 * @author Marius Gavrilescu <marius@ieval.ro>
289 private static final class FonBotPictureCallback
implements PictureCallback
{
290 /** Server hostname */
291 private final String hostname
;
293 private final int port
;
294 /** Context instance */
295 private final Context context
;
297 private final Address replyTo
;
300 * Construct a FonBotPictureCallback.
302 * @param context Context instance
303 * @param replyTo reply Address
304 * @param hostname server hostname
305 * @param port server port
307 FonBotPictureCallback(final Context context
, final Address replyTo
, final String hostname
, final int port
) {
308 this.hostname
=hostname
;
310 this.context
=context
;
311 this.replyTo
=replyTo
;
315 @SuppressWarnings("hiding")
316 public void onPictureTaken(final @Nullable byte[] data
, final @Nullable Camera camera
) {
317 if(camera
==null || data
==null)
319 camera
.stopPreview();
321 Utils
.sendMessage(toNonNull(context
), toNonNull(replyTo
), sending_photo
);
322 new SendDataAsyncTask(toNonNull(context
), toNonNull(replyTo
), toNonNull(hostname
), port
, data
).execute();
327 * Runnable that takes a screen capture and stores it in a file.
329 private static final class ScreencapRunnable
implements Runnable
{
330 private final Context context
;
331 private final Address replyTo
;
332 private final String filename
;
334 ScreencapRunnable(final Context context
, final Address replyTo
, final String filename
){
335 this.context
=context
;
336 this.replyTo
=replyTo
;
337 this.filename
=filename
;
344 exitCode
=Runtime
.getRuntime().exec(new String
[]{
347 "screencap -p \"" + filename
+ "\""
349 } catch (final Exception e
){
351 Utils
.sendMessage(toNonNull(context
), toNonNull(replyTo
), screencap_failed
);
355 if(exitCode
== 0 && new File(filename
).exists())
356 Utils
.sendMessage(toNonNull(context
), toNonNull(replyTo
), screencap_successful
);
358 Utils
.sendMessage(toNonNull(context
), toNonNull(replyTo
), screencap_failed
);
363 * Get help for a particular command
365 * @param context Context instance
366 * @param replyTo reply Address
367 * @param command command to get help for
369 public static void help(final Context context
, final Address replyTo
, final Command command
){//NOPMD method is a big switch statement. Nothing confusing.
372 Utils
.sendMessage(context
, replyTo
, answer_help
);
375 Utils
.sendMessage(context
, replyTo
, batt_help
);
378 Utils
.sendMessage(context
, replyTo
, bluetooth_help
);
381 Utils
.sendMessage(context
, replyTo
, calllog_help
);
384 Utils
.sendMessage(context
, replyTo
, contacts_help
);
387 Utils
.sendMessage(context
, replyTo
, data_help
);
389 case DELNOTIFICATION
:
390 Utils
.sendMessage(context
, replyTo
, delnotification_help
, Utils
.join(", ", toNonNull(MessageType
.values())));
393 Utils
.sendMessage(context
, replyTo
, dial_help
);
396 Utils
.sendMessage(context
, replyTo
, dialog_help
);
399 Utils
.sendMessage(context
, replyTo
, disable_help
, Utils
.join(", ", toNonNull(Command
.values())));
402 Utils
.sendMessage(context
, replyTo
, echo_help
);
405 Utils
.sendMessage(context
, replyTo
, enable_help
, Utils
.join(", ", toNonNull(Command
.values())));
408 Utils
.sendMessage(context
, replyTo
, flash_help
);
411 Utils
.sendMessage(context
, replyTo
, glocation_help
);
414 Utils
.sendMessage(context
, replyTo
, gps_help
);
417 Utils
.sendMessage(context
, replyTo
, hangup_help
);
420 Utils
.sendMessage(context
, replyTo
, help_help
, Utils
.join(", ",toNonNull(Command
.values())));
423 Utils
.sendMessage(context
, replyTo
, launch_help
);
426 Utils
.sendMessage(context
, replyTo
, location_help
, Utils
.join(", ",toNonNull(Utils
.LocationProvider
.values())));
429 Utils
.sendMessage(context
, replyTo
, lock_help
);
432 Utils
.sendMessage(context
, replyTo
, ls_help
);
435 Utils
.sendMessage(context
, replyTo
, ncfile_help
);
438 Utils
.sendMessage(context
, replyTo
, next_help
);
441 Utils
.sendMessage(context
, replyTo
, nolocation_help
);
444 Utils
.sendMessage(context
, replyTo
, pause_help
);
447 Utils
.sendMessage(context
, replyTo
, photo_help
);
450 Utils
.sendMessage(context
, replyTo
, play_help
);
453 Utils
.sendMessage(context
, replyTo
, poll_help
);
456 Utils
.sendMessage(context
, replyTo
, prev_help
);
459 Utils
.sendMessage(context
, replyTo
, ring_help
);
462 Utils
.sendMessage(context
, replyTo
, ringer_help
, Utils
.join(", ", toNonNull(RingerMode
.values())));
465 Utils
.sendMessage(context
, replyTo
, rm_help
);
467 case SETNOTIFICATION
:
468 Utils
.sendMessage(context
, replyTo
, setnotification_help
, Utils
.join(", ", toNonNull(MessageType
.values())));
471 Utils
.sendMessage(context
, replyTo
, setpassword_help
);
474 Utils
.sendMessage(context
, replyTo
, sms_help
);
477 Utils
.sendMessage(context
, replyTo
, smslog_help
);
480 Utils
.sendMessage(context
, replyTo
, speak_help
);
483 Utils
.sendMessage(context
, replyTo
, toast_help
);
486 Utils
.sendMessage(context
, replyTo
, vibrate_help
);
489 Utils
.sendMessage(context
, replyTo
, view_help
);
492 Utils
.sendMessage(context
, replyTo
, wifi_help
);
495 Utils
.sendMessage(context
, replyTo
, wipe_help
, Utils
.WIPE_CONFIRM_STRING
);
498 Utils
.sendMessage(context
, replyTo
, reboot_help
);
501 Utils
.sendMessage(context
, replyTo
, notify_help
);
504 Utils
.sendMessage(context
, replyTo
, screencap_help
);
507 Utils
.sendMessage(context
, replyTo
, torch_help
);
515 * @see #startCamera(Context, Address)
518 private static Camera camera
;
520 * Ringtone used by the {@link Utils.Command#RING RING} command.
522 * @see #setupRingtone(Context)
524 private static Ringtone ringtone
;
526 * Saved ringer volume.
528 * @see #startAlarm(Context, Address)
529 * @see #stopAlarm(Context, Address)
531 private static int savedRingVolume
;
535 * @see #startAlarm(Context, Address)
536 * @see #stopAlarm(Context, Address)
538 private static int savedRingerMode
;
540 /** Private constructor */
546 * Convert a phone number type to a string
548 * @param context Context instance
549 * @param type phone number type
550 * @param label name of a custom phone type
551 * @return the phone number type
553 private static @Nullable String
phoneNumberType(final Context context
, final int type
, final @Nullable String label
) {
555 case BaseTypes
.TYPE_CUSTOM
:
557 case Phone
.TYPE_ASSISTANT
:
558 return context
.getString(phone_numer_type_assistant
);
559 case Phone
.TYPE_CALLBACK
:
560 return context
.getString(phone_number_type_callback
);
562 return context
.getString(phone_number_type_car
);
563 case Phone
.TYPE_COMPANY_MAIN
:
564 return context
.getString(phone_number_type_company_main
);
565 case Phone
.TYPE_FAX_HOME
:
566 return context
.getString(phone_number_type_home_fax
);
567 case Phone
.TYPE_FAX_WORK
:
568 return context
.getString(phone_number_type_work_fax
);
569 case Phone
.TYPE_HOME
:
570 return context
.getString(phone_number_type_home
);
571 case Phone
.TYPE_ISDN
:
572 return context
.getString(phone_number_type_isdn
);
573 case Phone
.TYPE_MAIN
:
574 return context
.getString(phone_number_type_main
);
576 return context
.getString(phone_number_type_mms
);
577 case Phone
.TYPE_MOBILE
:
578 return context
.getString(phone_number_type_mobile
);
579 case Phone
.TYPE_OTHER
:
580 return context
.getString(phone_number_type_other
);
581 case Phone
.TYPE_OTHER_FAX
:
582 return context
.getString(phone_number_type_other_fax
);
583 case Phone
.TYPE_PAGER
:
584 return context
.getString(phone_number_type_pager
);
585 case Phone
.TYPE_RADIO
:
586 return context
.getString(phone_number_type_radio
);
587 case Phone
.TYPE_TELEX
:
588 return context
.getString(phone_number_type_telex
);
589 case Phone
.TYPE_TTY_TDD
:
590 return context
.getString(phone_number_type_textphone
);
591 case Phone
.TYPE_WORK
:
592 return context
.getString(phone_number_type_work
);
593 case Phone
.TYPE_WORK_MOBILE
:
594 return context
.getString(phone_number_type_work_mobile
);
595 case Phone
.TYPE_WORK_PAGER
:
596 return context
.getString(phone_number_type_work_pager
);
599 return context
.getString(phone_number_type_unknown
, Integer
.valueOf(type
));
603 * Setup the ringtone used by the {@link Utils.Command#RING RING} command
605 * @param context Context
607 private static void setupRingtone(final Context context
){
608 if(ringtone
==null){//NOPMD not supposed to be thread-safe
609 final Uri alert
=RingtoneManager
.getDefaultUri(RingtoneManager
.TYPE_RINGTONE
);
610 ringtone
=RingtoneManager
.getRingtone(context
, alert
);
615 * Make the phone start ringing. Turns up the volume and sets the ringer mode to NORMAL
617 * @param context Context instance
618 * @param replyTo reply Address
620 private static void startAlarm(final Context context
, final Address replyTo
){
621 Utils
.registerOngoing(context
, toNonNull(OngoingEvent
.RING
));
622 final AudioManager man
=(AudioManager
) context
.getSystemService(Context
.AUDIO_SERVICE
);
623 savedRingerMode
=man
.getRingerMode();
624 man
.setRingerMode(AudioManager
.RINGER_MODE_NORMAL
);
625 savedRingVolume
=man
.getStreamVolume(AudioManager
.STREAM_RING
);
626 man
.setStreamVolume(AudioManager
.STREAM_RING
, man
.getStreamMaxVolume(AudioManager
.STREAM_RING
), 0);
627 Utils
.sendMessage(context
, replyTo
, ringing
);
632 * Get a camera instance.
634 * @param context Context instance
635 * @param replyTo reply Address
637 private static void startCamera(final Context context
, final Address replyTo
){
641 camera
=Camera
.open();
642 } catch (Exception e
){
643 Utils
.sendMessage(context
, replyTo
, cannot_grab_camera
);
648 * Make the phone stop ringing. Restores the volume and ringer mode.
650 * @param context Context instance
651 * @param replyTo reply Address
653 private static void stopAlarm(final Context context
, final Address replyTo
){
654 Utils
.unregisterOngoing(context
, toNonNull(OngoingEvent
.RING
));
655 final AudioManager man
=(AudioManager
) context
.getSystemService(Context
.AUDIO_SERVICE
);
656 Utils
.sendMessage(context
, replyTo
, no_longer_ringing
);
658 man
.setStreamVolume(AudioManager
.STREAM_RING
, savedRingVolume
, 0);
659 man
.setRingerMode(savedRingerMode
);
663 * Release the previously grabbed camera instance
665 * @see #startCamera(Context, Address)
667 private static void stopCamera(){
675 * Send battery status information to an Address
677 * @param context Context instance
678 * @param replyTo destination Address
680 * @see #describeBatteryLevel(Context, Address, MessageType)
682 public static void batt(final Context context
, final Address replyTo
){
683 describeBatteryLevel(context
, replyTo
, null);
687 * Show the bluetooth radio status.
689 * @param context Context instance
690 * @param replyTo destination Address
692 public static void bluetooth(final Context context
, final Address replyTo
) {
693 final BluetoothAdapter adapter
=BluetoothAdapter
.getDefaultAdapter();
695 Utils
.sendMessage(context
, replyTo
, no_bluetooth_adapter
);
699 if(adapter
.isEnabled())
700 Utils
.sendMessage(context
, replyTo
, bluetooth_on
);
702 Utils
.sendMessage(context
, replyTo
, bluetooth_off
);
706 * Set the bluetooth radio status.
708 * @param context Context instance
709 * @param replyTo destination Address
710 * @param on the requested radio status
712 public static void bluetooth(final Context context
, final Address replyTo
, final boolean on
){
713 final BluetoothAdapter adapter
=BluetoothAdapter
.getDefaultAdapter();
715 Utils
.sendMessage(context
, replyTo
, no_bluetooth_adapter
);
721 Utils
.sendMessage(context
, replyTo
, enabling_bluetooth
);
725 Utils
.sendMessage(context
, replyTo
, disabling_bluetooth
);
730 * Cancel an ongoing event.
732 * @param context Context instance
733 * @param event the event to cancel
735 public static void cancelOngoing(final Context context
, final OngoingEvent event
){
738 nolocation(context
, toNonNull(Address
.BLACKHOLE
));
741 poll(context
, toNonNull(Address
.BLACKHOLE
), 0);
744 ring(context
, toNonNull(Address
.BLACKHOLE
), false);
750 * Send the last calls to an Address.
752 * @param context Context instance
753 * @param replyTo destination Address
754 * @param numCalls how many calls to send
756 public static void calllog(final Context context
, final Address replyTo
, final int numCalls
) {
757 final String
[] fields
= {
758 Calls
.TYPE
, Calls
.NUMBER
, Calls
.CACHED_NAME
, Calls
.DURATION
, Calls
.DATE
761 final Cursor cursor
= context
.getContentResolver().query(
769 if (cursor
.moveToFirst()) {
771 final StringBuilder sb
=new StringBuilder(50);//NOPMD different strings
772 final int type
=cursor
.getInt(0);
773 final String from
=cursor
.getString(1);
776 case Calls
.INCOMING_TYPE
:
777 sb
.append(context
.getString(incoming_call_from
, from
));
779 case Calls
.MISSED_TYPE
:
780 sb
.append(context
.getString(missed_call_from
, from
));
782 case Calls
.OUTGOING_TYPE
:
783 sb
.append(context
.getString(outgoing_call_to
, from
));
787 if (cursor
.getString(2) != null)
788 sb
.append('(').append(cursor
.getString(2)).append(") ");
790 sb
.append(context
.getString(duration_seconds_starting_at
,
791 Long
.valueOf(cursor
.getLong(3)),
792 new Date(cursor
.getLong(4))));
794 Utils
.sendMessage(context
, replyTo
, toNonNull(sb
.toString()));
795 } while (cursor
.moveToNext() && cursor
.getPosition() < numCalls
);
802 * Search for contacts by name/nickname and send matching entries to an Address.
804 * @param context Context instance
805 * @param replyTo destination Address
806 * @param name name/nickname part to search for
808 @SuppressLint("StringFormatMatches")
809 public static void contacts(final Context context
, final Address replyTo
, final String name
){
810 final Cursor cursor
=context
.getContentResolver().query(Uri
.withAppendedPath(
811 Contacts
.CONTENT_FILTER_URI
, name
),
812 new String
[]{Contacts
.DISPLAY_NAME
, BaseColumns
._ID
, Contacts
.LOOKUP_KEY
},
813 null, null, Contacts
.DISPLAY_NAME
);
815 if(cursor
.getCount()==0)
816 Utils
.sendMessage(context
, replyTo
, no_matching_contacts_found
);
818 while(cursor
.moveToNext()){
819 final String
[] fields
= {
820 CommonDataKinds
.Phone
.NUMBER
,
821 CommonDataKinds
.Phone
.TYPE
,
822 CommonDataKinds
.Phone
.LABEL
,
825 final Cursor inCursor
=context
.getContentResolver().query(Data
.CONTENT_URI
,
827 Data
.CONTACT_ID
+" = ? AND "+Data
.MIMETYPE
+ " = ?",
828 new String
[]{Long
.toString(cursor
.getLong(1)), CommonDataKinds
.Phone
.CONTENT_ITEM_TYPE
},
829 CommonDataKinds
.Phone
.LABEL
);
831 while(inCursor
.moveToNext())
832 Utils
.sendMessage(context
, replyTo
, toNonNull(context
.getString(contact_info
,
834 inCursor
.getString(0),
835 phoneNumberType(context
, inCursor
.getInt(1), inCursor
.getString(2)))));
844 * Send battery status information to an Address or as a notification
846 * @param context Context instance
847 * @param replyTo Address to send the information to, if sending to a direct address. Null otherwise.
848 * @param type Notification type, if sending as a notification. Null otherwise.
850 public static void describeBatteryLevel(final Context context
, final @Nullable Address replyTo
, final @Nullable MessageType type
) {
851 if(replyTo
==null&&type
==null)
853 final Intent intent
=context
.registerReceiver(null, new IntentFilter(Intent
.ACTION_BATTERY_CHANGED
));
856 final double level
=intent
.getIntExtra(BatteryManager
.EXTRA_LEVEL
, 0);
857 final int scale
=intent
.getIntExtra(BatteryManager
.EXTRA_SCALE
, 100);
858 final int plugged
=intent
.getIntExtra(BatteryManager
.EXTRA_PLUGGED
, 0);
859 final int status
=intent
.getIntExtra(BatteryManager
.EXTRA_STATUS
, BatteryManager
.BATTERY_STATUS_UNKNOWN
);
860 final int temp
=intent
.getIntExtra(BatteryManager
.EXTRA_TEMPERATURE
, 0);
861 final int volt
=intent
.getIntExtra(BatteryManager
.EXTRA_VOLTAGE
, 0);
863 final StringBuilder sb
=new StringBuilder(100);
864 sb
.append(context
.getString(battery_level
, Double
.valueOf(level
*100/scale
)));
868 sb
.append(context
.getString(not_plugged_in
));
870 case BatteryManager
.BATTERY_PLUGGED_AC
:
871 sb
.append(context
.getString(plugged_in_ac
));
873 case BatteryManager
.BATTERY_PLUGGED_USB
:
874 sb
.append(context
.getString(plugged_in_usb
));
876 case BatteryManager
.BATTERY_PLUGGED_WIRELESS
:
877 sb
.append(context
.getString(plugged_in_wireless
));
882 case BatteryManager
.BATTERY_STATUS_CHARGING
:
883 sb
.append(context
.getString(status_charging
));
885 case BatteryManager
.BATTERY_STATUS_DISCHARGING
:
886 sb
.append(context
.getString(status_discharging
));
888 case BatteryManager
.BATTERY_STATUS_FULL
:
889 sb
.append(context
.getString(status_full
));
891 case BatteryManager
.BATTERY_STATUS_NOT_CHARGING
:
892 sb
.append(context
.getString(status_not_charging
));
894 case BatteryManager
.BATTERY_STATUS_UNKNOWN
:
895 sb
.append(context
.getString(status_unknown
));
899 sb
.append(context
.getString(temperature
, Integer
.valueOf(temp
)));
901 sb
.append(context
.getString(voltage
, Integer
.valueOf(volt
)));
903 Utils
.sendMessage(context
, toNonNull(replyTo
), toNonNull(sb
.toString()));
905 Utils
.sendMessage(context
, type
, toNonNull(sb
.toString()));
909 * Dial a phone number.
911 * @param context Context instance
912 * @param replyTo reply Address
913 * @param nr phone number to dial
915 public static void dial(final Context context
, final Address replyTo
, final String nr
){
916 final Intent intent
=new Intent(Intent
.ACTION_CALL
,Uri
.parse("tel:"+nr
));
917 intent
.addFlags(Intent
.FLAG_ACTIVITY_NEW_TASK
);
918 final String name
=Utils
.callerId(context
, nr
);
920 Utils
.sendMessage(context
, replyTo
, dialing
, nr
);
922 Utils
.sendMessage(context
, replyTo
, dialing
, nr
+" ("+name
+")");
923 context
.startActivity(intent
);
927 * Show a dialog with a message and a list of buttons.
929 * @param context Context instance
930 * @param replyTo reply Address
931 * @param message dialog message
932 * @param buttons dialog buttons
934 public static void dialog(final Context context
, final Address replyTo
, final String message
, final String
[] buttons
){
935 final Intent intent
=new Intent(context
, DialogActivity
.class);
936 intent
.putExtra(DialogActivity
.EXTRA_MESSAGE
, message
);
937 intent
.putExtra(DialogActivity
.EXTRA_BUTTONS
, buttons
);
938 intent
.putExtra(DialogActivity
.EXTRA_REPLYTO
, replyTo
.toString());
940 Intent
.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
|
941 Intent
.FLAG_ACTIVITY_NEW_TASK
|
942 Intent
.FLAG_ACTIVITY_NO_USER_ACTION
|
943 Intent
.FLAG_FROM_BACKGROUND
);
944 Utils
.sendMessage(context
, toNonNull(replyTo
), showing_dialog
);
945 context
.startActivity(intent
);
949 * Turns the flashlight on or off.
951 * @param context Context instance
952 * @param replyTo reply Address
953 * @param on requested flashlight state
955 public static void flash(final Context context
, final Address replyTo
, final boolean on
){
956 startCamera(context
, replyTo
);
959 final Camera
.Parameters parms
=camera
.getParameters();
961 parms
.setFlashMode(Camera
.Parameters
.FLASH_MODE_TORCH
);
962 camera
.setParameters(parms
);
964 parms
.setFlashMode(Camera
.Parameters
.FLASH_MODE_AUTO
);
965 camera
.setParameters(parms
);
971 * Start sending location updates to an Address.
973 * @param context Context instance
974 * @param replyTo destination Address
975 * @param provider LocationProvider
976 * @param minTime minimum time between two consecutive updates (in ms)
977 * @param minDistance minimum distance between two consecutive updates (in meters)
979 * @see LocationManager#requestLocationUpdates(String, long, float, LocationListener)
981 public static void location(final Context context
, final Address replyTo
, final String provider
,final long minTime
,final float minDistance
){
982 final LocationManager man
=(LocationManager
) context
.getApplicationContext().getSystemService(Context
.LOCATION_SERVICE
);
983 if(locationListener
!=null)
984 nolocation(context
, toNonNull(Address
.BLACKHOLE
));
985 Utils
.registerOngoing(context
, toNonNull(OngoingEvent
.LOCATION
));
986 locationListener
=new FonBotLocationListener(context
, replyTo
);
987 man
.removeUpdates(locationListener
);
988 final Location lastKnownLocation
=man
.getLastKnownLocation(provider
);
989 if(lastKnownLocation
!=null){
990 Utils
.sendMessage(context
, replyTo
, last_known_location
);
991 locationListener
.onLocationChanged(lastKnownLocation
);
993 Utils
.sendMessage(context
, replyTo
, listening_for_location_updates
);
994 man
.requestLocationUpdates(provider
, minTime
, minDistance
, locationListener
);
1000 * @param context Context instance
1001 * @param replyTo reply Address
1003 public static void lock(final Context context
, final Address replyTo
) {
1004 final DevicePolicyManager dpm
=(DevicePolicyManager
) context
.getSystemService(Context
.DEVICE_POLICY_SERVICE
);
1006 Utils
.sendMessage(context
, replyTo
, device_locked
);
1010 * Send a command to a running instance of the music player
1012 * @param context Context instance
1013 * @param replyTo reply Address
1014 * @param command command to send
1016 public static void musicPlayerCommand(final Context context
, final Address replyTo
, final String command
) {
1017 final Intent intent
=new Intent("com.android.music.musicservicecommand");
1018 intent
.putExtra("command", command
);
1019 context
.sendBroadcast(intent
);
1020 Utils
.sendMessage(context
, replyTo
, command_sent
);
1024 * Send a file to a server.
1026 * @param context Context instance
1027 * @param replyTo reply Address
1028 * @param filename file to send
1029 * @param hostname server hostname
1030 * @param port server port
1032 public static void ncfile(final Context context
, final Address replyTo
, final String filename
,final String hostname
,final int port
){
1033 new AsyncTask
<Void
, Void
, Void
>() {
1035 protected @Nullable Void
doInBackground(@Nullable final Void
... params
) {
1036 final FileChannel in
;
1038 in
=new FileInputStream(filename
).getChannel();
1039 } catch (final FileNotFoundException e
){
1040 Utils
.sendMessage(context
, replyTo
, file_not_found
, filename
);
1043 final SocketChannel sock
;
1045 sock
= SocketChannel
.open(new InetSocketAddress(hostname
, port
));
1046 } catch (final IOException e
){
1047 Utils
.sendMessage(context
, replyTo
, toNonNull(context
.getString(
1048 cannot_connect_to_host_on_port
, hostname
, Integer
.valueOf(port
))));
1051 } catch (IOException ex
) {
1058 in
.transferTo(0, in
.size(), sock
);
1059 } catch (final IOException e
){
1060 Utils
.sendMessage(context
, replyTo
, toNonNull(context
.getString(
1061 io_error
, e
.getMessage())));
1065 } catch (IOException e
){
1070 } catch(IOException e
){
1078 protected void onPostExecute(@Nullable final Void result
) {
1079 Utils
.sendMessage(context
, replyTo
, file_sent
);
1085 * Stop sending location updates.
1087 * @param context Context instance
1088 * @param replyTo reply Address
1090 public static void nolocation(final Context context
, final Address replyTo
){
1091 Utils
.unregisterOngoing(context
, toNonNull(OngoingEvent
.LOCATION
));
1092 final LocationManager man
=(LocationManager
) context
.getApplicationContext().getSystemService(Context
.LOCATION_SERVICE
);
1093 man
.removeUpdates(locationListener
);
1094 locationListener
=null;
1095 Utils
.sendMessage(context
, replyTo
, no_longer_listening_for_location_updates
);
1099 * Take a photo and send it to a server.
1101 * @param context Context instance
1102 * @param replyTo reply Address
1103 * @param hostname server hostname
1104 * @param port server port
1106 public static void photo(final Context context
, final Address replyTo
, final String hostname
, final int port
){
1107 startCamera(context
, replyTo
);
1110 final Camera
.Parameters parms
=camera
.getParameters();
1111 parms
.setJpegQuality(70);
1112 parms
.setPictureFormat(ImageFormat
.JPEG
);
1113 camera
.setParameters(parms
);
1115 final SurfaceView fakeView
=new SurfaceView(context
);
1117 camera
.setPreviewDisplay(fakeView
.getHolder());
1118 } catch (IOException e
) {
1119 Utils
.sendMessage(context
, replyTo
, error_setting_preview_display
);
1122 camera
.startPreview();
1123 final Handler handler
=new Handler();
1125 new Thread(new Runnable() {
1130 } catch (InterruptedException e
) {
1134 handler
.post(new Runnable() {
1137 camera
.takePicture(null, null, new FonBotPictureCallback(context
, replyTo
, hostname
, port
));
1145 * Send a directory listing to an Address
1147 * @param context Context instance
1148 * @param replyTo destination Address
1149 * @param directory directory to list
1151 public static void ls(final Context context
, final Address replyTo
, final String directory
) {
1152 final File
[] files
=new File(directory
).listFiles();
1154 Utils
.sendMessage(context
, replyTo
, string_is_not_a_directory
, directory
);
1158 final StringBuilder sb
=new StringBuilder(context
.getString(files_in_directory
,directory
));
1159 for(final File file
: files
){
1160 sb
.append(file
.getName());
1161 if(file
.isDirectory())
1166 Utils
.sendMessage(context
, replyTo
, toNonNull(sb
.toString()));
1170 * Make the phone start ringing if it is not ringing or stop ringing if it is.
1172 * @param context Context instance
1173 * @param replyTo reply Address
1175 public static void ring(final Context context
, final Address replyTo
){
1176 setupRingtone(context
);
1178 Utils
.sendMessage(context
, replyTo
, no_ringtone_found
);
1181 if(ringtone
.isPlaying())
1182 stopAlarm(context
, replyTo
);
1184 startAlarm(context
, replyTo
);
1188 * Make the phone start/stop ringing.
1190 * @param context Context instance
1191 * @param replyTo reply Address
1192 * @param on true if the phone should start ringing, false otherwise
1194 public static void ring(final Context context
, final Address replyTo
, final boolean on
){
1195 setupRingtone(context
);
1197 Utils
.sendMessage(context
, replyTo
, no_ringtone_found
);
1200 if(on
&&!ringtone
.isPlaying())
1201 startAlarm(context
, replyTo
);
1202 else if(ringtone
.isPlaying()&&!on
)
1203 stopAlarm(context
, replyTo
);
1207 * Send the current ringer mode to an Address
1209 * @param context Context instance
1210 * @param replyTo destination Address
1212 public static void ringer(final Context context
, final Address replyTo
){
1213 final AudioManager man
=(AudioManager
) context
.getSystemService(Context
.AUDIO_SERVICE
);
1214 switch(man
.getRingerMode()){
1215 case AudioManager
.RINGER_MODE_NORMAL
:
1216 Utils
.sendMessage(context
, replyTo
, ringer_mode_normal
);
1218 case AudioManager
.RINGER_MODE_VIBRATE
:
1219 Utils
.sendMessage(context
, replyTo
, ringer_mode_vibrate
);
1221 case AudioManager
.RINGER_MODE_SILENT
:
1222 Utils
.sendMessage(context
, replyTo
, ringer_mode_silent
);
1225 Utils
.sendMessage(context
, replyTo
, unknown_ringer_mode
);
1230 * Set the ringer mode.
1232 * @param context Context instance
1233 * @param replyTo reply Address
1234 * @param ringerMode requested ringer mode
1236 * @see Utils.RingerMode
1238 public static void ringer(final Context context
, final Address replyTo
, final int ringerMode
){
1239 final AudioManager man
=(AudioManager
) context
.getSystemService(Context
.AUDIO_SERVICE
);
1240 man
.setRingerMode(ringerMode
);
1241 ringer(context
, replyTo
);
1245 * Remove a file or empty directory.
1247 * @param context Context instance
1248 * @param replyTo reply Address
1249 * @param filename file/empty directory to delete
1251 public static void rm(final Context context
, final Address replyTo
, final String filename
){
1252 if(new File(filename
).delete())
1253 Utils
.sendMessage(context
, replyTo
, file_deleted
);
1255 Utils
.sendMessage(context
, replyTo
, error_while_deleting_file
);
1259 * Clear the keyguard password.
1261 * @param context Context instance
1262 * @param replyTo reply Address
1263 * @throws SecurityException if FonBot does not have device administration permissions
1265 public static void setPassword(final Context context
, final Address replyTo
) throws SecurityException
{
1266 final DevicePolicyManager dpm
=(DevicePolicyManager
) context
.getSystemService(Context
.DEVICE_POLICY_SERVICE
);
1268 dpm
.resetPassword("", 0);
1269 Utils
.sendMessage(context
, replyTo
, password_cleared
);
1273 * Change the keyguard password.
1275 * @param context Context instance
1276 * @param replyTo reply Address
1277 * @param password new password
1278 * @throws SecurityException if FonBot does not have device administration permissions
1280 public static void setPassword(final Context context
, final Address replyTo
, final String password
) throws SecurityException
{
1281 final DevicePolicyManager dpm
=(DevicePolicyManager
) context
.getSystemService(Context
.DEVICE_POLICY_SERVICE
);
1283 dpm
.resetPassword(password
, 0);
1284 Utils
.sendMessage(context
, replyTo
, password_set
);
1288 * Send a text message.
1290 * @param context Context instance
1291 * @param replyTo reply Address
1292 * @param destination destination phone number
1293 * @param text text message contents
1295 public static void sms(final Context context
, final Address replyTo
, final String destination
, final String text
){
1296 final SmsManager manager
=SmsManager
.getDefault();
1297 final ArrayList
<String
> messages
=manager
.divideMessage(text
);
1298 if(messages
.size() > 1)
1299 Utils
.sendMessage(context
, replyTo
, message_was_split_into_parts
, Integer
.valueOf(messages
.size()));
1301 final ArrayList
<PendingIntent
> sents
=new ArrayList
<PendingIntent
>(messages
.size());
1302 final ArrayList
<PendingIntent
> delivereds
=new ArrayList
<PendingIntent
>(messages
.size());
1304 final String name
=Utils
.callerId(context
, destination
);
1305 final String fullDestination
;
1307 fullDestination
=destination
;
1309 fullDestination
=destination
+" ("+name
+")";
1311 for(int i
=0;i
<messages
.size();i
++){
1312 final Intent sent
=new Intent(context
,SmsStatusReceiver
.class);
1313 sent
.putExtra(SmsStatusReceiver
.EXTRA_DESTINATION
, fullDestination
);
1314 sent
.putExtra(SmsStatusReceiver
.EXTRA_PART
, i
+1);
1315 sent
.putExtra(SmsStatusReceiver
.EXTRA_TOTAL
, messages
.size());
1316 sent
.putExtra(SmsStatusReceiver
.EXTRA_REPLY_TO
, replyTo
.toString());
1317 sent
.setAction(SmsStatusReceiver
.SENT_ACTION
+i
);//actions must be unique
1318 sents
.add(PendingIntent
.getBroadcast(context
, 0, sent
, PendingIntent
.FLAG_UPDATE_CURRENT
));
1320 final Intent delivered
=new Intent(context
, SmsStatusReceiver
.class);
1321 delivered
.putExtra(SmsStatusReceiver
.EXTRA_DESTINATION
, fullDestination
);
1322 delivered
.putExtra(SmsStatusReceiver
.EXTRA_PART
, i
+1);
1323 delivered
.putExtra(SmsStatusReceiver
.EXTRA_TOTAL
, messages
.size());
1324 delivered
.putExtra(SmsStatusReceiver
.EXTRA_REPLY_TO
, replyTo
.toString());
1325 delivered
.setAction(SmsStatusReceiver
.DELIVERED_ACTION
+i
);//actions must be unique
1326 delivereds
.add(PendingIntent
.getBroadcast(context
, 0, delivered
, PendingIntent
.FLAG_UPDATE_CURRENT
));
1329 Log
.d(Heavy
.class.getName(), "Sending sms to "+destination
);
1330 manager
.sendMultipartTextMessage(destination
, null, messages
, sents
, delivereds
);
1334 * Send the last SMSes to an Address.
1336 * @param context Context instance
1337 * @param replyTo destination Address
1338 * @param numSms how many SMSes to send
1340 public static void smslog(final Context context
, final Address replyTo
, final int numSms
) {
1341 final String
[] fields
= {"type","address", "body", "date"};
1343 final Cursor cursor
= context
.getContentResolver().query (
1344 Uri
.parse("content://sms"),
1351 if (cursor
.moveToFirst()) {
1353 final String fromNumber
=cursor
.getString(1);
1355 final String name
=Utils
.callerId(context
, Utils
.toNonNull(fromNumber
));
1359 from
=fromNumber
+" ("+name
+')';
1360 final String message
=cursor
.getString(2).replace("\n", "\n ");
1361 final Date date
=new Date(cursor
.getLong(3));
1363 if(cursor
.getInt(0)==1)
1364 Utils
.sendMessage(context
, replyTo
, incoming_message
, from
, message
, date
);
1366 Utils
.sendMessage(context
, replyTo
, outgoing_message
, from
, message
, date
);
1367 } while (cursor
.moveToNext() && cursor
.getPosition() < numSms
);
1373 /** TTS instance, only used by {@link #speak(Context, Address, String)} */
1374 private static TextToSpeech tts
;
1377 * Speak a String using the text-to-speech engine.
1379 * @param context Context instance
1380 * @param replyTo reply Address
1381 * @param text text to speak
1383 public static void speak(final Context context
, final Address replyTo
, final String text
){
1384 tts
=new TextToSpeech(context
, new OnInitListener() {
1386 public void onInit(final int status
) {
1387 if(status
==TextToSpeech
.SUCCESS
){
1388 Utils
.sendMessage(context
, replyTo
, speaking
);
1389 tts
.speak(text
, TextToSpeech
.QUEUE_ADD
, null);
1391 Utils
.sendMessage(context
, replyTo
, tts_engine_not_available
);
1397 * Show a toast notification with the default duration.
1399 * @param context Context instance
1400 * @param replyTo reply Address
1401 * @param text toast text
1403 public static void toast(final Context context
, final Address replyTo
, final String text
){
1404 toast(context
, replyTo
, text
, Toast
.LENGTH_SHORT
);
1408 * Show a toast notification.
1410 * @param context Context instance
1411 * @param replyTo reply Address
1412 * @param text toast text
1413 * @param duration toast duration
1415 public static void toast(final Context context
, final Address replyTo
, final String text
, final int duration
){
1416 Toast
.makeText(context
,text
,duration
).show();
1417 Utils
.sendMessage(context
, replyTo
, toast_shown
);
1421 * Make the phone vibrate for a number of milliseconds.
1423 * @param context Context instance
1424 * @param replyTo reply Address
1425 * @param ms vibrate duration, in milliseconds
1427 public static void vibrate(final Context context
, final Address replyTo
, final long ms
){
1428 final Vibrator v
=(Vibrator
) context
.getSystemService(Context
.VIBRATOR_SERVICE
);
1429 Utils
.sendMessage(context
, replyTo
, vibrating
);
1434 * View an URI in an appropriate activity.
1436 * @param context Context instance
1437 * @param replyTo reply Address
1438 * @param uri URI to view
1440 public static void view(final Context context
, final Address replyTo
, final Uri uri
) {
1442 final Intent intent
=new Intent(Intent
.ACTION_VIEW
);
1443 intent
.setData(uri
);
1444 intent
.setFlags(Intent
.FLAG_FROM_BACKGROUND
|Intent
.FLAG_ACTIVITY_NEW_TASK
);
1445 context
.startActivity(intent
);
1446 Utils
.sendMessage(context
, replyTo
, url_opened
);
1447 } catch(ActivityNotFoundException e
){
1448 Utils
.sendMessage(context
, replyTo
, no_activity_found_for_this_url
);
1449 } catch(Exception e
){
1450 Utils
.sendMessage(context
, replyTo
, invalid_url
);
1455 * Get the current WiFi state.
1457 * @param context Context instance
1458 * @param replyTo reply Address
1460 public static void wifi(final Context context
, final Address replyTo
){
1461 final WifiManager man
=(WifiManager
) context
.getSystemService(Context
.WIFI_SERVICE
);
1462 if(man
.isWifiEnabled())
1463 Utils
.sendMessage(context
, replyTo
, wifi_on
);
1465 Utils
.sendMessage(context
, replyTo
, wifi_off
);
1469 * Set the WiFi state.
1471 * @param context Context instance
1472 * @param replyTo reply Address
1473 * @param on the requested WiFi state
1475 public static void wifi(final Context context
, final Address replyTo
, final boolean on
){
1476 final WifiManager man
=(WifiManager
) context
.getSystemService(Context
.WIFI_SERVICE
);
1477 man
.setWifiEnabled(on
);
1479 Utils
.sendMessage(context
, replyTo
, enabling_wifi
);
1481 Utils
.sendMessage(context
, replyTo
, disabling_wifi
);
1485 * Factory reset the phone, optionally deleting the SD card too.
1487 * @param context Context instance
1488 * @param type {@link Utils.WipeType} instance
1489 * @throws SecurityException if FonBot does not have device administration permissions
1491 @SuppressLint("InlinedApi")
1492 public static void wipe(final Context context
, final WipeType type
) throws SecurityException
{
1493 final DevicePolicyManager dpm
=(DevicePolicyManager
) context
.getSystemService(Context
.DEVICE_POLICY_SERVICE
);
1500 dpm
.wipeData(DevicePolicyManager
.WIPE_EXTERNAL_STORAGE
);
1506 * Disable a Command. The command cannot be used until enabled again with the {@link Utils.Command#ENABLE ENABLE} command.
1508 * @param context Context instance
1509 * @param replyTo reply Address
1510 * @param command Command to disable
1512 public static void disable(final Context context
, final Address replyTo
, final Command command
){
1513 PreferenceManager
.getDefaultSharedPreferences(context
).edit()
1514 .putBoolean(command
+"disabled", true).commit();
1515 Utils
.sendMessage(context
, replyTo
, command_disabled
, command
);
1519 * Re-enable a disabled Command.
1521 * @param context Context instance
1522 * @param replyTo reply Address
1523 * @param command Command to re-enable
1525 public static void enable(final Context context
, final Address replyTo
, final Command command
){
1526 PreferenceManager
.getDefaultSharedPreferences(context
).edit()
1527 .remove(command
+"disabled").commit();
1528 Utils
.sendMessage(context
, replyTo
, command_enabled
, command
);
1533 * Check whether a Command is disabled.
1535 * @param context Context instance
1536 * @param command Command to check
1537 * @return true if the Command is disabled, false otherwise
1539 public static boolean isCommandDisabled(final Context context
, final Command command
){
1540 return PreferenceManager
.getDefaultSharedPreferences(context
).getBoolean(command
+"disabled", false);
1544 * Poll the server for pending commands.
1546 * @param context Context instance
1547 * @param replyTo reply Address
1549 public static void poll(final Context context
, final Address replyTo
) {
1550 Utils
.sendMessage(context
, replyTo
, polling_server
);
1551 Utils
.pollServer(context
);
1555 * Change the server poll interval.
1557 * @param context Context instance
1558 * @param replyTo reply Address
1559 * @param ms server poll interval in milliseconds. If 0, server poll is disabled
1561 public static void poll(final Context context
, final Address replyTo
, final long ms
){
1562 final AlarmManager man
=(AlarmManager
) context
.getSystemService(Context
.ALARM_SERVICE
);
1563 final Intent pollAlarm
=new Intent(context
, FonBotMainService
.class);
1564 pollAlarm
.setAction(FonBotMainService
.ACTION_TRIGGER_POLL
);
1565 final PendingIntent intent
=PendingIntent
.getService(context
, 0, pollAlarm
, 0);
1567 Utils
.unregisterOngoing(context
, toNonNull(OngoingEvent
.POLL
));
1569 Utils
.sendMessage(context
, replyTo
, polling_stopped
);
1571 Utils
.registerOngoing(context
, toNonNull(OngoingEvent
.POLL
));
1572 man
.setRepeating(AlarmManager
.RTC_WAKEUP
, 0, ms
, intent
);
1573 Utils
.sendMessage(context
, replyTo
, polling_every_milliseconds
, Long
.valueOf(ms
));
1578 * Get an instance of {@link ITelephony}
1580 * @param context Context instance
1581 * @return an instance of {@link ITelephony}
1582 * @throws NoSuchMethodException thrown by reflection
1583 * @throws IllegalArgumentException thrown by reflection
1584 * @throws IllegalAccessException thrown by reflection
1585 * @throws InvocationTargetException thrown by reflection
1587 private static ITelephony
getITelephony(final Context context
) throws NoSuchMethodException
, IllegalArgumentException
, IllegalAccessException
, InvocationTargetException
{
1588 final TelephonyManager man
=(TelephonyManager
) context
.getSystemService(Context
.TELEPHONY_SERVICE
);
1589 final Method m
=TelephonyManager
.class.getDeclaredMethod("getITelephony");
1590 m
.setAccessible(true);
1591 return toNonNull((ITelephony
) m
.invoke(man
));
1595 * Hang up the phone.
1597 * @param context Context instance
1598 * @param replyTo reply Address
1600 public static void hangup(final Context context
, final Address replyTo
){
1602 getITelephony(context
).endCall();
1603 } catch(Exception e
){
1604 Utils
.sendMessage(context
, replyTo
, exception_while_hanging_up_call
,
1605 e
.getClass().getName(), e
.getMessage());
1610 * Answer the phone if it is ringing.
1612 * @param context Context instance
1613 * @param replyTo reply Address
1615 public static void answer(final Context context
, final Address replyTo
){
1617 getITelephony(context
).answerRingingCall();
1618 } catch(Exception e
){
1619 Utils
.sendMessage(context
, replyTo
, exception_while_answering_call
,
1620 e
.getClass().getName(), e
.getMessage());
1627 * @param context Context instance
1628 * @param replyTo reply Address
1629 * @param pkg name of the package to launch
1631 public static void launch(final Context context
, final Address replyTo
, final String pkg
){
1632 final Intent intent
=context
.getPackageManager().getLaunchIntentForPackage(pkg
);
1634 Utils
.sendMessage(context
, replyTo
, no_such_package
);
1637 context
.startActivity(intent
);
1638 Utils
.sendMessage(context
, replyTo
, app_launched
);
1642 * Get the mobile data enabled status.
1644 * @param context Context instance
1645 * @param replyTo reply Address
1647 public static void data(final Context context
, final Address replyTo
){
1649 final ConnectivityManager man
=(ConnectivityManager
) context
.getSystemService(Context
.CONNECTIVITY_SERVICE
);
1650 final Method m
=ConnectivityManager
.class.getDeclaredMethod("getMobileDataEnabled");
1651 m
.setAccessible(true);
1652 if(((Boolean
)m
.invoke(man
)).booleanValue())
1653 Utils
.sendMessage(context
, replyTo
, data_on
);
1655 Utils
.sendMessage(context
, replyTo
, data_off
);
1656 } catch(Exception e
){
1657 Utils
.sendMessage(context
, replyTo
, exception_while_determining_data_state
,
1658 e
.getClass().getName(), e
.getMessage());
1663 * Set the mobile data enabled status.
1665 * @param context Context instance
1666 * @param replyTo reply Address
1667 * @param enable whether to enable mobile data
1669 public static void data(final Context context
, final Address replyTo
, final boolean enable
) {
1672 getITelephony(context
).enableDataConnectivity();
1673 Utils
.sendMessage(context
, replyTo
, enabling_data
);
1675 getITelephony(context
).disableDataConnectivity();
1676 Utils
.sendMessage(context
, replyTo
, disabling_data
);
1678 } catch(Exception e
){
1679 Utils
.sendMessage(context
, replyTo
, exception_while_getting_itelephony
,
1680 e
.getClass().getName(), e
.getMessage());
1685 * Get the GPS status.
1687 * @param context Context instance
1688 * @param replyTo reply Address
1690 public static void gps(final Context context
, final Address replyTo
){
1691 if(Secure
.isLocationProviderEnabled(context
.getContentResolver(), LocationManager
.GPS_PROVIDER
))
1692 Utils
.sendMessage(context
, replyTo
, gps_on
);
1694 Utils
.sendMessage(context
, replyTo
, gps_off
);
1698 * Set the GPS status.
1700 * @param context Context instance
1701 * @param replyTo reply Address
1702 * @param enabled requested GPS status
1704 public static void gps(final Context context
, final Address replyTo
, final boolean enabled
) {
1705 Secure
.setLocationProviderEnabled(context
.getContentResolver(), LocationManager
.GPS_PROVIDER
, enabled
);
1707 Utils
.sendMessage(context
, replyTo
, enabling_gps
);
1709 Utils
.sendMessage(context
, replyTo
, disabling_gps
);
1713 * Get the Google location (aka network location) state.
1715 * @param context Context instance
1716 * @param replyTo reply Address
1718 public static void glocation(final Context context
, final Address replyTo
){
1719 if(Secure
.isLocationProviderEnabled(context
.getContentResolver(), LocationManager
.NETWORK_PROVIDER
))
1720 Utils
.sendMessage(context
, replyTo
, network_location_on
);
1722 Utils
.sendMessage(context
, replyTo
, network_location_off
);
1726 * Set the Google location (aka network location) state.
1728 * @param context Context instance
1729 * @param replyTo reply Address
1730 * @param enabled requested Google location state
1732 public static void glocation(final Context context
, final Address replyTo
, final boolean enabled
) {
1733 Secure
.setLocationProviderEnabled(context
.getContentResolver(), LocationManager
.NETWORK_PROVIDER
, enabled
);
1735 Utils
.sendMessage(context
, replyTo
, enabling_network_location
);
1737 Utils
.sendMessage(context
, replyTo
, disabling_network_location
);
1743 * @param context Context instance
1744 * @param replyTo reply Address
1745 * @param reason reboot reason
1747 * @see PowerManager#reboot(String)
1749 public static void reboot(final Context context
, final Address replyTo
, final @Nullable String reason
) {
1750 final PowerManager pm
=(PowerManager
) context
.getSystemService(Context
.POWER_SERVICE
);
1751 Utils
.sendMessage(context
, replyTo
, rebooting
);
1756 * Cancel a notification.
1758 * @param context Context instance
1759 * @param replyTo reply Address
1760 * @param id notification ID
1762 public static void notify(final Context context
, final Address replyTo
, final int id
) {
1763 final NotificationManager man
=(NotificationManager
) context
.getSystemService(Context
.NOTIFICATION_SERVICE
);
1765 Utils
.sendMessage(context
, replyTo
, notification_canceled
);
1769 * Show a notification.
1771 * @param context Context instance
1772 * @param replyTo reply Address
1773 * @param id notification ID
1774 * @param title notificationO title
1775 * @param text notification text
1777 public static void notify(final Context context
, final Address replyTo
, final int id
, final String title
, final String text
) {
1778 final NotificationManager man
=(NotificationManager
) context
.getSystemService(Context
.NOTIFICATION_SERVICE
);
1779 man
.notify(id
, new NotificationCompat
.Builder(context
).
1780 setContentTitle(title
).
1781 setContentText(text
).
1782 setSmallIcon(android
.R
.drawable
.stat_notify_sync_noanim
).
1784 Utils
.sendMessage(context
, replyTo
, notification_shown
);
1788 * Take a screen capture. Uses the screencap utility and requires root.
1790 * @param context Context instance
1791 * @param replyTo reply Address
1792 * @param filename capture file location
1794 public static void screencap(final Context context
, final Address replyTo
, final String filename
){
1795 new Thread(new ScreencapRunnable(context
, replyTo
, filename
)).start();
1799 * Toggle the torch state using the Torch (net.cactii.torch2) app.
1801 * @param context Context instance
1802 * @param replyTo reply Address
1804 public static void torch(final Context context
, final Address replyTo
){
1805 context
.sendBroadcast(new Intent("net.cactii.flash2.TOGGLE_FLASHLIGHT"));
1806 Utils
.sendMessage(context
, replyTo
, toggling_torch_state
);