2

我正在尝试实现连接服务类并希望向用户提供呼叫功能。我不想在通话中使用意图。Intent 将触发默认拨号应用程序,因此,我想制作自己的应用程序并将其设为默认值。

到目前为止,我已经实施了以下操作:

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.example.dialapp">

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

    <!-- Needed only if your calling app reads numbers from the `PHONE_STATE` intent action. -->
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />

    <application
        android:allowBackup="false"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme"
        tools:ignore="GoogleAppIndexingWarning">

        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <service
            android:name=".service.MeriConnectionService"
            android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE">
            <intent-filter>
                <action android:name="android.telecom.ConnectionService" />
            </intent-filter>
        </service>
    </application>

</manifest>

相关代码MainActivity.java

    public void register() {
        TelecomManager manager = (TelecomManager) getSystemService(TELECOM_SERVICE);
        PhoneAccountHandle phoneAccountHandle = new PhoneAccountHandle(
                new ComponentName(getPackageName(), MeriConnectionService.class.getName()),
                MERI_CONNECTION_SERVICE_ID
        );
        PhoneAccount.Builder builder = PhoneAccount.builder(phoneAccountHandle, PHONE_ACCOUNT_LABEL);
        builder.setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER | PhoneAccount.CAPABILITY_CONNECTION_MANAGER);
        PhoneAccount phoneAccount = builder.build();
        manager.registerPhoneAccount(phoneAccount);
    }

    public void call(View view) {
        placeCall(mBinding.phoneNumberEt.getText().toString());
    }

    private void placeCall(String telNumber) {
        TelecomManager telecomManager =
                (TelecomManager) getSystemService(Context.TELECOM_SERVICE);
        try {
            Bundle extras = new Bundle();

            Uri uri = Uri.fromParts("tel",
                    telNumber, null);
            extras.putParcelable(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, uri);

            // Pass your own implementation of ConnectionService class to PhoneAccountHandle object here
            PhoneAccountHandle phoneAccountHandle = new PhoneAccountHandle(
                    new ComponentName(getPackageName(),
                            MeriConnectionService.class.getName()), MERI_CONNECTION_SERVICE_ID);
            // Make sure to set the permission
            extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle);


            // Pass the newly created PhoneAccountHandle to PhoneAccount object
            PhoneAccount.Builder builder = new PhoneAccount.Builder(phoneAccountHandle, MERI_CONNECTION_SERVICE_ID);

            // Make sure to set the capability
            builder.setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER);
            PhoneAccount phoneAccount = builder.build();

            // Register our own PhoneAccount to TelecomManager object
            telecomManager.registerPhoneAccount(phoneAccount);

            // This to show a prompt to the user to enable our own PhoneAccount because it is disabled by default
            // There might be better way of this programmatically without the need of user interaction
            Intent intent = new Intent();
            intent.setComponent(new ComponentName("com.android.server.telecom", "com.android.server.telecom.settings.EnableAccountPreferenceActivity"));
            intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
            this.startActivity(intent);

            // This is just for testing
            if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED) {
                List<PhoneAccountHandle> lstPA = telecomManager.getCallCapablePhoneAccounts();
                int accoutSum = lstPA.size();
                for (int i = 0; i < accoutSum; i++)
                    Log.d("MainActivity", "accountSum: " + accoutSum + lstPA.get(i));
                for (PhoneAccountHandle account : lstPA) {
                    if (account.getComponentName().getClassName().equals(MeriConnectionService.class.getCanonicalName())) {
                        Log.d("Main Activity", account.getComponentName().getClassName());
                        break;
                    }
                }
            }

            // Finally, make the call. This will bind our ConnectionService with TelecomManager.
            // This will use the default dialer app user interface but using our own implementation of managing the call.
            // By default, the system uses TelephonyConnectionService class
            telecomManager.placeCall(uri, extras);
        } catch (SecurityException se) {
            Log.d("TESTAPP", se.getMessage());
        }
    }
}

现在,首先我使用以下代码尝试了电话的虚拟实现:

package com.example.dialapp.service;

import android.content.Context;
import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.telecom.Connection;
import android.telecom.ConnectionRequest;
import android.telecom.ConnectionService;
import android.telecom.DisconnectCause;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
import android.util.Log;
import android.widget.Toast;

import com.example.dialapp.R;

import static android.media.AudioAttributes.CONTENT_TYPE_SPEECH;
import static android.media.AudioAttributes.USAGE_VOICE_COMMUNICATION;

public class MeriConnectionService extends ConnectionService {

    private static final String LOG_TAG = MeriConnectionService.class.getSimpleName();
    private static MeriConnectionService INSTANCE;
    private final Handler mHandler = new Handler();
    /**
     * Used to play an audio tone during a call.
     */
    private MediaPlayer mMediaPlayer;

    public static MeriConnectionService getInstance() {
        return INSTANCE;
    }

    @Override
    public void onCreate() {
        INSTANCE = this;
    }

