2012-03-14 15 views
9

En mi aplicación habrá una notificación de envío de sms a una acción. ¿Ahora cómo me aseguro de que se envíe el SMS?Cómo asegurarse de que se envíe un SMS en Android

Por ejemplo, si no hay removeed red o tarjeta SIM, ¿cómo asegurarse de que el SMS se enviará una vez que hay una red disponible en un momento posterior? ¿Es posible agregar los sms a la cola?

¿Esto es posible?

Gracias por adelantado, Perumal

Respuesta

2

Puede capturar el estado de envío/entrega de SMS con un receptor de difusión. El BroadcastReceiver debe estar registrado antes de enviar el SMS. Yo uso esta aplicación, junto con una clase padre, para obtener el resultado de la operación hasta el estado SENT o DELIVERED (debe elegir uno de los dos)

package com.mycie.myapp; 

import java.util.ArrayList; 
import java.util.logging.Level; 

import android.app.Activity; 
import android.app.PendingIntent; 
import android.content.Context; 
import android.content.Intent; 
import android.net.Uri; 
import android.os.SystemClock; 
import android.telephony.SmsManager; 
import android.widget.Toast; 

extern class Sequencer; 
extern class Test; 
extern class Status; 

/** 
* Sends multipart SMS to multiple destinations and verifies the send and 
* deliver status of each part sent away. The operation is a success if all the 
* codes of the SMS_SENT of all the messages sent are equal to 
* {@link Activity#RESULT_OK}. 
*/ 
public class SendSms extends ModemDependentAtom { 

    private static final String LOG_TAG = "SendSms"; 

    // set this flag to true to print many traces to follow the machine state. 
    // NB: DEBUG level traces are always printed for the SEND and DELIVER status 
    // of the part, so you don't need to activate this flag to get them. 
    private static final boolean DEBUG = false; 

    public static final String SMS_DELIVERY = "com.mycie.myapp.SMS_DELIVERY"; 

    public static final String SMS_SENT = "com.mycie.myapp.SMS_SENT"; 

    // infinite time-out 
    public static final int TIMEOUT_INFINITE = -1; 

    // timeout per part. 
    private static final int DEFAULT_TIMEOUT = 10000; 

    private String mSmsNumber; 

    private String mSmsBody; 

    private int mTimeout; 

    private long mOrgRealtime; 

    private SendSmsReceiver mSendReceiver; 

    private SendSmsReceiver mDeliverReceiver; 

    private int mNbParts; 

    private int mDestIndex; 

    private int mState; 

    private String[] mNumbers; 

    private ArrayList<String> mParts; 

    private Status mStatus; 

    public enum SmsSendNotification { 
     SEND, 
     DELIVER 
    } 

    private final SmsSendNotification mSignificantNotification; 

    private ArrayList<PendingIntent> mSentIntents; 

    private ArrayList<PendingIntent> mDeliveryIntents; 

    public SendSms(SmsSendNotification significant) { 
     mSignificantNotification = significant; 

     mSmsNumber = ""; 
     mSmsBody = ""; 
     mTimeout = DEFAULT_TIMEOUT; 
     mOrgRealtime = 0; 
     mStatus = Status.NotDone; 
     mState = 0; 

     mSentIntents = new ArrayList<PendingIntent>(); 
     mDeliveryIntents = new ArrayList<PendingIntent>(); 
    } 

    private final class SendCallBack implements SendSmsReceiver.Callback { 

     SendCallBack(Sequencer sequencer, Test test) { 
      mSequencer = sequencer; 
      mTest = test; 
     } 

     private Sequencer mSequencer; 
     private Test mTest; 

     @Override 
     public void onPartResult(int part, int code) { 

      Log.d(LOG_TAG, String.format("SEND part %d: %s", part, 
        SendSmsReceiver.getCodeString(code))); 

      Context context = Controller.controller.getApplication().getApplicationContext(); 

      if (mSendReceiver.findCode(SendSmsReceiver.RESULT_UNDEFINED) == -1) { 
       // no more sent notification to receive. 
       int globalCode = mSendReceiver.findNotCode(Activity.RESULT_OK); 

       if (globalCode == -1) { 
        // all part were sent successfully. 
        globalCode = Activity.RESULT_OK; 
       } else { 
        // at least one part failed to be sent. 
        mSequencer.onTestEvent(mTest, Level.SEVERE, Sequencer.TEST_EXCEPTION, 
          "one or more parts failed to be sent"); //$NON-NLS-1$ 

        mTest.setStatus(Status.Error); 
        mStatus = Status.Executed; 
       } 

       Log.d(LOG_TAG, "SMS send result: " + SendSmsReceiver.getCodeString(globalCode)); 

       Toast.makeText(context, 
         "SEND: " + SendSmsReceiver.getCodeString(globalCode), 
         Toast.LENGTH_LONG).show(); 
      } 
     } 
    }; 

