Replace default phone APP

北城余情 提交于 2020-12-29 04:31:18

问题


I'm trying to replace the default android phone call app. More specifically I want to launch my custom phone call screen everytime a call action is performed.

I understand this is possible since Android's API 24 (Version 7.0 - Nougat) but I found no references on how to achieve this. Just to be clear, I do not want to show an overlay layout for the call screen. I want to set my call app as default.

I found a similar question for the sms app here: Stackoverflow - replace default sms app. The answer made it clear that you need to list all the components for the sms app in order to be able to set it as the default application in android's settings. But I can't find any reference for the phone call components.

How can I achieve this?

For reference this is my manifest file:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.mynumbers">
    <uses-permission android:name="android.permission.CALL_PHONE" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>
    <uses-permission android:name="android.permission.ANSWER_PHONE_CALLS"/>
    <uses-permission android:name="android.permission.INTERNET" />
    <application
        android:label="">

        <activity
            android:name=".MainActivity"
            android:theme="@style/Theme.AppCompat.Light.NoActionBar">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <action android:name="android.intent.action.DIAL" />
                <action android:name="android.intent.action.CALL_BUTTON" />
                <action android:name="android.intent.action.CALL_PRIVILEGED" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />
                <category android:name="android.intent.category.LAUNCHER" />
                <data android:scheme="tel"/>
            </intent-filter>
        </activity>

        <activity
            android:name=".NumberManagementActivity"
            android:parentActivityName=".MainActivity">
            <meta-data
                android:name="android.support.PARENT_ACTIVITY"
                android:value=".MainActivity" />
        </activity>

        <activity
        android:name=".CallActivity"
            android:parentActivityName=".MainDialerActivity"
            android:theme="@style/Theme.AppCompat.Light.NoActionBar">
            <meta-data
                android:name="android.support.PARENT_ACTIVITY"
                android:value=".MainDialerActivity" />
            <intent-filter>
                <action android:name="android.permission.CALL_PHONE" />
                <action android:name="android.intent.action.PHONE_STATE" />
            </intent-filter>
        </activity>

        <activity
            android:name=".MainDialerActivity"
            android:parentActivityName=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.DIAL" />
                <action android:name="android.intent.action.CALL_BUTTON"/>
                <action android:name="android.intent.action.CALL_PRIVILEGED" />
                <action android:name="android.permission.CALL_PHONE"/>
                <data android:scheme="tel"/>
            </intent-filter>
        </activity>

        <receiver
            android:name=".PhoneCallListener">
            <intent-filter android:priority="1">
                <action android:name="android.intent.action.PHONE_STATE" />
                <action android:name="android.intent.action.NEW_OUTGOING_CALL" />
            </intent-filter>
        </receiver>
        <!-- Incoming call activity -->
        <activity
            android:name=".IncomingCallActivity"
            android:configChanges="orientation"
            android:label="@string/title_activity_fullscreen"
            android:theme="@style/IncomingCallTheme">
            <intent-filter>
                <action android:name="android.intent.action.ANSWER" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>

        <activity
            android:name=".DefaultConfigurationActivity"
            android:parentActivityName=".MainActivity">
        </activity>
    </application>
</manifest>

回答1:


I have searched all over the google, and finally find a solution. now I can replace the default phone(calling) app with my custom created app If you add those permissions in your manifest file you will able to change your app as default calling app in -> Phone Menu -> Settings -> Apps -> Default Apps (Menu item in right top corner) -> Default Calling apps and finally select your app.

here are the permissions list

<uses-permission
    android:name="android.permission.CALL_PRIVILEGED"
    tools:ignore="ProtectedPermissions" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
<uses-permission android:name="android.permission.READ_CALL_LOG" />
<uses-permission android:name="android.permission.WRITE_CALL_LOG" />
<uses-permission android:name="android.permission.READ_PROFILE" />
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
<uses-permission android:name="android.permission.NFC" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission
    android:name="android.permission.MODIFY_PHONE_STATE"
    tools:ignore="ProtectedPermissions" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<uses-permission android:name="android.permission.USE_CREDENTIALS" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
<uses-permission android:name="com.android.voicemail.permission.ADD_VOICEMAIL" />
<uses-permission android:name="com.android.voicemail.permission.READ_WRITE_ALL_VOICEMAIL" />
<uses-permission
    android:name="android.permission.ALLOW_ANY_CODEC_FOR_PLAYBACK"
    tools:ignore="ProtectedPermissions" />
<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" />
<!-- allow broadcasting secret code intents that reboot the phone -->
<uses-permission
    android:name="android.permission.REBOOT"
    tools:ignore="ProtectedPermissions" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<!--
 This tells the activity manager to not delay any of our activity
 start requests, even if they happen immediately after the user
 presses home.
-->
<uses-permission
    android:name="android.permission.STOP_APP_SWITCHES"
    tools:ignore="ProtectedPermissions" />



回答2:


since API 23 it is possible, see Replacing default Phone app on Android 6 and 7 with InCallService arekolek.

Kotlin version: Simple Phone

Java version: Simple Phone Dialer

It is posted below the simplest java version. For more details, access the links above.

Below is shown the Manifest with the intent-filter needed.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.aliton.customphonecall">

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

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

                <!-- Handle links from other applications -->
                <action android:name="android.intent.action.VIEW" />
                <action android:name="android.intent.action.DIAL" />
                <!-- Populate the system chooser -->
                <category android:name="android.intent.category.DEFAULT" />
                <!-- Handle links in browsers -->
                <category android:name="android.intent.category.BROWSABLE" />

                <data android:scheme="tel" />
            </intent-filter>
            <intent-filter>
                <action android:name="android.intent.action.DIAL" />

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

        <service
            android:name=".CallService"
            android:permission="android.permission.BIND_INCALL_SERVICE">
            <meta-data
                android:name="android.telecom.IN_CALL_SERVICE_UI"
                android:value="true" />

            <intent-filter>
                <action android:name="android.telecom.InCallService" />
            </intent-filter>
        </service>

        <activity android:name=".CallActivity"></activity>
    </application>

