在Android中定制呼入/呼出电话屏幕

11

我正在尝试实现自定义的呼入/呼出屏幕。以下是我尝试的内容,遇到了两个问题:

  1. 有时它会调用我手机上的默认呼入屏幕,有时它会调用自定义屏幕。我希望手机总是调用自定义屏幕。

  2. 我无法发起呼叫。自定义屏幕弹出来,但不会拨打任何电话。

我不确定如何解决这个问题。如果有人能帮我解决这个问题,那就太好了。

以下是我正在尝试的内容:

清单:

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

<application
    android:allowBackup="true"
    android:icon="@drawable/ic_launcher"
    android:label="@string/app_name"
    android:theme="@android:style/Theme.NoTitleBar" >
    <activity
        android:name="com.honey.ringer.MainActivity"
        android:label="@string/app_name" >
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
    <activity
        android:name="com.honey.ringer.AcceptCall"
        android:screenOrientation="portrait"
        android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen" >
        <intent-filter>
            <action android:name="android.intent.action.ANSWER" />
            <category android:name="android.intent.category.DEFAULT" />
        </intent-filter>
    </activity>

    <receiver
        android:name="com.honey.ringer.PhoneListenerBroad">
        <intent-filter>
            <action android:name="android.intent.action.PHONE_STATE" />
            <action android:name="android.intent.action.NEW_OUTGOING_CALL" />
        </intent-filter>
    </receiver>
</application>

BroadcastReceiver:这个类既可以接收广播也可以发送广播。

public class PhoneListenerBroad extends BroadcastReceiver
{

Context c;
private String outgoing;

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

    if (intent.getAction().equals("android.intent.action.NEW_OUTGOING_CALL")) 
    {
        outgoing = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER); 
        int state = 2;
        Intent intentPhoneCall = new Intent(c, AcceptCall.class);
        intentPhoneCall.putExtra("incomingnumber", outgoing);
        intentPhoneCall.putExtra("state", state);
        intentPhoneCall.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        c.startActivity(intentPhoneCall);
    }

    try
    {
        TelephonyManager tmgr = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
        MyPhoneStateListener PhoneListener = new MyPhoneStateListener();
        tmgr.listen(PhoneListener, PhoneStateListener.LISTEN_CALL_STATE);
    }
    catch (Exception e) 
    {
        Log.e("Phone Receive Error", " " + e);
    }

}

private class MyPhoneStateListener extends PhoneStateListener
{
    public void onCallStateChanged(final int state, final String incomingNumber) 
    {
        Handler callActionHandler = new Handler();
        Runnable runRingingActivity = new Runnable() 
        {
            @Override
            public void run() 
            {
                if (state == 1)
                {
                    Intent intentPhoneCall = new Intent(c, AcceptCall.class);
                    intentPhoneCall.putExtra("incomingnumber", incomingNumber);
                    intentPhoneCall.putExtra("state", state);
                    intentPhoneCall.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                    c.startActivity(intentPhoneCall);
                }
            }
        };

        if (state == 1)
        {   
            callActionHandler.postDelayed(runRingingActivity, 100);
        }

        if (state == 0) 
        {
            callActionHandler.removeCallbacks(runRingingActivity);
        }
    }
}

}

AcceptCall.java(用于UI目的-呼入和呼出):

 @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
 public class AcceptCall extends Activity implements OnClickListener 
 {
LinearLayout answerButton;
LinearLayout rejectButton;
LinearLayout timerLayout;

TextView contactName;
TextView contactNumber;
ImageView profile;

private String incomingnumber;
private int state;

String name = null;
String contactId = null;

InputStream photo_stream;

TextView callType;



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

    answerButton = (LinearLayout) findViewById(R.id.callReceive);
    answerButton.setOnClickListener(this);

    rejectButton = (LinearLayout) findViewById(R.id.callReject);
    rejectButton.setOnClickListener(this);

    timerLayout = (LinearLayout) findViewById(R.id.timerLayout);

    contactName = (TextView) findViewById(R.id.contactName);
    contactNumber = (TextView) findViewById(R.id.contactNumber);

    callType = (TextView) findViewById(R.id.callType);

    timerValue = (TextView) findViewById(R.id.timerValue);

    profile  = (ImageView)findViewById(R.id.contactPhoto);     

    Bundle bundle =  getIntent().getExtras();
    if(bundle != null)
    {
        incomingnumber = bundle.getString("incomingnumber");
        state = bundle.getInt("state");
    }

    contactslookup(incomingnumber);

    contactName.setText(name);
    contactNumber.setText(incomingnumber);


    if (state == 2)
    {
        /*String uri = "tel:" + incomingnumber.trim();
        Intent intent = new Intent(Intent.ACTION_CALL);
        intent.setData(Uri.parse(uri));
        startActivity(intent);*/
    }

    PhoneStateListener phoneStateListener = new PhoneStateListener() 
    {
        @Override
        public void onCallStateChanged(int state, String incomingNumber) 
        {
            //wen ringing
            if (state == TelephonyManager.CALL_STATE_RINGING)
            {
                Log.e("CALL_STATE_RINGING","CALL_STATE_RINGING");
            } 

            //after call cut
            else if(state == TelephonyManager.CALL_STATE_IDLE)
            {
                RejectCall();
            } 

            //wen speaking / outgoing call
            else if(state == TelephonyManager.CALL_STATE_OFFHOOK)
            {
                Log.e("CALL_STATE_OFFHOOK","CALL_STATE_OFFHOOK");
            }
            super.onCallStateChanged(state, incomingNumber);
        }
    };
    TelephonyManager mgr = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
    if(mgr != null) 
    {
        mgr.listen(phoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
    }
}


private void contactslookup(String number) 
{

    Log.v("ffnet", "Started uploadcontactphoto...");

    //InputStream input = null;

    // define the columns I want the query to return
    String[] projection = new String[] {ContactsContract.PhoneLookup.DISPLAY_NAME,ContactsContract.PhoneLookup._ID};

    // encode the phone number and build the filter URI
    Uri contactUri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(number));

    // query time
    Cursor cursor = getContentResolver().query(contactUri, projection, null, null, null);

    if (cursor.moveToFirst()) 
    {
        // Get values from contacts database:
        contactId = cursor.getString(cursor.getColumnIndex(ContactsContract.PhoneLookup._ID));
        name =      cursor.getString(cursor.getColumnIndex(ContactsContract.PhoneLookup.DISPLAY_NAME));
    } 

    else 
    {
        return; // contact not found
    }


    int currentapiVersion = android.os.Build.VERSION.SDK_INT;
    if (currentapiVersion >= 14)
    {
        Uri my_contact_Uri = ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, Long.parseLong(contactId));
        photo_stream = ContactsContract.Contacts.openContactPhotoInputStream(getContentResolver(), my_contact_Uri, true);
    }
    else
    {
        Uri my_contact_Uri = ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, Long.parseLong(contactId));
        photo_stream = ContactsContract.Contacts.openContactPhotoInputStream(getContentResolver(), my_contact_Uri);
    }

    if(photo_stream != null) 
    {
        BufferedInputStream buf =new BufferedInputStream(photo_stream);
        Bitmap my_btmp = BitmapFactory.decodeStream(buf);
        profile.setImageBitmap(my_btmp);
    }
    else
    {
        profile.setImageResource(R.drawable.contactpic);
    }

    cursor.close();
}