    private final class DeliverCallBack implements SendSmsReceiver.Callback { 

     DeliverCallBack(Sequencer sequencer, Test test) { 
      mSequencer = sequencer; 
      mTest = test; 
     } 

     private Sequencer mSequencer; 
     private Test mTest; 

     @Override 
     public void onPartResult(int part, int code) { 

      Log.d(LOG_TAG, String.format("DELIVER part %d: %s", part, 
        SendSmsReceiver.getCodeString(code))); 

      if (mDeliverReceiver.findCode(SendSmsReceiver.RESULT_UNDEFINED) == -1) { 
       // no more delivery notification to receive. 
       int globalCode = mDeliverReceiver.findNotCode(Activity.RESULT_OK); 

       if (globalCode == -1) { 
        // all part were sent successfully. 
        globalCode = Activity.RESULT_OK; 
       } else { 
        // at least one part failed to be sent. 
        mSequencer.onTestEvent(mTest, Level.SEVERE, Sequencer.TEST_EXCEPTION, 
          "one or more parts failed to be delivered"); //$NON-NLS-1$ 

        mTest.setStatus(Status.Error); 
        mStatus = Status.Executed; 
       } 

       Context context = Controller.controller.getApplication().getApplicationContext(); 

       Log.d(LOG_TAG, "SMS deliver result: " + SendSmsReceiver.getCodeString(globalCode)); 

       Toast.makeText(context, 
         "DELIVER: " + SendSmsReceiver.getCodeString(globalCode), 
         Toast.LENGTH_LONG).show(); 

       // we can early examine the send results so do it. 
       examine(mSequencer, mTest); 
      } 
     } 
    }; 

    @Override 
    public Status process(Sequencer sequencer, Test test) { 

     if (mState == 0) { 

      mNumbers = PhoneUtils.splitNumbers(mSmsNumber); 

      PhoneUtils.parsePhoneNumbers(mNumbers, new PhoneUtils.PrivilegiateSms()); 

      AppData data = Controller.controller.getData(); 
      SmsManager smsManager = data.services.getSmsManager(); 
      mParts = smsManager.divideMessage(mSmsBody); 
      mNbParts = mParts.size(); 

      Context context = Controller.controller.getApplication().getApplicationContext(); 
      mSendReceiver = SendSmsReceiver.getInstance(context, 
        sequencer.getPosition(), SMS_SENT, new SendCallBack(sequencer, test)); 
      mDeliverReceiver = SendSmsReceiver.getInstance(context, 
        sequencer.getPosition(), SMS_DELIVERY, new DeliverCallBack(sequencer, test)); 

      if (DEBUG) { 
       Log.d(LOG_TAG, "broadcast receivers created."); 
      } 

      mDestIndex = 0; 
      if (DEBUG) { 
       Log.d(LOG_TAG, "state=1"); 
      } 
      mState = 1; 

      process(sequencer, test); 

      mStatus = Status.Active; 

     } else if (mState == 1) { 

      if (mDestIndex < mNumbers.length) { 
       Log.i(LOG_TAG, "Sending SMS to " + mNumbers[mDestIndex] + "..."); 
       Status result = sendMultipartText(sequencer, test, mNumbers[mDestIndex]); 
       if (result == Status.Error) { 
        // error: fail to send to a receiver. 
        test.setStatus(Status.Error); 

        mStatus = Status.Executed; 
       } else { 
        if (DEBUG) { 
         Log.d(LOG_TAG, "state=2"); 
        } 
        mState = 2; 
       } 
      } else { 
       // no more: completed. 
       if (DEBUG) { 
        Log.d(LOG_TAG, "No more recipient. Test OK."); 
       } 

       mStatus = Status.Executed; 
      } 

     } else if (mState == 2) { 
      if (mModemReset) { 

       sequencer.onTestEvent(test, Level.SEVERE, Sequencer.EXTERNAL_ERROR, 
         "the test probably caused a modem reset"); //$NON-NLS-1$ 

       test.setStatus(Status.Error); 
       mStatus = Status.Error; 

       mModemReset = false; 
      } 
      // wait for status to change. Force examination if timeout expires. 
      else if (isTimerExpired()) { 
       if (DEBUG) { 
        Log.d(LOG_TAG, "timeout"); 
       } 
       examine(sequencer, test); 
      } 
     } else { 
      // what? 
     } 

     if (mStatus != Status.Active) { 
      if (DEBUG) { 
       Log.d(LOG_TAG, "status = " + mStatus); 
      } 
     } else { 
      sequencer.wakeAfter(0); 
     } 

     return mStatus; 
    } 