</manifest>

The MainActivity just have a Button with an Intent redirecting to the DialerActivity.

Below is the DialerActivity. In this Activity, you will set your app as default in order to make call with your UI.

public class DialerActivity extends AppCompatActivity {

    private static final int REQUEST_CALL_PHONE = 10;

    EditText phoneNumber;

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

        phoneNumber = (EditText) findViewById(R.id.etNumber);
        Button bCall = (Button) findViewById(R.id.btnCall);

        offerReplacingDefaultDialer();

        bCall.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                makeCall();
            }
        });
    }

    private void makeCall() {
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE) == PackageManager.PERMISSION_GRANTED) {
            Uri uri = Uri.parse("tel:"+phoneNumber.getText().toString().trim());
            Intent Call = new Intent(Intent.ACTION_CALL, uri);
            //Toast.makeText(this, "Entered makeCall()", Toast.LENGTH_SHORT).show();
            Log.i("debinf Dialer", "Entered makeCall()");
            startActivity(Call);

        } else {
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CALL_PHONE}, REQUEST_CALL_PHONE);
        }
    }

    private void offerReplacingDefaultDialer() {
        if (getSystemService(TelecomManager.class).getDefaultDialerPackage() != getPackageName()) {
            Intent ChangeDialer = new Intent(TelecomManager.ACTION_CHANGE_DEFAULT_DIALER);
            ChangeDialer.putExtra(TelecomManager.EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME, getPackageName());
            startActivity(ChangeDialer);
        }

    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);

        if (requestCode == REQUEST_CALL_PHONE) {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                makeCall();
            } else {
                Toast.makeText(this, "calling permission denied", Toast.LENGTH_LONG).show();
            }
            //return;
        }
    }

}

Here is how InCallService is implemented to manage the calls.

public class CallService extends InCallService {

    OngoingCallObject ongoingCallObject;

    @Override
    public void onCallAdded(Call call) {
        super.onCallAdded(call);
        new OngoingCallObject().setCall(call);

        //Intent CallAct = new Intent(this, CallActivity.class);
        //startActivity(CallAct);

        CallActivity.start(this, call);
    }

    @Override
    public void onCallRemoved(Call call) {
        super.onCallRemoved(call);
        new OngoingCallObject().setCall(null);
    }

}

Here is how the Object is implemented.

public class OngoingCallObject {

    private static Call call;

    private Object callback = new Callback() {
        @Override
        public void onStateChanged(Call call, int state) {
            super.onStateChanged(call, state);
            Log.i("debinf OngoingObj", "state is "+state);
        }
    };

    public void setCall(Call call) {
        if (this.call != null) {
            this.call.unregisterCallback((Call.Callback)callback);
        }

        if (call != null) {
            call.registerCallback((Call.Callback)callback);
            Log.i("debinf OngoingObj", "call.getState() is "+call.getState());
        }

        this.call = call;
    }

    public void answer() {
        //assert this.call != null;
        if (this.call != null) {
            this.call.answer(VideoProfile.STATE_AUDIO_ONLY);
        }
    }

    public void hangup() {
        //assert this.call != null;
        if (this.call != null) {
            this.call.disconnect();
        }
    }
}

And finally, the CallActivity where you launch your UI.

public class CallActivity extends AppCompatActivity {

    TextView callInfo;
    Button answer, hangup;

    private String number;
    private OngoingCallObject ongoingCall;

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

        ongoingCall = new OngoingCallObject();

        answer = (Button) findViewById(R.id.answer);
        hangup = (Button) findViewById(R.id.hangup);
        callInfo = (TextView) findViewById(R.id.callInfo);

        number = Objects.requireNonNull(getIntent().getData().getSchemeSpecificPart());

        callInfo.setText("Calling number : "+number);

        answer.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //Toast.makeText(CallActivity.this, "Answer button", Toast.LENGTH_SHORT).show();
                ongoingCall.answer();
            }
        });

        hangup.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                ongoingCall.hangup();
            }
        });


    }

    public static void start(Context context, Call call) {
        Intent intent = new Intent(context, CallActivity.class);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        intent.setData(call.getDetails().getHandle());
        context.startActivity(intent);
    }

}

Here is the xml for DialerActivity: activity_dialer.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".DialerActivity">

    <EditText
        android:id="@+id/etNumber"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="Tel number"/>

    <Button
        android:id="@+id/btnCall"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:text="CallActivity"
        android:layout_below="@+id/etNumber" />

</RelativeLayout>

Here is the xml for CallActivity: activity_call.xml

<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".CallActivity">

    <TextView
        android:id="@+id/callInfo"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.3"
        tools:text="Hello World!" />

    <Button
        android:id="@+id/answer"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Answer"
        app:layout_constraintBaseline_toBaselineOf="@+id/hangup"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/hangup" />

    <Button
        android:id="@+id/hangup"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hang up"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@+id/answer"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/callInfo" />


</android.support.constraint.ConstraintLayout>

I hope it helps!




回答3:


<intent-filter>
    <action android:name="android.intent.action.DIAL" />
    <category android:name="android.intent.category.DEFAULT" />
</intent-filter>

Set this in your manifest.



来源:https://stackoverflow.com/questions/48153516/replace-default-phone-app

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!