android 实现挂断电话【四种方式】,亲测可用

网上查到两种挂断电话方式

第一种:通过反射得到隐藏的API,需要aidl文件,但是这种方式测试后,无效。方法写出来吧,也许对你有你有帮助。

首先在main文件下创建报名com.android.internal.telephony,然后创建文件ITelephony.aidl

/*
 * Copyright (C) 2007 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.internal.telephony;

import android.os.Bundle;
import java.util.List;
import android.telephony.NeighboringCellInfo;

/**
 * Interface used to interact with the phone.  Mostly this is used by the 
 * TelephonyManager class.  A few places are still using this directly.
 * Please clean them up if possible and use TelephonyManager insteadl.
 *
 * {@hide}
 */
interface ITelephony {

    /**
     * Dial a number. This doesn't place the call. It displays
     * the Dialer screen.
     * @param number the number to be dialed. If null, this
     * would display the Dialer screen with no number pre-filled.
     */
    void dial(String number);

    /**
     * Place a call to the specified number.
     * @param number the number to be called.
     */
    void call(String number);

    /**
     * If there is currently a call in progress, show the call screen.
     * The DTMF dialpad may or may not be visible initially, depending on
     * whether it was up when the user last exited the InCallScreen.
     *
     * @return true if the call screen was shown.
     */
    boolean showCallScreen();

    /**
     * Variation of showCallScreen() that also specifies whether the
     * DTMF dialpad should be initially visible when the InCallScreen
     * comes up.
     *
     * @param showDialpad if true, make the dialpad visible initially,
     *                    otherwise hide the dialpad initially.
     * @return true if the call screen was shown.
     *
     * @see showCallScreen
     */
    boolean showCallScreenWithDialpad(boolean showDialpad);

    /**
     * End call or go to the Home screen
     *
     * @return whether it hung up
     */
    boolean endCall();

    /**
     * Answer the currently-ringing call.
     *
     * If there's already a current active call, that call will be
     * automatically put on hold.  If both lines are currently in use, the
     * current active call will be ended.
     *
     * TODO: provide a flag to let the caller specify what policy to use
     * if both lines are in use.  (The current behavior is hardwired to
     * "answer incoming, end ongoing", which is how the CALL button
     * is specced to behave.)
     *
     * TODO: this should be a oneway call (especially since it's called
     * directly from the key queue thread).
     */
    void answerRingingCall();

    /**
     * Silence the ringer if an incoming call is currently ringing.
     * (If vibrating, stop the vibrator also.)
     *
     * It's safe to call this if the ringer has already been silenced, or
     * even if there's no incoming call.  (If so, this method will do nothing.)
     *
     * TODO: this should be a oneway call too (see above).
     *       (Actually *all* the methods here that return void can
     *       probably be oneway.)
     */
    void silenceRinger();

    /**
     * Check if we are in either an active or holding call
     * @return true if the phone state is OFFHOOK.
     */
    boolean isOffhook();

    /**
     * Check if an incoming phone call is ringing or call waiting.
     * @return true if the phone state is RINGING.
     */
    boolean isRinging();

    /**
     * Check if the phone is idle.
     * @return true if the phone state is IDLE.
     */
    boolean isIdle();

    /**
     * Check to see if the radio is on or not.
     * @return returns true if the radio is on.
     */
    boolean isRadioOn();

    /**
     * Check if the SIM pin lock is enabled.
     * @return true if the SIM pin lock is enabled.
     */
    boolean isSimPinEnabled();

    /**
     * Cancels the missed calls notification.
     */
    void cancelMissedCallsNotification(); 

    /**
     * Supply a pin to unlock the SIM.  Blocks until a result is determined.
     * @param pin The pin to check.
     * @return whether the operation was a success.
     */
    boolean supplyPin(String pin);

    /**
     * Handles PIN MMI commands (PIN/PIN2/PUK/PUK2), which are initiated
     * without SEND (so <code>dial</code> is not appropriate).
     * 
     * @param dialString the MMI command to be executed.
     * @return true if MMI command is executed.
     */
    boolean handlePinMmi(String dialString);

    /**
     * Toggles the radio on or off.
     */
    void toggleRadioOnOff();

    /**
     * Set the radio to on or off
     */
    boolean setRadio(boolean turnOn);

    /**
     * Request to update location information in service state
     */
    void updateServiceLocation();

    /**
     * Enable location update notifications.
     */
    void enableLocationUpdates();