    @Override
    public Connection onCreateOutgoingConnection(
            PhoneAccountHandle connectionManagerAccount,
            final ConnectionRequest originalRequest) {

        final Uri handle = originalRequest.getAddress();
        String number = originalRequest.getAddress().getSchemeSpecificPart();
        Log.d(LOG_TAG, "call, number: " + number);

        // Crash on 555-DEAD to test call service crashing.
        if ("5550340".equals(number)) {
            throw new RuntimeException("Goodbye, cruel world.");
        }

        Bundle extras = originalRequest.getExtras();
        String gatewayPackage = extras.getString(TelecomManager.GATEWAY_PROVIDER_PACKAGE);
        Uri originalHandle = extras.getParcelable(TelecomManager.GATEWAY_ORIGINAL_ADDRESS);

        if (extras.containsKey(TelecomManager.EXTRA_CALL_SUBJECT)) {
            String callSubject = extras.getString(TelecomManager.EXTRA_CALL_SUBJECT);
            Log.d(LOG_TAG, "Got subject: " + callSubject);
            Toast.makeText(getApplicationContext(), "Got subject :" + callSubject,
                    Toast.LENGTH_SHORT).show();
        }

        Log.d(LOG_TAG, "gateway package [" + gatewayPackage + "], original handle [" +
                originalHandle + "]");

        final TestConnection connection =
                new TestConnection(false /* isIncoming */, originalRequest);

        // Have a special phone number to test the account-picker dialog flow.
        if (number != null && number.contentEquals("09085686814")) {

            connection.setDialing();
            connection.startOutgoing();

            for (Connection c : getAllConnections()) {
                c.setOnHold();
            }
        } else {
            Log.d(LOG_TAG, "Not a test number");
        }
        return connection;
    }

    private void activateCall(TestConnection connection) {
        if (mMediaPlayer == null) {
            mMediaPlayer = createMediaPlayer();
        }
        if (!mMediaPlayer.isPlaying()) {
            mMediaPlayer.start();
        }
    }

    private MediaPlayer createMediaPlayer() {
        AudioAttributes attributes = new AudioAttributes.Builder()
                .setUsage(USAGE_VOICE_COMMUNICATION)
                .setContentType(CONTENT_TYPE_SPEECH)
                .build();

        final int audioSessionId = ((AudioManager) getSystemService(
                Context.AUDIO_SERVICE)).generateAudioSessionId();
        // Prepare the media player to play a tone when there is a call.
        MediaPlayer mediaPlayer = MediaPlayer.create(getApplicationContext(), R.raw.ring, attributes,
                audioSessionId);
        mediaPlayer.setLooping(true);
        return mediaPlayer;
    }

    private void destroyCall(TestConnection connection) {
        // Ensure any playing media and camera resources are released.
        //connection.stopAndCleanupMedia();

        // Stops audio if there are no more calls.
        if (mMediaPlayer != null && mMediaPlayer.isPlaying()) {
            mMediaPlayer.stop();
            mMediaPlayer.release();
            mMediaPlayer = createMediaPlayer();
        }

    }

    final class TestConnection extends Connection {

        /**
         * Used to cleanup camera and media when done with connection.
         */
        //private TestVideoProvider mTestVideoCallProvider;
        private ConnectionRequest mOriginalRequest;

        TestConnection(boolean isIncoming, ConnectionRequest request) {
            mOriginalRequest = request;
            int properties = getConnectionProperties();

            setConnectionProperties(properties);

        }

        void startOutgoing() {
            setDialing();
            mHandler.postDelayed(() -> {
                setActive();
                activateCall(TestConnection.this);
            }, 4000);
        }

        /**
         * ${inheritDoc}
         */
        @Override
        public void onAbort() {
            destroyCall(this);
            destroy();
        }

        /**
         * ${inheritDoc}
         */
        @Override
        public void onAnswer(int videoState) {
            activateCall(this);
            setActive();
        }

        /**
         * ${inheritDoc}
         */
        @Override
        public void onPlayDtmfTone(char c) {
            if (c == '1') {
                setDialing();
            }
        }

        /**
         * ${inheritDoc}
         */
        @Override
        public void onStopDtmfTone() {
        }

        /**
         * ${inheritDoc}
         */
        @Override
        public void onDisconnect() {
            setDisconnected(new DisconnectCause(DisconnectCause.REMOTE));
            destroyCall(this);
            destroy();
        }

        /**
         * ${inheritDoc}
         */
        @Override
        public void onHold() {
            setOnHold();
        }

        /**
         * ${inheritDoc}
         */
        @Override
        public void onReject() {
            setDisconnected(new DisconnectCause(DisconnectCause.REJECTED));
            destroyCall(this);
            destroy();
        }

        /**
         * ${inheritDoc}
         */
        @Override
        public void onUnhold() {
            setActive();
        }
    }

}

对于实际实现,我指的是,com.android.services.telephony.TelephonyConnectionService但我遇到的问题是我无法访问以下类的 android api:

import com.android.internal.telephony.Call;
import com.android.internal.telephony.CallStateException;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.PhoneFactory;
import com.android.internal.telephony.PhoneProxy;
import com.android.internal.telephony.SubscriptionController;
import com.android.internal.telephony.cdma.CDMAPhone;

我的虚拟实现工作正常,所以我认为应该有一些方法来实现运营商呼叫,ConnectionService但我无法找出如何?

请指导我如何实现这一目标。我将非常感谢你。

4

0 回答 0