【问题标题】:Android Phone Call UI - ChangeAndroid 电话用户界面 - 更改
【发布时间】:2016-05-08 15:32:07
【问题描述】:

如何更改电话用户界面?就像我有自己的拨号器布局和联系人布局一样,但如何更改呼叫 UI。那么,当通话进行时,我可以移除扬声器按钮吗?

这是我创建的拨号器场景:Dialer Picture

但我不知道如何编辑此屏幕:Calling Picture

编辑:我已经构建了 UI,只是无法在通话期间显示它!

这是一个更简单版本的代码:

public class MainActivity extends Activity {

private Button callBtn;
private Button dialBtn;
private EditText number;

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

    number = (EditText) findViewById(R.id.phoneNumber);
    callBtn = (Button) findViewById(R.id.call);
    dialBtn = (Button) findViewById(R.id.dial);

    // add PhoneStateListener for monitoring
    MyPhoneListener phoneListener = new MyPhoneListener();
    TelephonyManager telephonyManager = 
        (TelephonyManager) this.getSystemService(Context.TELEPHONY_SERVICE);
    // receive notifications of telephony state changes 
    telephonyManager.listen(phoneListener,PhoneStateListener.LISTEN_CALL_STATE);

    callBtn.setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {
            try {
                // set the data
                String uri = "tel:"+number.getText().toString();
                Intent callIntent = new Intent(Intent.ACTION_CALL, Uri.parse(uri));

                startActivity(callIntent);
            }catch(Exception e) {
                Toast.makeText(getApplicationContext(),"Your call has failed...",
                    Toast.LENGTH_LONG).show();
                e.printStackTrace();
            }
        }
    });

    dialBtn.setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {
            try {
                String uri = "tel:"+number.getText().toString();
                Intent dialIntent = new Intent(Intent.ACTION_DIAL, Uri.parse(uri));

                startActivity(dialIntent);
            }catch(Exception e) {
                Toast.makeText(getApplicationContext(),"Your call has failed...",
                    Toast.LENGTH_LONG).show();
                e.printStackTrace();
            }
        }
    });
}

private class MyPhoneListener extends PhoneStateListener {

    private boolean onCall = false;

    @Override
    public void onCallStateChanged(int state, String incomingNumber) {

        switch (state) {
        case TelephonyManager.CALL_STATE_RINGING:
            // phone ringing...
            Toast.makeText(MainActivity.this, incomingNumber + " calls you", 
                    Toast.LENGTH_LONG).show();
            break;

        case TelephonyManager.CALL_STATE_OFFHOOK:
            // one call exists that is dialing, active, or on hold
            Toast.makeText(MainActivity.this, "on call...", 
                    Toast.LENGTH_LONG).show();
            //because user answers the incoming call
            onCall = true;
            break;

        case TelephonyManager.CALL_STATE_IDLE:
            // in initialization of the class and at the end of phone call 

            // detect flag from CALL_STATE_OFFHOOK
            if (onCall == true) {
                Toast.makeText(MainActivity.this, "restart app after call", 
                        Toast.LENGTH_LONG).show();

                // restart our application
                Intent restart = getBaseContext().getPackageManager().
                    getLaunchIntentForPackage(getBaseContext().getPackageName());
                restart.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                startActivity(restart);

                onCall = false;
            }
            break;
        default:
            break;
        }

    }
}
}

谢谢!

【问题讨论】:

  • 构建自己的拨号器
  • 我构建了 UI,但如何让它在通话期间显示?
  • 你解决了吗?有一个代码stackoverflow.com/a/49835987/4300670,但它是用一种我不知道如何将其翻译成普通java(Android Studio)的语言编写的。

标签: android user-interface phone-call


【解决方案1】:

在清单中添加调用权限

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

然后需要检查是否按下了呼叫按钮。用于以下意图过滤器

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

在触发 UI 时

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

这意味着您在清单中的调用活动将是这样的

<activity
        android:name="com.example.MainActivity"
        android:label="@string/app_name" >
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>

        <!-- open activity when establishing a call -->
        <intent-filter>
            <action android:name="android.intent.action.CALL_PRIVILEGED" />
            <category android:name="android.intent.category.DEFAULT" />
            <data android:scheme="tel" />
        </intent-filter>

    </activity>

【讨论】:

    【解决方案2】:

    由于 API 23 是可能的,请参阅 Replacing default Phone app on Android 6 and 7 with InCallService arekolek

    Kotlin 版本:Simple Phone

    Java 版本:Simple Phone Dialer

    编辑

    按照推荐,下面是最简单的java版本。

    下面显示了 Manifest,需要 intent-filter

    <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>
    

    MainActivity 只是有一个 Button 和一个 Intent 重定向到 DialerActivity

    下面是DialerActivity。在此 Activity 中,您将应用设置为默认应用,以便使用您的 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;
            }
        }
    
    }
    

    下面是如何实现InCallService 来管理调用。

    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);
        }
    
    }
    

    这是对象的实现方式。

    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();
            }
        }
    }
    

    最后,CallActivity 是您启动 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);
        }
    
    }
    

    这是DialerActivityxmlactivity_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>
    

    这是CallActivityxmlactivity_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>
    

    希望对你有帮助!

    【讨论】:

    • 它不适用于小米设备。即 CallService 在通话期间未调用。
    • 我正在使用适当的级别,这就是为什么它在小米设备之外工作。
    • +1 部分设备不支持此功能,请务必检查品牌:stackoverflow.com/a/27836910/1683466 并排除小米,这也需要在华为上测试。
    【解决方案3】:

    编辑: 实际上 api 23 已发布,请查看 this answer。请记住,某些设备可能不支持此功能。

    预览:

    实际呼叫视图(您在呼叫期间看到的)无法更改。

    更具体地说,android 的某些部分不能被覆盖, android 有他的核心,作为开发人员,我们对它的访问权限有限 核。在你的情况下你可以覆盖拨号器,但你不能 覆盖实际的电话视图

    在 android 团队决定分享之前,您对此无能为力 我们(开发者)拥有这个核心功能。

    【讨论】:

    • 请描述得更详细一些,也许可以告诉他可以做什么,因为他想要的东西行不通,谢谢。
    • Ash Patel 问“我就是无法在通话中显示它!”,答案是:不,你不能,因为没有人可以!
    • 是的,请阅读我的回答,在否决之前,它的结尾是“在 android 团队决定与我们(开发人员)分享这个核心功能之前,你对此无能为力。”所以基本上 api 是可以预见的变化,
    【解决方案4】:

    构建您自己的拨号器用户界面。检查this。 您将需要一个 Activity 来处理意图,然后显示自定义 UI 是您的工作。

    【讨论】:

    • 如果您点击该链接,那么它实际上会告诉您如何将意图重定向到您的应用
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-30
    • 2017-01-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多