如何在安卓系统中拨打电话并在通话结束后返回我的活动页面?

130

我正在启动一个拨打电话的活动,但当我按下“结束通话”按钮时,它不会返回到我的活动。请告诉我如何启动一个可以在按下“结束通话”按钮时返回到我这里的通话活动?以下是我拨打电话的方式:

    String url = "tel:3334444";
    Intent intent = new Intent(Intent.ACTION_CALL, Uri.parse(url));
21个回答

106

使用PhoneStateListener来检测通话何时结束。您很可能需要触发侦听器动作以等待通话开始(等待从PHONE_STATE_OFFHOOK更改为PHONE_STATE_IDLE),然后编写一些代码在IDLE状态下重新启动您的应用程序。

您可能需要在服务中运行侦听器以确保它保持运行并重新启动您的应用程序。以下是一些示例代码:

EndCallListener callListener = new EndCallListener();
TelephonyManager mTM = (TelephonyManager)this.getSystemService(Context.TELEPHONY_SERVICE);
mTM.listen(callListener, PhoneStateListener.LISTEN_CALL_STATE);

监听器定义:

private class EndCallListener extends PhoneStateListener {
    @Override
    public void onCallStateChanged(int state, String incomingNumber) {
        if(TelephonyManager.CALL_STATE_RINGING == state) {
            Log.i(LOG_TAG, "RINGING, number: " + incomingNumber);
        }
        if(TelephonyManager.CALL_STATE_OFFHOOK == state) {
            //wait for phone to go offhook (probably set a boolean flag) so you know your app initiated the call.
            Log.i(LOG_TAG, "OFFHOOK");
        }
        if(TelephonyManager.CALL_STATE_IDLE == state) {
            //when this state occurs, and your flag is set, restart your app
            Log.i(LOG_TAG, "IDLE");
        }
    }
}

在你的Manifest.xml文件中添加以下权限:

<uses-permission android:name="android.permission.READ_PHONE_STATE"/>

10
别忘了授权。 ;) - Gp2mv3
5
正如Gp2mv3所指出的那样,在AndroidManifest.xml文件中别忘了添加READ_PHONE_STATE权限。 - Display name
6
@moonlightcheese,你能否添加代码使我们的应用程序从通话应用程序返回? - Geek
这很危险,因为每次调用时它都会打开您的应用程序活动。 - user924

50

关于Starter提出的问题,我来回答。

你代码中的问题是你没有正确地传递数字。

代码应该是:

private OnClickListener next = new OnClickListener() {

     public void onClick(View v) {
        EditText num=(EditText)findViewById(R.id.EditText01); 
        String number = "tel:" + num.getText().toString().trim();
        Intent callIntent = new Intent(Intent.ACTION_CALL, Uri.parse(number)); 
        startActivity(callIntent);
    }
};

不要忘记在清单文件中添加权限。

<uses-permission android:name="android.permission.CALL_PHONE"></uses-permission>
或者
<uses-permission android:name="android.permission.CALL_PRIVILEGED"></uses-permission>

为了在使用DIAL时紧急联系,请拨打紧急电话。


7
我很难看出你的代码与原问题中的代码有什么不同。 - Ilya Saunkin
代码没有变化。您需要在清单文件中添加权限。 - Pria
4
android.permission.CALL_PRIVILEGED 权限仅授予系统应用程序,而非在应用程序级别可用。 - CoDe
而那是什么?它不会返回到您的活动。 - user924

25
我们曾经遇到同样的问题,通过使用 PhoneStateListener 来识别电话结束并解决了该问题,但我们还需要在使用 startActivity 启动原始活动之前先 finish() 它,否则通话日志将显示在其前面。

5
如果您使用不同的方法,就可以避免这种情况。如果你建立一个观察Android通话日志的ContentObserver,那么应用程序将一直等待,直到通话日志更改为止。我不得不放弃PhoneStateListener,转而使用这个模型,因为我的应用程序需要通话日志数据,而监听器在这些更改发生之前就返回了。http://pastebin.com/bq2s9EVa - moonlightcheese
11
@André:您提供的链接似乎损坏了。 - aggregate1166877
如果我有很多声望,我会给你一百万个(哈哈)!感谢让我今天过得愉快! - keybee
1
链接的问题,我说! - Dheeraj Bhaskar
互联网档案馆来拯救了...http://web.archive.org/web/20120123224619/http://umamao.com/questions/Como-fazer-uma-app-de-Android-chamar-o-discador-e-faz%C3%AA-lo-retornar-%C3%A0-app-ap%C3%B3s-o-t%C3%A9rmino-da-chamada - Basic