@Override
public boolean onCreateOptionsMenu(Menu menu) 
{
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.main, menu);
    return true;
}

@Override
public void onClick(View v) 
{
    // TODO Auto-generated method stub
    if(v.getId() == answerButton.getId())
    {
        timerLayout.setVisibility(0);

        startTime = SystemClock.uptimeMillis();
        customHandler.postDelayed(updateTimerThread, 0);

        callType.clearAnimation();

        // Simulate a press of the headset button to pick up the call
        Intent buttonDown = new Intent(Intent.ACTION_MEDIA_BUTTON);     
        buttonDown.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_HEADSETHOOK));
        this.sendOrderedBroadcast(buttonDown, "android.permission.CALL_PRIVILEGED");

        // froyo and beyond trigger on buttonUp instead of buttonDown
        Intent buttonUp = new Intent(Intent.ACTION_MEDIA_BUTTON);       
        buttonUp.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_HEADSETHOOK));
        this.sendOrderedBroadcast(buttonUp, "android.permission.CALL_PRIVILEGED"); 
    }

    if(v.getId() == rejectButton.getId())
    {
        RejectCall();
    }

}

private void RejectCall() 
{
    TelephonyManager telephony = (TelephonyManager)this.getSystemService(Context.TELEPHONY_SERVICE);

    try {
        // Java reflection to gain access to TelephonyManager's
        // ITelephony getter
        Class c = Class.forName(telephony.getClass().getName());
        Method m = c.getDeclaredMethod("getITelephony");
        m.setAccessible(true);
        com.android.internal.telephony.ITelephony telephonyService = (ITelephony) m.invoke(telephony);
        telephonyService.endCall();
        finish();

        timeSwapBuff += timeInMilliseconds;
        customHandler.removeCallbacks(updateTimerThread);
    }
    catch (Exception e) 
    {
        e.printStackTrace();
        Log.e("Error", "FATAL ERROR: could not connect to telephony subsystem");
        Log.e("Error", "Exception object: " + e);
    }
}

private Runnable updateTimerThread = new Runnable() 
{

    public void run() 
    {
        timeInMilliseconds = SystemClock.uptimeMillis() - startTime;
        updatedTime = timeSwapBuff + timeInMilliseconds;
        int secs = (int) (updatedTime / 1000);
        int mins = secs / 60;
        int hours = mins / 60;
        secs = secs % 60;
        int milliseconds = (int) (updatedTime % 1000);
        timerValue.setText(""+ hours + ":" + String.format("%02d", mins) + ":"
                + String.format("%02d", secs));
        customHandler.postDelayed(this, 0);
    }

};

  }

你找到解决办法了吗?我也有类似的问题。 - Basher51
@Basher51 - 你遇到的问题是接收方面还是发送方面? - TheDevMan
我的问题是一个外向性问题。我正在创建一个亭子应用程序,用户可以使用它进行电话呼叫。为此,我想创建一个定制的呼出屏幕,以替换默认的呼出屏幕。 - Basher51
1个回答

6
对于呼出电话:作为一种解决方法,我做了以下操作,现在运行良好。我创建了一个具有清单中所需的所有权限的呼出接收器。
通过使用处理程序延迟调用活动。
像这样:
@Override
public void onReceive(Context context, Intent intent) 
{
    c = context;
    setResultData(null);
    phonenumber = getResultData();
    if (phonenumber == null)
    {
        phonenumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
    }
    setResultData(phonenumber);
    callActionHandler.postDelayed(runRingingActivity, 1000);
}


Handler callActionHandler = new Handler();
Runnable runRingingActivity = new Runnable() 
{
    @Override
    public void run() 
    {

        Intent intentPhoneCall = new Intent(c, OutgoingCallActivity.class);
        intentPhoneCall.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        intentPhoneCall.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        c.startActivity(intentPhoneCall);
    }
};

您可以使用电话号码将其发送到新活动。

如果您有任何问题,请告诉我!


这里是否有事件可以知道呼出电话是否结束/被接听? - ralphgabb

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