    @Override 
    public void onFinalRelease(Status unused) { 
     super.onFinalRelease(unused); 

     if (DEBUG) { 
      Log.d(LOG_TAG, "broadcast receivers released."); 
     } 

     cancelPendingIntents(); 

     if (mSendReceiver != null) { 
      mSendReceiver.discard(); 
      mSendReceiver = null; 
     } 

     if (mDeliverReceiver != null) { 
      mDeliverReceiver.discard(); 
      mDeliverReceiver = null; 
     } 
    } 

    @Override 
    public void reset() { 
     super.reset(); 

     mState = 0; 
     if (DEBUG) { 
      Log.d(LOG_TAG, "state=0"); 
     } 

     mStatus = Status.NotDone; 
    } 

    public void examine(Sequencer sequencer, Test test) { 

     // If a SENT result is missing then the test failed due to timeout. The 
     // method should have been called by the timeout handler, if not this is 
     // a programming error. 
     if (mSendReceiver.findCode(SendSmsReceiver.RESULT_UNDEFINED) != -1) { 

      sequencer.onTestEvent(test, Level.SEVERE, Sequencer.TEST_EXCEPTION, 
        "not all send results received before timeout"); //$NON-NLS-1$ 

      test.setStatus(Status.Error); 
      mStatus = Status.Executed; 
     } 

     // If one or more SENT failed then the test failed. 
     else if (mSendReceiver.findNotCode(Activity.RESULT_OK) != -1) { 

      sequencer.onTestEvent(test, Level.SEVERE, Sequencer.TEST_EXCEPTION, 
        "failed to send at least one SMS part."); //$NON-NLS-1$ 

      test.setStatus(Status.Error); 
      mStatus = Status.Executed; 
     } 

     // Otherwise, if DELIVER is the significant notification then the 
     // test fails if there is one or more delivery failed. 
     else if (mSignificantNotification.equals(SmsSendNotification.DELIVER)) { 
      if (mDeliverReceiver.findCode(SendSmsReceiver.RESULT_UNDEFINED) != -1) { 

       // failed due to timeout on DELIVERY 
       sequencer.onTestEvent(test, Level.SEVERE, Sequencer.TEST_EXCEPTION, 
         "not all delivery results received before timeout"); //$NON-NLS-1$ 

       test.setStatus(Status.Error); 
       mStatus = Status.Executed; 

      } else if (mDeliverReceiver.findNotCode(Activity.RESULT_OK) != -1) { 

       // at least on part delivery failed. 
       sequencer.onTestEvent(test, Level.SEVERE, Sequencer.TEST_EXCEPTION, 
         "failed to deliver at least one SMS part."); //$NON-NLS-1$ 

       test.setStatus(Status.Error); 
       mStatus = Status.Executed; 

      } else { 
       // OK, proceed with next number. 
       if (DEBUG) { 
        Log.d(LOG_TAG, "send to next recipient, if any."); 
       } 
       mDestIndex ++; 
       mState = 1; 
      } 
     } else { 
      // SEND is significant and all SENT were OK: proceed with next number. 
      if (DEBUG) { 
       Log.d(LOG_TAG, "send to next receiver, if any."); 
      } 
      mDestIndex ++; 
      mState = 1; 
     } 
    } 

