通过Intent发送短信并知道是否已发送

9
我尝试使用以下代码通过Intent发送短信:
```java ```
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("smsto:" + phoneNumber));
intent.putExtra("address", phoneNumber);
intent.putExtra("sms_body", messageBody);
intent.putExtra("exit_on_sent", true);
startActivityForResult(intent, CODE);

接下来,我想知道短信是否已经发送,我使用了以下代码:

public void onActivityResult(int requestCode, int resultCode, Intent intent) {

    switch (requestCode) {

        case CODE:
        if (resultCode == Activity.RESULT_OK)
        {
            //Then do...
        }
        elseif(resultCode == Activity.RESULT_CANCELED)
        {
            // Do...
        }
        break;    
    }
}

事实上,结果总是0(Activity.RESULT_CANCELED),即使短信已经发送成功。我如何知道短信是否已发送?我想使用手机上的默认短信应用程序,而不是创建一个发送短信的界面。

是的,实际上你需要一个接收器来监视消息的状态! - Pavlos
http://mobiforge.com/design-development/sms-messaging-android - Pavlos
只有当您的应用程序自己发送短信时,才可以像@Pavlos提供的链接中所示使用BroadcastReceiver。如果您使用Intent启动另一个应用程序来发送消息,则无法使用它。但是,您可以使用“ContentObserver”来实现您想要做的事情。 - Mike M.
是的,那也可能行!不知道原帖作者确切想要什么。 - Pavlos
感谢您的回复。我想使用Intent启动默认的短信应用程序来发送消息,因此ContentObserver可能是解决方案。再次感谢! - jbiral
1个回答

11
在下面的示例中,我们使用一个ContentObserver来监视短信提供程序的更新。在触发SMS Intent之前创建并启动此Observer,并根据目标地址检查Provider更改。创建Observer的Activity必须实现SmsSendObserver.SmsSendListener接口以接收回调。
Observer的构造函数包括一个timeout参数(以毫秒为单位),以允许如果在合理的时间内未发送消息,则正确取消注册Observer。如果需要,可以将其设置为NO_TIMEOUT。但是,按照编写的方式,该类适用于“一次性”使用,它将在回调时取消注册自身并使成员为空。如果没有回调,可以使用stop()方法进行清理。在任一情况下,该实例不再可用,对它的任何引用都应设置为null。
示例Activity:
public class MainActivity extends Activity
    implements SmsSendObserver.SmsSendListener {
    ...

    private void sendMessage(String phoneNumber, String messageBody) {
        // This example has a timeout set to 15 seconds
        new SmsSendObserver(this, phoneNumber, 15000).start();

        Intent intent = new Intent(Intent.ACTION_VIEW);
        intent.setData(Uri.parse("smsto:" + phoneNumber));
        intent.putExtra("address", phoneNumber);
        intent.putExtra("sms_body", messageBody);
        intent.putExtra("exit_on_sent", true);
        startActivity(intent);
    }

    public void onSmsSendEvent(boolean sent) {
        Toast.makeText(this, sent ? "Message was sent" : "Timed out",
                       Toast.LENGTH_SHORT).show();
    }
}

SmsSendObserver类:

public class SmsSendObserver extends ContentObserver {
    public static final int NO_TIMEOUT = -1;

    private static final Handler handler = new Handler();
    private static final Uri uri = Uri.parse("content://sms/"); 

    private static final String COLUMN_ADDRESS = "address";
    private static final String COLUMN_TYPE = "type";
    private static final String[] PROJECTION = { COLUMN_ADDRESS, COLUMN_TYPE };
    private static final int MESSAGE_TYPE_SENT = 2;

    private Context context = null;
    private ContentResolver resolver = null;

    private String phoneNumber = null;
    private long timeout = NO_TIMEOUT;
    private boolean wasSent = false;
    private boolean timedOut = false;

    public SmsSendObserver(Context context, String phoneNumber, long timeout) {
        super(handler);

        if (context instanceof SmsSendListener) {       
            this.context = context;
            this.resolver = context.getContentResolver();
            this.phoneNumber = phoneNumber;
            this.timeout = timeout;
        }
        else {
            throw new IllegalArgumentException(
                "Context must implement SmsSendListener interface");
        }
    }

    private Runnable runOut = new Runnable() {
        @Override
        public void run() {
            if (!wasSent) {
                timedOut = true;
                callBack();
            }
        }
    };

    public void start() {
        if (resolver != null) {
            resolver.registerContentObserver(uri, true, this);

            if (timeout > NO_TIMEOUT) {
                handler.postDelayed(runOut, timeout);
            }
        }
        else {
            throw new IllegalStateException(
                "Current SmsSendObserver instance is invalid");
        }
    }

    public void stop() {
        if (resolver != null) {
            resolver.unregisterContentObserver(this);

            resolver = null;
            context = null;
        }
    }

    private void callBack() {
        ((SmsSendListener) context).onSmsSendEvent(wasSent);
        stop();
    }

    @Override
    public void onChange(boolean selfChange) {
        if (wasSent || timedOut)
            return;

        Cursor cursor = null;

        try {
            cursor = resolver.query(uri, PROJECTION, null, null, null);

            if (cursor != null && cursor.moveToFirst()) {
                final String address =
                    cursor.getString(cursor.getColumnIndex(COLUMN_ADDRESS));
                final int type =
                    cursor.getInt(cursor.getColumnIndex(COLUMN_TYPE));

                if (PhoneNumberUtils.compare(address, phoneNumber) &&
                        type == MESSAGE_TYPE_SENT) {

                    wasSent = true;
                    callBack();
                }
            }
        }
        finally {
            if (cursor != null) {
                cursor.close();
            }
        }
    }

    public interface SmsSendListener {
        // Passes true if the message was sent
        // Passes false if timed out
        public void onSmsSendEvent(boolean sent);
    }
}

我无法在片段中调用它,你能帮我吗? - MilapTank
@Milap 我并不是真正推荐使用这个确切的解决方案。我一直想更新这个,但是我总是忘记。SmsSendObserver类仍然可以如常使用,但很可能启动回调之前发起的Activity实例 - 甚至是托管您的Fragment的实例 - 将被终止。我建议使用一个Service - 可能是前台Service - 来启动观察者并接收回调,并通过其他方式将结果传递给Activity;例如,事件总线,通过持久存储保存/检索结果等。 - Mike M.
2
我认为这段代码在新的短信政策变更方面已经不再可用。https://android-developers.googleblog.com/2019/01/reminder-smscall-log-policy-changes.html - Sinan Dizdarević
1
只要您拥有所需的权限,此代码仍然可以完美使用。如果您在Play商店上分发应用程序,并且无法从Google获得“READ_SMS”权限的豁免,则没有任何解决方案可以完全适用于您;不仅是这一个。然而,谷歌的政策是一个完全独立的问题,并且不应该在这个答案中被考虑或讨论(顺便说一下,这个答案已经超过4年了)。 - Mike M.
我说过,根据新的短信政策,你不能使用这段代码。如果你想在Play商店上发布你的应用,你会被拒绝的。如果你的项目核心是一个默认的短信应用程序,你将能够使用这个权限和一些例外。但是,如果你想开发默认的短信应用程序,你将无法打开另一个短信应用程序的屏幕。此时,要从你的应用程序发送消息,你可以打开默认的短信应用程序并发送参数。我没有找到使用默认应用程序发送消息并获取响应而不使用READ_SMS权限的解决方案。 - Sinan Dizdarević

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接