13
我发现EndCallListener是最实用的示例,为了获得所描述的行为(finish(),调用,重启),我添加了一些SharedPreferences,以便Listener具有管理此行为的引用。
我的OnClick、初始化和EndCallListener只响应来自应用程序的呼叫。其他呼叫被忽略。
import android.content.Intent;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.util.Log;

public class EndCallListener extends PhoneStateListener {

private String TAG ="EndCallListener";
private int     LAUNCHED = -1;

SharedPreferences prefs = PreferenceManager
                            .getDefaultSharedPreferences(
                                myActivity.mApp.getBaseContext());

SharedPreferences.Editor _ed = prefs.edit();

@Override
    public void onCallStateChanged(int state, String incomingNumber) {
    String _prefKey = myActivity.mApp                          
                      .getResources().getString(R.string.last_phone_call_state_key),
    _bPartyNumber = myActivity.mApp                           
                      .getResources().getString(R.string.last_phone_call_bparty_key);

    int mLastCallState = prefs.getInt(_prefKey, LAUNCHED);

    //Save current call sate for next call
    _ed.putInt(_prefKey,state);
    _ed.commit();

        if(TelephonyManager.CALL_STATE_RINGING == state) {
            Log.i(TAG, " >> RINGING, number: " + incomingNumber);
        }
        if(TelephonyManager.CALL_STATE_IDLE == state && mLastCallState != LAUNCHED ) {
            //when this state occurs, and your flag is set, restart your app

            if (incomingNumber.equals(_bPartyNumber) == true) {
                //Call relates to last app initiated call
            Intent  _startMyActivity =  
               myActivity.mApp                               
               .getPackageManager()                                  
               .getLaunchIntentForPackage(
                 myActivity.mApp.getResources()
                 .getString(R.string.figjam_package_path));

_startMyActivity.setAction(                                     
        myActivity.mApp.getResources()
        .getString(R.string.main_show_phone_call_list));

                myActivity.mApp
                        .startActivity(_startMyActivity);
                Log.i(TAG, "IDLE >> Starting MyActivity with intent");
            }
            else
                Log.i(TAG, "IDLE after calling "+incomingNumber);

        }

    }
}

将这些添加到strings.xml文件中

<string name="main_show_phone_call_list">android.intent.action.SHOW_PHONE_CALL_LIST</string>
<string name="last_phone_call_state_key">activityLpcsKey</string>
<string name="last_phone_call_bparty_key">activityLpbpKey</string>

如果您需要在调用后返回到外观和感觉之前,请在清单中添加类似以下内容的代码

  <activity android:label="@string/app_name" android:name="com.myPackage.myActivity" 
      android:windowSoftInputMode="stateHidden"
        android:configChanges="keyboardHidden" >
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <action android:name="android.intent.action.SHOW_PHONE_CALL_LIST" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
  </activity>

然后将它们放入您的'myActivity'中

public static Activity mApp=null; //Before onCreate()
  ...
onCreate( ... ) {
  ...
if (mApp == null) mApp = this; //Links your resources to other classes
  ...
    //Test if we've been called to show phone call list
    Intent _outcome = getIntent();
    String _phoneCallAction = mApp.getResources().getString(R.string.main_show_phone_call_list);
    String _reqAction = _outcome.getAction();//Can be null when no intent involved

         //Decide if we return to the Phone Call List view
         if (_reqAction != null &&_reqAction.equals(_phoneCallAction) == true) {
                         //DO something to return to look and feel
         }

  ...
        myListView.setOnItemClickListener(new OnItemClickListener() { //Act on item when selected
             @Override
             public void onItemClick(AdapterView<?> a, View v, int position, long id) {

                 myListView.moveToPosition(position);
                 String _bPartyNumber = "tel:"+myListView.getString(myListView.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)); 

                 //Provide an initial state for the listener to access.
                 initialiseCallStatePreferences(_bPartyNumber);

                 //Setup the listener so we can restart myActivity
                    EndCallListener _callListener = new EndCallListener();
                    TelephonyManager _TM = (TelephonyManager)mApp.getSystemService(Context.TELEPHONY_SERVICE);

                    _TM.listen(_callListener, PhoneStateListener.LISTEN_CALL_STATE);

                         Intent _makeCall = new Intent(Intent.ACTION_CALL, Uri.parse(_bPartyNumber));

                 _makeCall.setComponent(new ComponentName("com.android.phone","com.android.phone.OutgoingCallBroadcaster"));
                    startActivity(_makeCall);                           
                finish();
              //Wait for call to enter the IDLE state and then we will be recalled by _callListener
              }
        });


}//end of onCreate()

使用此代码在myActivity中初始化您的onClick行为,例如在onCreate()之后。

