7 package com.google.appinventor.components.runtime;
9 import java.io.BufferedReader;
10 import java.io.FileNotFoundException;
11 import java.io.FileOutputStream;
12 import java.io.IOException;
13 import java.io.InputStream;
14 import java.io.InputStreamReader;
15 import java.io.OutputStreamWriter;
16 import java.net.CookieManager;
17 import java.net.HttpCookie;
18 import java.net.HttpURLConnection;
20 import java.net.URLEncoder;
21 import java.util.List;
22 import java.util.Queue;
23 import java.util.concurrent.ConcurrentLinkedQueue;
25 import android.Manifest;
26 import android.net.Uri;
27 import android.text.TextUtils;
28 import org.json.JSONException;
29 import org.json.JSONObject;
30 import java.util.ArrayList;
57 import android.app.Activity;
58 import android.app.NotificationManager;
59 import android.app.PendingIntent;
60 import android.content.BroadcastReceiver;
61 import android.content.Context;
62 import android.content.Intent;
63 import android.content.IntentFilter;
64 import android.content.SharedPreferences;
65 import android.os.AsyncTask;
66 import android.telephony.SmsManager;
67 import android.telephony.SmsMessage;
68 import android.util.Log;
69 import android.widget.Toast;
113 @SuppressWarnings(
"deprecation")
114 @DesignerComponent(version = YaVersion.TEXTING_COMPONENT_VERSION,
115 description =
"<p>A component that will, when the <code>SendMessage</code> method is " +
116 "called, send the text message specified in the <code>Message</code> " +
117 "property to the phone number specified in the <code>PhoneNumber</code> " +
119 "<p>If the <code>ReceivingEnabled</code> property is set to 1 messages " +
120 "will <b>not</b> be received. If <code>ReceivingEnabled</code> is set " +
121 "to 2 messages will be received only when the application is " +
122 "running. Finally if <code>ReceivingEnabled</code> is set to 3, " +
123 "messages will be received when the application is running <b>and</b> " +
124 "when the application is not running they will be queued and a " +
125 "notification displayed to the user.</p> " +
126 "<p>When a message arrives, the <code>MessageReceived</code> event is " +
127 "raised and provides the sending number and message.</p> " +
128 "<p> An app that includes this component will receive messages even " +
129 "when it is in the background (i.e. when it's not visible on the " +
130 "screen) and, moreso, even if the app is not running, so long as it's " +
131 "installed on the phone. If the phone receives a text message when the " +
132 "app is not in the foreground, the phone will show a notification in " +
133 "the notification bar. Selecting the notification will bring up the " +
134 "app. As an app developer, you'll probably want to give your users the " +
135 "ability to control ReceivingEnabled so that they can make the phone " +
136 "ignore text messages.</p> " +
137 "<p>If the GoogleVoiceEnabled property is true, messages can be sent " +
138 "over Wifi using Google Voice. This option requires that the user have " +
139 "a Google Voice account and that the mobile Voice app is installed on " +
140 "the phone. The Google Voice option works only on phones that support " +
141 "Android 2.0 (Eclair) or higher.</p> " +
142 "<p>To specify the phone number (e.g., 650-555-1212), set the " +
143 "<code>PhoneNumber</code> property to a Text string with the specified " +
144 "digits (e.g., 6505551212). Dashes, dots, and parentheses may be " +
145 "included (e.g., (650)-555-1212) but will be ignored; spaces may not be " +
147 "<p>Another way for an app to specify a phone number would be to " +
148 "include a <code>PhoneNumberPicker</code> component, which lets the " +
149 "users select a phone numbers from the ones stored in the the phone's " +
151 category = ComponentCategory.SOCIAL,
153 iconName =
"images/texting.png")
156 @UsesPermissions(permissionNames =
157 "com.google.android.apps.googlevoice.permission.RECEIVE_SMS, " +
158 "com.google.android.apps.googlevoice.permission.SEND_SMS, " +
159 "android.permission.ACCOUNT_MANAGER, android.permission.MANAGE_ACCOUNTS, " +
160 "android.permission.GET_ACCOUNTS, android.permission.USE_CREDENTIALS")
161 @UsesLibraries(libraries =
162 "google-api-client-beta.jar," +
163 "google-api-client-android2-beta.jar," +
164 "google-http-client-beta.jar," +
165 "google-http-client-android2-beta.jar," +
166 "google-http-client-android3-beta.jar," +
167 "google-oauth-client-beta.jar," +
173 public static final String TAG =
"Texting Component";
178 public static final int TEXTING_REQUEST_CODE = 0x54455854;
180 public static final String SMS_RECEIVED =
"android.provider.Telephony.SMS_RECEIVED";
181 public static final String GV_SMS_RECEIVED =
"com.google.android.apps.googlevoice.SMS_RECEIVED";
182 public static final String PHONE_NUMBER_TAG =
"com.google.android.apps.googlevoice.PHONE_NUMBER";
183 public static final String MESSAGE_TAG =
"com.google.android.apps.googlevoice.TEXT";
184 public static final String TELEPHONY_INTENT_FILTER =
"android.provider.Telephony.SMS_RECEIVED";
185 public static final String GV_INTENT_FILTER =
"com.google.android.apps.googlevoice.SMS_RECEIVED";
186 public static final String GV_PACKAGE_NAME =
"com.google.android.apps.googlevoice";
187 public static final String GV_SMS_SEND_URL =
"https://www.google.com/voice/b/0/sms/send/";
188 public static final String GV_URL =
"https://www.google.com/voice/b/0/redirection/voice";
193 public static final String META_DATA_SMS_KEY =
"sms_handler_component";
194 public static final String META_DATA_SMS_VALUE =
"Texting";
197 private static final String GV_SERVICE =
"grandcentral";
198 private static final String USER_AGENT =
"Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/525.13 (KHTML, like Gecko) Chrome/0.A.B.C Safari/525.13";
199 private static final int SERVER_TIMEOUT_MS = 30000;
200 private static final String SENT =
"SMS_SENT";
201 private static final String UTF8 =
"UTF-8";
202 private static final String MESSAGE_DELIMITER =
"\u0001";
203 private static final String PREF_GVENABLED =
"gvenabled";
204 private static final String PREF_RCVENABLED_LEGACY =
"receiving";
205 private static final String PREF_RCVENABLED =
"receiving2";
206 private static final String PREF_FILE =
"TextingState";
210 private GoogleVoiceUtil gvHelper;
211 private static Activity activity;
213 private String authToken;
217 private SmsManager smsManager;
220 private String phoneNumber;
222 private String message;
224 private boolean googleVoiceEnabled;
227 private boolean isInitialized;
231 private static boolean isRunning;
234 private static final String CACHE_FILE =
"textingmsgcache";
235 private static int messagesCached;
236 private static Object cacheLock =
new Object();
239 private Queue<String> pendingQueue =
new ConcurrentLinkedQueue<String>();
244 private boolean havePermission =
false;
247 private boolean haveReceivePermission =
false;
255 super(container.
$form());
256 Log.d(TAG,
"Texting constructor");
257 this.container = container;
261 SharedPreferences prefs = activity.getSharedPreferences(PREF_FILE, Activity.MODE_PRIVATE);
263 receivingEnabled = prefs.getInt(PREF_RCVENABLED, -1);
264 if (receivingEnabled == -1) {
265 if (prefs.getBoolean(PREF_RCVENABLED_LEGACY,
true)) {
272 googleVoiceEnabled = prefs.getBoolean(PREF_GVENABLED,
false);
273 Log.i(TAG,
"Starting with receiving Enabled=" + receivingEnabled +
" GV enabled=" + googleVoiceEnabled);
276 googleVoiceEnabled =
false;
280 if (googleVoiceEnabled)
281 new AsyncAuthenticate().execute();
283 smsManager = SmsManager.getDefault();
286 isInitialized =
false;
303 Log.i(TAG,
"onInitialize()");
304 isInitialized =
true;
306 processCachedMessages();
307 NotificationManager nm = (NotificationManager) activity.getSystemService(Context.NOTIFICATION_SERVICE);
315 requestReceiveSmsPermission(
"Initialize");
326 public
void PhoneNumber(String phoneNumber) {
327 Log.i(TAG,
"PhoneNumber set: " + phoneNumber);
328 this.phoneNumber = phoneNumber;
338 description =
"The number that the message will be sent to when the SendMessage method " +
340 "number is a text string with the specified digits (e.g., 6505551212). Dashes, dots, " +
341 "and parentheses may be included (e.g., (650)-555-1212) but will be ignored; spaces " +
342 "should not be included.")
343 public String PhoneNumber() {
356 description =
"The message that will be sent when the SendMessage method is called.")
357 public
void Message(String message) {
358 Log.i(TAG,
"Message set: " + message);
359 this.message = message;
376 String phoneNumber = this.phoneNumber;
377 String message = this.message;
379 Uri uri = Uri.parse(
"smsto:" + phoneNumber);
380 Intent i =
new Intent(Intent.ACTION_SENDTO, uri);
381 i.putExtra(
"sms_body", message);
382 if (i.resolveActivity(form.getPackageManager()) !=
null) {
383 form.registerForActivityResult(
this, TEXTING_REQUEST_CODE);
384 form.startActivityForResult(i, TEXTING_REQUEST_CODE);
393 @
UsesPermissions({Manifest.permission.SEND_SMS, Manifest.permission.READ_PHONE_STATE})
396 Log.i(TAG,
"Sending message " + message +
" to " + phoneNumber);
399 String phoneNumber = this.phoneNumber;
400 String message = this.message;
403 if (this.googleVoiceEnabled) {
406 if (authToken ==
null) {
407 Log.i(TAG,
"Need to get an authToken -- enqueing " + phoneNumber +
" " + message);
408 boolean ok = pendingQueue.offer(phoneNumber +
":::" + message);
412 Toast.makeText(activity,
"Pending message queue full. Can't send message", Toast.LENGTH_SHORT).show();
419 if (pendingQueue.size() == 1)
420 new AsyncAuthenticate().execute();
424 Log.i(TAG,
"Creating AsyncSendMessage");
425 new AsyncSendMessage().execute(phoneNumber, message);
430 Log.i(TAG,
"Sending via SMS");
431 this.sendViaSms(
"SendMessage");
438 private void processPendingQueue() {
439 while (pendingQueue.size() != 0) {
440 String entry = (String)pendingQueue.remove();
441 String phoneNumber = entry.substring(0,entry.indexOf(
":::"));
442 String message = entry.substring(entry.indexOf(
":::") + 3);
443 Log.i(TAG,
"Sending queued message " + phoneNumber +
" " + message);
444 new AsyncSendMessage().execute(phoneNumber, message);
459 Log.i(TAG,
"MessageReceived from " + number +
":" + messageText);
461 Log.i(TAG,
"Dispatch successful");
463 Log.i(TAG,
"Dispatch failed, caching");
464 synchronized (cacheLock) {
465 addMessageToCache(activity, number, messageText);
482 description =
"If true, then SendMessage will attempt to send messages over Wifi " +
483 "using Google Voice. This requires that the Google Voice app must be installed " +
484 "and set up on the phone or tablet, with a Google Voice account. If GoogleVoiceEnabled " +
485 "is false, the device must have phone and texting service in order to send or " +
486 "receive messages with this component.")
487 public
boolean GoogleVoiceEnabled() {
488 return googleVoiceEnabled;
501 @
ReceiverElement(name =
"com.google.appinventor.components.runtime.util.SmsBroadcastReceiver",
504 @
ActionElement(name =
"com.google.android.apps.googlevoice.SMS_RECEIVED")
510 this.googleVoiceEnabled = enabled;
511 SharedPreferences prefs = activity.getSharedPreferences(PREF_FILE, Activity.MODE_PRIVATE);
512 SharedPreferences.Editor editor = prefs.edit();
513 editor.putBoolean(PREF_GVENABLED, enabled);
516 Toast.makeText(activity,
"Sorry, your phone's system does not support this option.", Toast.LENGTH_LONG).show();
531 description =
"If set to 1 (OFF) no messages will be received. If set to 2 (FOREGROUND) or" +
532 "3 (ALWAYS) the component will respond to messages if it is running. If the " +
533 "app is not running then the message will be discarded if set to 2 " +
534 "(FOREGROUND). If set to 3 (ALWAYS) and the app is not running the phone will " +
535 "show a notification. Selecting the notification will bring up the app " +
536 "and signal the MessageReceived event. Messages received when the app " +
537 "is dormant will be queued, and so several MessageReceived events might " +
538 "appear when the app awakens. As an app developer, it would be a good " +
539 "idea to give your users control over this property, so they can make " +
540 "their phones ignore text messages when your app is installed.")
541 public
int ReceivingEnabled() {
542 return receivingEnabled;
565 @
ReceiverElement(name =
"com.google.appinventor.components.runtime.util.SmsBroadcastReceiver",
568 @
ActionElement(name =
"android.provider.Telephony.SMS_RECEIVED"),
580 Texting.receivingEnabled = enabled;
581 SharedPreferences prefs = activity.getSharedPreferences(PREF_FILE, Activity.MODE_PRIVATE);
582 SharedPreferences.Editor editor = prefs.edit();
583 editor.putInt(PREF_RCVENABLED, enabled);
584 editor.remove(PREF_RCVENABLED_LEGACY);
587 requestReceiveSmsPermission(
"ReceivingEnabled");
592 SharedPreferences prefs = context.getSharedPreferences(PREF_FILE, Activity.MODE_PRIVATE);
593 int retval = prefs.getInt(PREF_RCVENABLED, -1);
595 if (prefs.getBoolean(PREF_RCVENABLED_LEGACY,
true))
614 Object[] messages = (Object[]) intent.getSerializableExtra(
"pdus");
615 byte[][] pduObjs =
new byte[messages.length][];
617 for (
int i = 0; i < messages.length; i++) {
618 pduObjs[i] = (
byte[]) messages[i];
620 byte[][] pdus =
new byte[pduObjs.length][];
621 int pduCount = pdus.length;
622 SmsMessage[] msgs =
new SmsMessage[pduCount];
623 for (
int i = 0; i < pduCount; i++) {
624 pdus[i] = pduObjs[i];
625 msgs[i] = SmsMessage.createFromPdu(pdus[i]);
634 private void processCachedMessages() {
635 String[] messagelist =
null;
636 synchronized (cacheLock) {
637 messagelist = retrieveCachedMessages();
639 if (messagelist ==
null)
641 Log.i(TAG,
"processing " + messagelist.length +
" cached messages ");
643 for (
int k = 0; k < messagelist.length; k++) {
644 String phoneAndMessage = messagelist[k];
645 Log.i(TAG,
"Message + " + k +
" " + phoneAndMessage);
647 int delim = phoneAndMessage.indexOf(
":");
650 if ((receivingEnabled > ComponentConstants.TEXT_RECEIVING_OFF) && delim != -1) {
651 MessageReceived(phoneAndMessage.substring(0,delim),
652 phoneAndMessage.substring(delim+1));
662 private String[] retrieveCachedMessages() {
663 Log.i(TAG,
"Retrieving cached messages");
666 byte[] bytes = FileUtil.readFile(form, CACHE_FILE);
667 cache =
new String(bytes);
668 activity.deleteFile(CACHE_FILE);
670 Log.i(TAG,
"Retrieved cache " + cache);
671 }
catch (FileNotFoundException e) {
672 Log.e(TAG,
"No Cache file found -- this is not (usually) an error");
674 }
catch (IOException e) {
675 Log.e(TAG,
"I/O Error reading from cache file");
679 String messagelist[] = cache.split(MESSAGE_DELIMITER);
696 return messagesCached;
704 Log.i(TAG,
"onResume()");
707 processCachedMessages();
708 NotificationManager nm = (NotificationManager) activity.getSystemService(Context.NOTIFICATION_SERVICE);
718 Log.i(TAG,
"onPause()");
729 MessageReceived(phone, msg);
731 synchronized (cacheLock) {
732 addMessageToCache(context, phone, msg);
743 private static void addMessageToCache(Context context, String phone, String msg) {
745 String cachedMsg = phone +
":" + msg + MESSAGE_DELIMITER;
746 Log.i(TAG,
"Caching " + cachedMsg);
747 FileOutputStream fos = context.openFileOutput(CACHE_FILE, Context.MODE_APPEND);
748 fos.write(cachedMsg.getBytes());
751 Log.i(TAG,
"Cached " + cachedMsg);
752 }
catch (FileNotFoundException e) {
753 Log.e(TAG,
"File not found error writing to cache file");
755 }
catch (IOException e) {
756 Log.e(TAG,
"I/O Error writing to cache file");
763 if (requestCode == TEXTING_REQUEST_CODE) {
764 handleSentMessage(form,
null, resultCode, data ==
null ?
"" :
765 data.getStringExtra(
"sms_body"));
766 form.unregisterForActivityResult(
this);
778 class GoogleVoiceUtil {
779 private final int MAX_REDIRECTS = 5;
780 private static final String COOKIES_HEADER =
"Set-Cookie";
786 private boolean isInitialized;
787 CookieManager cookies =
new CookieManager();
793 public GoogleVoiceUtil(String authToken) {
794 Log.i(TAG,
"Creating GV Util");
795 this.authToken = authToken;
797 this.general = getGeneral();
798 Log.i(TAG,
"general = " + this.general);
800 isInitialized =
true;
801 }
catch (IOException e) {
806 public boolean isInitialized() {
807 return isInitialized;
815 private String sendGvSms(String smsData) {
816 Log.i(TAG,
"sendGvSms()");
817 StringBuilder response =
new StringBuilder();
820 smsData +=
"&" + URLEncoder.encode(
"_rnr_se", UTF8) +
"=" + URLEncoder.encode(rnrSEE, UTF8);
821 Log.i(TAG,
"smsData = " + smsData);
822 URL smsUrl =
new URL(GV_SMS_SEND_URL);
824 HttpURLConnection smsConn = (HttpURLConnection) smsUrl.openConnection();
825 smsConn.setRequestProperty(
"Authorization",
"GoogleLogin auth=" + authToken );
826 smsConn.setRequestProperty(
"User-agent", USER_AGENT);
828 smsConn.setDoOutput(
true);
829 smsConn.setConnectTimeout(SERVER_TIMEOUT_MS);
831 Log.i(TAG,
"sms request = " + smsConn);
832 OutputStreamWriter callwr =
new OutputStreamWriter(smsConn.getOutputStream());
833 callwr.write(smsData);
836 processCookies(smsConn);
837 BufferedReader callrd =
new BufferedReader(
new InputStreamReader(smsConn.getInputStream()));
840 while ((line = callrd.readLine()) !=
null) {
841 response.append(line);
842 response.append(
"\n");
844 Log.i(TAG,
"sendGvSms: Sent SMS, response = " + response);
849 if (response.length() == 0) {
850 throw new IOException(
"No Response Data Received.");
852 return response.toString();
854 }
catch (IOException e) {
855 Log.i(TAG,
"IO Error on Send " + e.getMessage(), e);
856 return "IO Error Message not sent";
870 public String getGeneral() throws IOException {
871 Log.i(TAG,
"getGeneral()");
882 private void setRNRSEE() throws IOException {
883 Log.i(TAG,
"setRNRSEE()");
884 if (general !=
null) {
885 if(general.contains(
"'_rnr_se': '")) {
886 String p1 = general.split(
"'_rnr_se': '", 2)[1];
887 rnrSEE = p1.split(
"',", 2)[0];
888 Log.i(TAG,
"Successfully Received rnr_se.");
891 Log.i(TAG,
"Answer did not contain rnr_se! "+ general);
892 throw new IOException(
"Answer did not contain rnr_se! "+ general);
895 Log.i(TAG,
"setRNRSEE(): Answer was null!");
896 throw new IOException(
"setRNRSEE(): Answer was null!");
906 void setCookies(HttpURLConnection conn) {
907 if (cookies.getCookieStore().getCookies().size() > 0) {
908 conn.setRequestProperty(
"Cookie", TextUtils.join(
";", cookies.getCookieStore().getCookies()));
918 void processCookies(HttpURLConnection conn) {
919 List<String> cookiesHeader = conn.getHeaderFields().get(COOKIES_HEADER);
920 if (cookiesHeader !=
null) {
921 for (String cookie : cookiesHeader) {
922 cookies.getCookieStore().add(
null, HttpCookie.parse(cookie).get(0));
936 String
get(String urlString)
throws IOException {
937 URL url =
new URL(urlString);
938 HttpURLConnection conn = (HttpURLConnection) url.openConnection();
939 int responseCode = 0;
941 conn.setRequestProperty(
"Authorization",
"GoogleLogin auth="+authToken );
942 conn.setRequestProperty(
"User-agent", USER_AGENT);
943 conn.setInstanceFollowRedirects(
false);
948 responseCode = conn.getResponseCode();
949 Log.i(TAG, urlString +
" - " + conn.getResponseMessage());
950 }
catch (Exception e) {
951 throw new IOException(urlString +
" : " + conn.getResponseMessage() +
"("+responseCode+
") : IO Error.");
954 processCookies(conn);
957 if(responseCode==200) {
958 is = conn.getInputStream();
959 }
else if(responseCode==HttpURLConnection.HTTP_MOVED_PERM || responseCode==HttpURLConnection.HTTP_MOVED_TEMP || responseCode==HttpURLConnection.HTTP_SEE_OTHER || responseCode==307) {
961 if(redirectCounter > MAX_REDIRECTS) {
963 throw new IOException(urlString +
" : " + conn.getResponseMessage() +
"("+responseCode+
") : Too many redirects. exiting.");
965 String location = conn.getHeaderField(
"Location");
966 if(location!=
null && !location.equals(
"")) {
967 System.out.println(urlString +
" - " + responseCode +
" - new URL: " + location);
968 return get(location);
970 throw new IOException(urlString +
" : " + conn.getResponseMessage() +
"("+responseCode+
") : Received moved answer but no Location. exiting.");
973 is = conn.getErrorStream();
978 throw new IOException(urlString +
" : " + conn.getResponseMessage() +
"("+responseCode+
") : InputStream was null : exiting.");
984 BufferedReader rd =
new BufferedReader(
new InputStreamReader(is));
986 StringBuffer sb =
new StringBuffer();
988 while ((line = rd.readLine()) !=
null) {
989 sb.append(line +
"\n\r");
992 result = sb.toString();
993 }
catch (Exception e) {
994 throw new IOException(urlString +
" - " + conn.getResponseMessage() +
"("+responseCode+
") - " +e.getLocalizedMessage());
1017 private synchronized void handleSentMessage(Context context,
1018 BroadcastReceiver receiver,
int resultCode, String smsMsg) {
1019 switch (resultCode) {
1020 case Activity.RESULT_OK:
1021 Log.i(TAG,
"Received OK, msg:" + smsMsg);
1022 Toast.makeText(activity,
"Message sent", Toast.LENGTH_SHORT).show();
1024 case SmsManager.RESULT_ERROR_GENERIC_FAILURE:
1025 Log.e(TAG,
"Received generic failure, msg:" + smsMsg);
1026 Toast.makeText(activity,
"Generic failure: message not sent", Toast.LENGTH_SHORT).show();
1028 case SmsManager.RESULT_ERROR_NO_SERVICE:
1029 Log.e(TAG,
"Received no service error, msg:" + smsMsg);
1030 Toast.makeText(activity,
"No Sms service available. Message not sent.", Toast.LENGTH_SHORT).show();
1032 case SmsManager.RESULT_ERROR_NULL_PDU:
1033 Log.e(TAG,
"Received null PDU error, msg:" + smsMsg);
1034 Toast.makeText(activity,
"Received null PDU error. Message not sent.", Toast.LENGTH_SHORT).show();
1036 case SmsManager.RESULT_ERROR_RADIO_OFF:
1037 Log.e(TAG,
"Received radio off error, msg:" + smsMsg);
1038 Toast.makeText(activity,
"Could not send SMS message: radio off.", Toast.LENGTH_LONG).show();
1047 private void sendViaSms(
final String caller) {
1048 Log.i(TAG,
"Sending via built-in Sms");
1051 if (!havePermission) {
1052 final Form form = container.
$form();
1053 final Texting me =
this;
1054 form.runOnUiThread(
new Runnable() {
1057 form.askPermission(Manifest.permission.SEND_SMS,
1058 new PermissionResultHandler() {
1060 public void HandlePermissionResponse(String permission,
boolean granted) {
1062 me.havePermission =
true;
1063 me.sendViaSms(caller);
1065 form.dispatchPermissionDeniedEvent(me, caller, Manifest.permission.SEND_SMS);
1074 ArrayList<String> parts = smsManager.divideMessage(message);
1075 int numParts = parts.size();
1076 ArrayList<PendingIntent> pendingIntents =
new ArrayList<PendingIntent>();
1077 for (
int i = 0; i < numParts; i++)
1078 pendingIntents.add(PendingIntent.getBroadcast(activity, 0,
new Intent(SENT), 0));
1081 BroadcastReceiver sendReceiver =
new BroadcastReceiver() {
1083 public synchronized void onReceive(Context arg0, Intent arg1) {
1085 handleSentMessage(arg0,
null, getResultCode(), message);
1086 activity.unregisterReceiver(
this);
1087 }
catch (Exception e) {
1088 Log.e(
"BroadcastReceiver",
1089 "Error in onReceive for msgId " + arg1.getAction());
1090 Log.e(
"BroadcastReceiver", e.getMessage());
1091 e.printStackTrace();
1096 activity.registerReceiver(sendReceiver,
new IntentFilter(SENT));
1097 smsManager.sendMultipartTextMessage(phoneNumber,
null, parts, pendingIntents,
null);
1100 private void requestReceiveSmsPermission(
final String caller) {
1101 form.runOnUiThread(
new Runnable() {
1104 form.askPermission(Manifest.permission.RECEIVE_SMS,
1105 new PermissionResultHandler() {
1107 public void HandlePermissionResponse(String permission,
boolean granted) {
1109 haveReceivePermission =
true;
1111 form.dispatchPermissionDeniedEvent(Texting.this, caller, Manifest.permission.RECEIVE_SMS);
1123 class AsyncAuthenticate
extends AsyncTask<Void, Void, String> {
1126 protected String doInBackground(Void... arg0) {
1127 Log.i(TAG,
"Authenticating");
1130 return new OAuth2Helper().getRefreshedAuthToken(activity, GV_SERVICE);
1137 protected void onPostExecute(String result) {
1138 Log.i(TAG,
"authToken = " + result);
1141 Toast.makeText(activity,
"Finished authentication", Toast.LENGTH_SHORT).show();
1144 processPendingQueue();
1155 class AsyncSendMessage
extends AsyncTask<String, Void, String> {
1165 protected String doInBackground(String... args) {
1166 String phoneNumber = args[0];
1167 String message = args[1];
1168 String response =
"";
1169 String smsData =
"";
1171 Log.i(TAG,
"Async sending phoneNumber = " + phoneNumber +
" message = " + message);
1177 URLEncoder.encode(
"phoneNumber", UTF8) +
"=" + URLEncoder.encode(phoneNumber, UTF8) +
1178 "&" + URLEncoder.encode(
"text", UTF8) +
"=" + URLEncoder.encode(message, UTF8);
1180 if (gvHelper ==
null) {
1181 gvHelper =
new GoogleVoiceUtil(authToken);
1183 if (gvHelper.isInitialized()) {
1184 response = gvHelper.sendGvSms(smsData);
1185 Log.i(TAG,
"Sent SMS, response = " + response);
1187 return "IO Error: unable to create GvHelper";
1189 }
catch (Exception e) {
1190 e.printStackTrace();
1196 protected void onPostExecute(String result) {
1197 super.onPostExecute(result);
1203 json =
new JSONObject(result);
1204 ok = json.getBoolean(
"ok");
1205 code = json.getJSONObject(
"data").getInt(
"code");
1206 }
catch (JSONException e) {
1208 e.printStackTrace();
1211 Toast.makeText(activity,
"Message sent", Toast.LENGTH_SHORT).show();
1212 else if (code == 58)
1213 Toast.makeText(activity,
"Errcode 58: SMS limit reached", Toast.LENGTH_SHORT).show();
1214 else if (result.contains(
"IO Error"))
1215 Toast.makeText(activity, result, Toast.LENGTH_SHORT).show();
1224 SharedPreferences prefs = activity.getSharedPreferences(PREF_FILE, Activity.MODE_PRIVATE);
1225 SharedPreferences.Editor editor = prefs.edit();
1226 editor.putInt(PREF_RCVENABLED, receivingEnabled);
1227 editor.putBoolean(PREF_GVENABLED, googleVoiceEnabled);
1233 form.unregisterForActivityResult(
this);