    /**
     * Disable location update notifications.
     */
    void disableLocationUpdates();

    /**
     * Enable a specific APN type.
     */
    int enableApnType(String type);

    /**
     * Disable a specific APN type.
     */
    int disableApnType(String type);

    /**
     * Allow mobile data connections.
     */
    boolean enableDataConnectivity();

    /**
     * Disallow mobile data connections.
     */
    boolean disableDataConnectivity();

    /**
     * Report whether data connectivity is possible.
     */
    boolean isDataConnectivityPossible();

    Bundle getCellLocation();

    /**
     * Returns the neighboring cell information of the device.
     */
    List<NeighboringCellInfo> getNeighboringCellInfo();

     int getCallState();
     int getDataActivity();
     int getDataState();
}

根据文档查询,这种方式只在2.3版本一下才有效。

第二种,通过耳机方式挂断,通过查看代码发现是jni回调java。代码已经集成完毕,使用方式跟第一种方式一样,调用answerRingingCall即可,但是此方法只对4.4以下有效。

第三种:通过无障碍模式,模拟点击挂断电话。需要继承AccessibilityService并开启服务。,监听广播,确定挂机按钮位置,再进行模拟点击挂断操作。

可以实现,但是方法过于麻烦。

package com.aaa.overlayviewdemo.auth;

import android.accessibilityservice.AccessibilityService;
import android.annotation.TargetApi;
import android.os.Build;
import android.text.TextUtils;
import android.util.Log;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;

import java.util.ArrayList;
import java.util.List;

/**
 * Auther xcd on 2022年10月26日17:55:12
 */
public class MyAccessibilityService extends AccessibilityService {

    private static final String TAG = "MyAccessibilityService";

    private String nowPackageName;
    private boolean isClickSetMoney;
    private boolean isClick, canClick = true;
    private int count;
    private int linearCount;
    private int clickCount;

    private boolean isRefresh;

    private boolean isTv, isRecycler;

    private boolean isFirst = true;

    private AccessibilityNodeInfo linearAccessibility;

    private AccessibilityNodeInfo relateAccessibility;