private void initialiseCallStatePreferences(String _BParty) {
    final int LAUNCHED = -1;
    SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(
                                mApp.getBaseContext());
    SharedPreferences.Editor _ed = prefs.edit();

    String _prefKey = mApp.getString(R.string.last_phone_call_state_key),
           _bPartyKey = mApp.getString(R.string.last_phone_call_bparty_key);

    //Save default call state before next call
        _ed.putInt(_prefKey,LAUNCHED);
        _ed.putString(_bPartyKey,_BParty);
        _ed.commit();

}

您应该发现,点击电话号码列表会完成您的活动,拨打该号码并在通话结束时返回到您的活动。

在您的应用程序仍然存在的情况下从外部进行呼叫不会重新启动您的活动(除非它与上次通话的BParty号码相同)。

:)


5
抱歉,但这段代码看起来有点丑陋。感谢回答。 - Pierre

7

您可以使用startActivityForResult()函数。


1
在使用Android 5.0时,onActivityResult方法会在调用开始时立即被调用! - Panciz

6

这是我的示例,首先用户需要输入他/她想要拨打的号码,然后按下呼叫按钮,并被引导到电话界面。在取消呼叫后,用户会被送回应用程序。为了实现这一点,按钮需要在xml中具有一个onClick方法(在此示例中为 'makePhoneCall')。您还需要在清单中注册权限。

清单文件

<uses-permission android:name="android.permission.CALL_PHONE"></uses-permission>
<uses-permission android:name="android.permission.READ_PHONE_STATE" />

活动

import android.net.Uri;
import android.os.Bundle;
import android.app.Activity;
import android.content.Intent;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;

public class PhoneCall extends Activity {

    EditText phoneTo;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_phone_call);

        phoneTo = (EditText) findViewById(R.id.phoneNumber);

    }
    public void makePhoneCall(View view) {




        try {
            String number = phoneTo.getText().toString();
            Intent phoneIntent = new Intent(Intent.ACTION_CALL);
            phoneIntent.setData(Uri.parse("tel:"+ number));
            startActivity(phoneIntent);


        } catch (android.content.ActivityNotFoundException ex) {
            Toast.makeText(PhoneCall.this,
                    "Call failed, please try again later!", Toast.LENGTH_SHORT).show();
        }
    }

}

XML

 <EditText
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:inputType="phone"
        android:ems="10"
        android:id="@+id/phoneNumber"
        android:layout_marginTop="67dp"
        android:layout_below="@+id/textView"
        android:layout_centerHorizontal="true" />

    <Button
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="Call"
        android:id="@+id/makePhoneCall"
        android:onClick="makePhoneCall"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true" />

你根本不知道自己在说什么。 READ_PHONE_STATE - 你在示例代码中根本没有使用它,为什么要添加它?当然,如果你按下取消按钮,它会把你带回到应用程序活动界面,但问题的提问者是想知道如何在电话接通后返回到活动界面。 - user924

6
@Override
public void onClick(View view) {
    Intent phoneIntent = new Intent(Intent.ACTION_CALL);
    phoneIntent.setData(Uri.parse("tel:91-000-000-0000"));
    if (ActivityCompat.checkSelfPermission(mContext, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
        return;
    }
    startActivity(phoneIntent);
}

6

这是我个人的解决方案:

ok.setOnClickListener(this);
@Override
public void onClick(View view) {
    if(view == ok){
        Intent intent = new Intent(Intent.ACTION_CALL);
        intent.setData(Uri.parse("tel:" + num));
        activity.startActivity(intent);

    }

当然,在Activity(类)定义中,您必须实现View.OnClickListener。

5
如果您要使用监听器,还需要在清单文件中添加此权限。
<uses-permission android:name="android.permission.READ_PHONE_STATE" />

3

在 PhoneStateListener 内,在检测到通话结束后,最好使用以下操作:

Intent intent = new Intent(CallDispatcherActivity.this, CallDispatcherActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);

CallDispatcherActivity是用户启动呼叫的活动(在我的情况下,是出租车服务调度员)。这只是将Android电话应用程序从顶部移除,用户会回到一个更美观的界面,而不是看到丑陋的代码。


1
在电话通话完成后,别忘了移除监听器,就像这样:((TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE)).listen(this, LISTEN_NONE); - Dmitri Novikov
我尝试使用您的方法,但活动(称为ControlPanel)未重新激活。 显示仍然显示手机拨号界面,并且ControlPanel中的onResume和onNewIntent入口点中的记录器完全沉默。 这是意图:'Intent intentRestart = new Intent(ControlPanel.this,ControlPanel.class);'。 我应该指出,PhoneStateListener也在ControlPanel中。 也就是说,我的目标是将UI恢复到启动电话呼叫之前的状态。 有什么建议吗? - PeteH
尝试在PhoneStateListener实现中进行登录。 - Dmitri Novikov

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