循环时处理动作NEW_OUTGOING_CALL的正确方法是什么?

时间:2010-07-14 05:42:00

标签: android

目前我正在开发一个应用程序,该应用程序在NEW_OUTGOING_CALL的帮助下捕获操作BroadcastReceiver。我通过拨打setResultData(null)来中止通话。之后,我向用户显示一个对话框,允许他决定是否要使用我的应用程序重写其编号。当用户决定发生时,我将根据决定发出新呼叫。现在我的广播接收器再次被召唤。

了解我已经处理过该号码的正确方法是什么?我得到了一个工作解决方案,它使用时间戳来猜测它是否已经被处理过。另一种解决方案是在处理后的号码末尾添加"+"。 这些方法适用于我的应用程序是唯一一个捕获NEW_OUTGOING_CALL事件的方法。但是当其他应用程序(如Sipdroid或Google Voice)也坐在那里时,我应该怎么做才能抓住NEW_OUTGOING_CALL广播中止并重新启动它?我没有看到是否有可能知道我们是否仍在同一个“呼叫流程”并且我已经处理了这个号码。

我很想听听你对这个问题的想法!

4 个答案:

答案 0 :(得分:2)

您正在使用哪种API级别?如果它是> = 11,请检查新的BroadcastReceiver.goAsync函数,该函数允许您在接收器的onReceive函数之外扩展广播的处理。这可以绕过完全循环的需要。

如果像我一样,你在11级之前试图做到这一点,那么优雅地做到这一点是非常棘手的。您可能也已经这样做了,但我尝试在我的代码生成的ACTION_CALL意图中包含一个“已处理”标志作为额外内容,希望它以某种方式包含在生成的ACTION_NEW_OUTGOING_CALL广播中,但遗憾的是这不起作用。

我能找到的最佳解决方案是在您生成的ACTION_CALL意图的URI中包含一个片段。此片段将包含在最终的ACTION_NEW_OUTGOING_CALL广播中,因此您的广播接收器可以区分原始呼叫和您生成的呼叫,但不会干扰不寻找它的处理程序。

这是基本代码。

在你的BroadcastReceiver中为ACTION_NEW_OUTGOING_CALL

public class YourBroadcastReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        // extract the fragment from the URI
        String uriFragment = Uri.parse(
            intent.getStringExtra("android.phone.extra.ORIGINAL_URI")).getFragment();

        // if the fragment is missing or does not have your flag, it is new
        if (uriFragment == null || !uriFragment.contains("your_flag")) {

            // launch your activity, pass the phone number, etc.
            // use getResultData to get the number in order to respect
            // earlier broadcast receivers
            ...

            // abort the broadcast
            this.setResultData(null);
            this.abortBroadcast();
        }
        // otherwise, your code is there, this call was triggered by you
        else {
            // unless you have a special need, you'll probably just let the broadcast
            // go through here

            // note that resultData ignores the fragment, so other receivers should
            // be blissfully unaware of it
        }
    }
}

当用户首次拨打该号码时,该片段将完全丢失或您的旗帜不会出现,因此您将中止广播并开始您的活动。在您的活动中,如果您决定再次拨打电话,请执行以下操作:

startActivity(new Intent(Intent.ACTION_CALL,
                         Uri.parse("tel:" + modified_number + "#your_flag")));

“your_flag”片段将出现在随后的NEW_OUTGOING_CALL广播中,因此您可以在广播接收器中以不同方式处理此情况。

关于这一点的好处是片段被完全忽略,除非你在ORIGINAL_URI中寻找它,所以其他广播接收器可以继续运行。如果你想要非常好,你可能想要寻找一个现有的片段并将它添加到它(可能使用逗号分隔符)。

我希望有所帮助。祝你好运!

答案 1 :(得分:1)

  

我认为不可能到达   知道我们是否还在同一个电话中   流动“如果我已经处理了   号。

从技术上讲,您不在同一个“呼叫流程”中,因为拨打新呼叫是异步的。您必须使用提示(例如时间戳),因为您似乎已经在做了。

如果您确信除了更改前缀或添加后缀之外其他应用程序不会重写该数字,您可能需要添加另一个“接近检查”提示以避免误报/否定,但我担心这是关于你所能做的一切。

答案 2 :(得分:0)

Broadcast接收器中的onReceive()方法接收Intent作为参数。 使用Intent.getExtras()从Intent中提取Bundle。 该Bundle包含3个键值对,如下所示:

  1. android.phone.extra.ALREADY_CALLED = null
  2. android.intent.extra.PHONE_NUMBER = 98xxxxxx98
  3. android.phone.extra.ORIGINAL_URI = tel: 98xxxxxx98
  4. 98xxxxxx98 是用户拨打的号码。

    再次调用onReceive()时,此数字会变为 98xxxxxx98 * 0 * 通过检查所拨号码末尾的星号(*),可以推断出第一次或下次后续调用onReceive()方法。

答案 3 :(得分:0)

其中一个答案是跟踪意图中的布尔额外值。谷歌手机应用程序以类似的方式完成。您可以查看此BroadcastReceiver here(查找已停用的用法)

另一种方式就是将广播中的“重写”号码传递到下一个广播接收器(可以是任何应用,如Sipdroid,Google Voice或自定义VoIP应用),而无需调用ACTION_CALL意图(这这就是为什么你得到循环,你广播接收器再次呼叫)以下代码是我如何在我的自定义VoIP应用程序中处理呼叫的示例。当我在广播接收器中拦截NEW_OUTGOING_CALL时,我首先检查是否有互联网连接。如果手机连接到互联网,我使用我的活动的自定义意图动作来通过我的VoIP应用程序拨打电话。如果没有互联网连接,我只需将原始电话号码设置为广播接收器结果数据。这是由下一个广播接收器(可能是默认电话应用程序,但不一定是)在拨打电话的流程中使用。

public class BHTTalkerCallReceiver extends BroadcastReceiver {

    private static final String TAG = "BHTTalkerCallReceiver";

    @Override
    public void onReceive(Context context, Intent intent) {

        Log.d(TAG, "Broadcast successfull ... ");

        // Extract phone number reformatted by previous receivers
        String phoneNumber = getResultData();
        if (phoneNumber == null) {
            // No reformatted number, use the original
            phoneNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
        }

        if (isNetworkAvailable(context)) { // Make sure this app handles call only if there is internet connection
            // My app will bring up the call, so cancel the broadcast
            setResultData(null);
            // Start my app to bring up the call
            Intent voipCallIntent = new Intent(context, TalkerActivity.class);
            voipCallIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            voipCallIntent.putExtra(TalkerActivity.OUT_CALL_NUMBER, phoneNumber);
            voipCallIntent.setAction(TalkerActivity.BHT_TALKER_OUT_CALL);
            context.startActivity(voipCallIntent);
        } else { //otherwise make a regular call...
            // Forward phone data to standard phone call
            setResultData(phoneNumber);
        }
    }

    private boolean isNetworkAvailable(final Context context) {
        final ConnectivityManager connectivityManager = ((ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE));
        return connectivityManager.getActiveNetworkInfo() != null && connectivityManager.getActiveNetworkInfo().isConnected();
    }
}