    private List<String> titleList = new ArrayList<>();


    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {
        nowPackageName = event.getPackageName().toString();
        Log.e("TAG_辅助功能","nowPackageName="+nowPackageName);
        if (nowPackageName.equals(getPackageName()) && MyApplication.getInstance()
                .getFlag()) {
            //      if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
            AccessibilityNodeInfo rootNode = this.getRootInActiveWindow();
            executeOperation(rootNode);
            //      }
        }
    }

    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
    private boolean executeOperation(AccessibilityNodeInfo info) {
        if (info == null) return false;

        if ("android.widget.Button".equals(info.getClassName()) && "刷新".equals(info.getText().toString())) {
            if (isFirst) {
                isFirst = false;
                return true;
            }
            if (!isRecycler) {
                info.performAction(AccessibilityNodeInfo.ACTION_CLICK);
                isRefresh = true;
                isRecycler = true;
            }
        }

        if ("android.widget.LinearLayout".equals(info.getClassName())) {
            linearAccessibility = info;
            int mCount = info.getChildCount();
            for (int i = 0; i < mCount; i++) {
                executeOperation(info.getChild(i));
            }
        }

        if ("android.widget.RelativeLayout".equals(info.getClassName())) {
            /*if (isClick) {
                info.performAction(AccessibilityNodeInfo.ACTION_CLICK);
                isClick = false;
                canClick = true;
            }*/
            relateAccessibility = info;
            return true;
        }

        if (info.getChildCount() == 0) {
            if ("android.widget.Image".equals(info.getClassName())) {
                if (!canClick) {
                    return true;
                }
                if(count == 5){
                    performGlobalAction(GLOBAL_ACTION_BACK);
                    canClick = false;
                    isRecycler = false;
                    return true;
                }
                if (!"90".equals(info.getText().toString())) {
                    try {
                        canClick = false;
                        count++;
                        Thread.sleep(6500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                   info.performAction(AccessibilityNodeInfo.ACTION_CLICK);
                }
                return true;
            }


            if (!TextUtils.isEmpty(info.getText()) && "关闭".equals(info.getText().toString())) {
                info.performAction(AccessibilityNodeInfo.ACTION_CLICK);
            }

            if ("android.widget.TextView".equals(info.getClassName()) && !TextUtils.isEmpty(info.getText()) && info.getText().toString().contains("/")) {
                canClick = true;
                relateAccessibility.performAction(AccessibilityNodeInfo.ACTION_CLICK);
            }

            if ("android.widget.TextView".equals(info.getClassName()) && !TextUtils.isEmpty(info.getText()) && (info.getText().toString().contains("视频")|| info.getText().toString().contains("置顶"))) {
                isTv = true;
            }

            if ("android.widget.TextView".equals(info.getClassName()) && !TextUtils.isEmpty(info.getText()) && (info.getText().toString().contains("评")|| info.getText().toString().contains("刚刚"))) {
                if (isTv) {
                    isTv = false;
                    return true;
                }
                if(!isRefresh && !isFirst){
                    return true;
                }
                isRecycler = false;
                canClick = true;
                count = 0;

                isRefresh = false;
                linearAccessibility.performAction(AccessibilityNodeInfo.ACTION_CLICK);
            }



           /* if (info.getText() != null) {
                if ("关注".equals(info.getText().toString())) {
                    if ("android.widget.TextView".equals(info.getClassName())) {

                    }
                } else if ("android.widget.ImageView".equals(info.getClassName())) {
                    if (isClick) {
                        info.performAction(AccessibilityNodeInfo.ACTION_CLICK);
                        isClick = false;
                    }
                } else if ("android.support.v4.view.ViewPager".equals(info.getClassName())) {
                    isClick = true;
                } else if ("金额".equals(info.getText().toString())) {
                    if ("android.widget.TextView".equals(info.getClassName())) {
                        AccessibilityNodeInfo parent = info.getParent();
                        AccessibilityNodeInfo child = parent.getChild(1);
                        ClipboardManager clipboardManager = (ClipboardManager) getSystemService
                                (CLIPBOARD_SERVICE);
                        ClipData clipData = ClipData.newPlainText("scb", MyApplication.getInstance()
                                .getParams());
                        clipboardManager.setPrimaryClip(clipData);
                        boolean b = child.performAction(AccessibilityNodeInfo.ACTION_PASTE);
                        isClickSetMoney = false;
                    }
                } else if ("确定".equals(info.getText().toString())) {
                    if ("android.widget.Button".equals(info.getClassName())) {
                        AccessibilityNodeInfo parent = info;
                        while (parent != null) {
                            if (parent.isClickable()) {
                                boolean b = parent.performAction(AccessibilityNodeInfo
                                        .ACTION_CLICK);
                                return b;
                            }
                            parent = parent.getParent();
                        }
                    }
                } else if ("android.widget.ScrollView".equals(info.getPackageName())) {
                    AccessibilityNodeInfo parent = info;
                    if (parent.isScrollable()) {
                        parent.performAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
                    }
                }
            }*/
        } else

        {
            for (int i = 0; i < info.getChildCount(); i++) {
                if (info.getChild(i) != null) {
                    executeOperation(info.getChild(i));
                }
            }
        }
        return false;
    }

    @Override
    public void onInterrupt() {

    }

}

第四种方式:抛异常,挂断电话,并关闭通话界面。此方式最简单,并且能实现项目需求。

private boolean killPhoneCall() {
        try {

            tryEndCall();

            TelephonyManager telephonyManager =
                    (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
            Class classTelephony = Class.forName(telephonyManager.getClass().getName());
            Method methodGetITelephony = classTelephony.getDeclaredMethod("getITelephony");
            methodGetITelephony.setAccessible(true);
            Object telephonyInterface = methodGetITelephony.invoke(telephonyManager);
            Class telephonyInterfaceClass =
                    Class.forName(telephonyInterface.getClass().getName());
            Method methodEndCall = telephonyInterfaceClass.getDeclaredMethod("endCall");
            methodEndCall.invoke(telephonyInterface);
        } catch (Exception e) {
            e.printStackTrace();
            Log.d(TAG1, "hangupPhoneCall: " + e.toString());
            return false;
        }
        return true;
    }

    private void tryEndCall() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            try {

                TelecomManager telecomManager = (TelecomManager) getSystemService(
                        Context.TELECOM_SERVICE);
                if (telecomManager != null) {
                    boolean res = telecomManager.endCall();
                    Log.d(TAG1, "tryEndCall: " + res);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

注意一:不要忘记添加权限

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

注意二:电话权限必须是始终允许,否则endCall无效。【这一条我被坑了一顿饭】

猜你喜欢

转载自blog.csdn.net/x158454996/article/details/78016851