    private Status sendMultipartText(Sequencer sequencer, Test test, 
      String number) { 

     Intent sent; 
     Intent delivery; 
     PendingIntent sentIntent; 
     PendingIntent deliveryIntent; 

     AppData data = Controller.controller.getData(); 
     SmsManager smsManager = data.services.getSmsManager(); 
     Context context = Controller.controller.getApplication().getApplicationContext(); 

     if (mNbParts > 1) { 
      Log.i(LOG_TAG, String.format("SMS is devided in %d parts.", mNbParts)); 
     } 

     cancelPendingIntents(); 

     String serial = getNewSerial(number); 

     int iPart; 
     for (iPart = 0; iPart < mNbParts; iPart++) { 
      Uri uri = new Uri.Builder() 
       .scheme(String.format("pos%d", sequencer.getPosition())) 
       .authority(serial) 
       .appendQueryParameter("part", Integer.toString(iPart)) 
       .build(); 
      sent = new Intent(SMS_SENT, uri); 

      sentIntent = PendingIntent.getBroadcast(context, 0, sent, 0); 
      if (sentIntent == null) { 
       sequencer.onTestEvent(test, Level.SEVERE, Sequencer.PROGRAM_ERROR, 
         "null " + SMS_SENT + " intent"); //$NON-NLS-1$ 
       return Status.Error; 
      } 

      mSentIntents.add(sentIntent); 

      delivery = new Intent(SMS_DELIVERY, uri); 

      deliveryIntent = PendingIntent.getBroadcast(context, 0, delivery, 0); 
      if (deliveryIntent == null) { 
       sequencer.onTestEvent(test, Level.SEVERE, Sequencer.PROGRAM_ERROR, 
         "null " + SMS_DELIVERY + " intent"); 
       return Status.Error; 
      } 

      mDeliveryIntents.add(deliveryIntent); 
     } 

     this.mOrgRealtime = SystemClock.elapsedRealtime(); 
     if (DEBUG) { 
      Log.d(LOG_TAG, "timeout: " + getRealTimeout()); 
     } 

     mSendReceiver.restart(serial, mNbParts); 
     mDeliverReceiver.restart(serial, mNbParts); 

     smsManager.sendMultipartTextMessage(number, null, 
       mParts, mSentIntents, mDeliveryIntents); 

     return Status.Executed; 
    } 

    private static String getNewSerial(String number) { 
     String serial = number + "." + SystemClock.uptimeMillis(); 
     return serial; 
    } 

    private void cancelPendingIntents() { 
     // cancel precedent pending intents. 
     for (PendingIntent pi : mSentIntents) { 
      pi.cancel(); 
     } 
     mSentIntents.clear(); 

     for (PendingIntent pi : mDeliveryIntents) { 
      pi.cancel(); 
     } 
     mDeliveryIntents.clear(); 
    } 

    private boolean isTimerExpired() { 
     boolean expired = false; 

     long timeout = getRealTimeout(); 
     if (timeout > 0) { 
      if (mOrgRealtime <= 0) { 
       mOrgRealtime = SystemClock.elapsedRealtime(); 
      } else { 
       long elapsed = SystemClock.elapsedRealtime() - mOrgRealtime; 
       if (elapsed > timeout) { 
        expired = true; 
       } 
      } 
     } 

     return expired; 
    } 

    private long getRealTimeout() { 
     long timeout; 

     if (mTimeout > 0) { 
      if (mSignificantNotification.equals(SmsSendNotification.SEND)) { 
       timeout = mTimeout * mNbParts; 
      } else { 
       // double the times if DELIVER is the significant status. 
       timeout = 2 * mTimeout * mNbParts; 
      } 
     } else { 
      timeout = TIMEOUT_INFINITE; 
     } 

     return timeout; 
    } 

    public void setTimeout(int timeout) { 
     mTimeout = timeout; 
    } 

    public void setNumber(final String number) { 
     mSmsNumber = PhoneUtils.normalizeList(number); 
    } 

    public String getNumber() { 
     return mSmsNumber; 
    } 

    public void setBody(final String body) { 
     mSmsBody = body; 
    } 

    public String getBody() { 
     return mSmsBody; 
    } 

    /** 
    * Send a text message to one or many receivers and forget about the 
    * notifications. 
    * @param number receiver(s) number(s), separated by a comma if more than one. 
    * @param text to send 
    */ 
    public static void sendMessage(String number, String text) { 
     AppData data = Controller.controller.getData(); 
     SmsManager smsManager = data.services.getSmsManager(); 

     ArrayList<String> parts = smsManager.divideMessage(text); 
     Log.d(LOG_TAG, String.format("sending SMS composed of %d parts.", parts.size())); //$NON-NLS-1$ 

     String[] array = PhoneUtils.splitNumbers(number); 
     for (String n : array) { 
      smsManager.sendMultipartTextMessage(n, null, parts, null, null); 
     } 
    } 

} 
Cuestiones relacionadas