Use sendMultipartTextMessage for respond with sms

Long messages won't send properly if used in the respond-with-sms API.
Use sendMultipartTextMessage to get around this. Also display an error
toast if the message didn't send properly.

Bug: 65087120
Test: manual
Change-Id: I31ba24a0600e42010752b1216b1aca7d1098b319
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 9202e74..b17c6f3 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -21,6 +21,8 @@
         android:sharedUserId="android.uid.system">
 
     <protected-broadcast android:name="android.intent.action.SHOW_MISSED_CALLS_NOTIFICATION" />
+    <protected-broadcast android:name="com.android.server.telecom.MESSAGE_SENT" />
+
 
     <!-- Prevents the activity manager from delaying any activity-start
          requests by this package, including requests immediately after
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 8c29a21..9065173 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -79,6 +79,10 @@
         a text response. [CHAR LIMIT=40] -->
     <string name="respond_via_sms_confirmation_format">Message sent to <xliff:g id="phone_number">%s</xliff:g>.</string>
 
+    <!-- "Respond via SMS": Error toast shown after failing to send
+        a text response. [CHAR LIMIT=40] -->
+    <string name="respond_via_sms_failure_format">Message failed to send to <xliff:g id="phone_number">%s</xliff:g>.</string>
+
     <!-- Title of settings screen that allows user to enable and disable phone-accounts.
          Each method for placing a call (SIM1, SIM2, SIP account, etc) has a phone-account.
          Phone-accounts that are created by third party apps can be disabled and enabled by user.
diff --git a/src/com/android/server/telecom/RespondViaSmsManager.java b/src/com/android/server/telecom/RespondViaSmsManager.java
index fadc6b5..4c13222 100644
--- a/src/com/android/server/telecom/RespondViaSmsManager.java
+++ b/src/com/android/server/telecom/RespondViaSmsManager.java
@@ -18,10 +18,14 @@
 
 // TODO: Needed for move to system service: import com.android.internal.R;
 import com.android.internal.os.SomeArgs;
-import com.android.internal.telephony.SmsApplication;
 
+import android.app.Activity;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.SharedPreferences;
 import android.content.res.Resources;
 import android.os.Handler;
@@ -45,30 +49,37 @@
  * Helper class to manage the "Respond via Message" feature for incoming calls.
  */
 public class RespondViaSmsManager extends CallsManagerListenerBase {
-    private static final int MSG_SHOW_SENT_TOAST = 2;
+    private static final String ACTION_MESSAGE_SENT = "com.android.server.telecom.MESSAGE_SENT";
+
+    private static final class MessageSentReceiver extends BroadcastReceiver {
+        private final String mContactName;
+        private final int mNumMessageParts;
+        private int mNumMessagesSent = 0;
+        MessageSentReceiver(String contactName, int numMessageParts) {
+            mContactName = contactName;
+            mNumMessageParts = numMessageParts;
+        }
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (getResultCode() == Activity.RESULT_OK) {
+                mNumMessagesSent++;
+                if (mNumMessagesSent == mNumMessageParts) {
+                    showMessageResultToast(mContactName, context, true);
+                    context.unregisterReceiver(this);
+                }
+            } else {
+                context.unregisterReceiver(this);
+                showMessageResultToast(mContactName, context, false);
+                Log.w(RespondViaSmsManager.class.getSimpleName(),
+                        "Message failed with error %s", getResultCode());
+            }
+        }
+    }
 
     private final CallsManager mCallsManager;
     private final TelecomSystem.SyncRoot mLock;
 
-    private final Handler mHandler = new Handler(Looper.getMainLooper()) {
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MSG_SHOW_SENT_TOAST: {
-                    SomeArgs args = (SomeArgs) msg.obj;
-                    try {
-                        String toastMessage = (String) args.arg1;
-                        Context context = (Context) args.arg2;
-                        showMessageSentToast(toastMessage, context);
-                    } finally {
-                        args.recycle();
-                    }
-                    break;
-                }
-            }
-        }
-    };
-
     public RespondViaSmsManager(CallsManager callsManager, TelecomSystem.SyncRoot lock) {
         mCallsManager = callsManager;
         mLock = lock;
@@ -144,13 +155,15 @@
         }
     }
 
-    private void showMessageSentToast(final String phoneNumber, final Context context) {
+    private static void showMessageResultToast(final String phoneNumber,
+            final Context context, boolean success) {
         // ...and show a brief confirmation to the user (since
         // otherwise it's hard to be sure that anything actually
         // happened.)
         final Resources res = context.getResources();
-        final String formatString = res.getString(
-                R.string.respond_via_sms_confirmation_format);
+        final String formatString = res.getString(success
+                ? R.string.respond_via_sms_confirmation_format
+                : R.string.respond_via_sms_failure_format);
         final String confirmationMsg = String.format(formatString, phoneNumber);
         int startingPosition = confirmationMsg.indexOf(phoneNumber);
         int endingPosition = startingPosition + phoneNumber.length();
@@ -192,13 +205,20 @@
 
         SmsManager smsManager = SmsManager.getSmsManagerForSubscriptionId(subId);
         try {
-            smsManager.sendTextMessage(phoneNumber, null, textMessage, null /*sentIntent*/,
-                    null /*deliveryIntent*/);
-
-            SomeArgs args = SomeArgs.obtain();
-            args.arg1 = !TextUtils.isEmpty(contactName) ? contactName : phoneNumber;
-            args.arg2 = context;
-            mHandler.obtainMessage(MSG_SHOW_SENT_TOAST, args).sendToTarget();
+            ArrayList<String> messageParts = smsManager.divideMessage(textMessage);
+            ArrayList<PendingIntent> sentIntents = new ArrayList<>(messageParts.size());
+            for (int i = 0; i < messageParts.size(); i++) {
+                Intent intent = new Intent(ACTION_MESSAGE_SENT);
+                PendingIntent pendingIntent = PendingIntent.getBroadcast(context, i, intent,
+                        PendingIntent.FLAG_ONE_SHOT);
+                sentIntents.add(pendingIntent);
+            }
+            MessageSentReceiver receiver = new MessageSentReceiver(
+                    !TextUtils.isEmpty(contactName) ? contactName : phoneNumber,
+                    messageParts.size());
+            context.registerReceiver(receiver, new IntentFilter(ACTION_MESSAGE_SENT));
+            smsManager.sendMultipartTextMessage(phoneNumber, null, messageParts,
+                    sentIntents/*sentIntent*/, null /*deliveryIntent*/);
         } catch (IllegalArgumentException e) {
             Log.w(RespondViaSmsManager.this, "Couldn't send SMS message: " +
                     e.getMessage());