Телефон отключен при совершении исходящих звонков с использованием TelephonyManager - PullRequest
1 голос
/ 04 марта 2020

Здравствуйте и спасибо за вашу помощь в улучшении,

Так что я столкнулся с проблемой при разработке этого производственного кода. В некоторых случаях телефон кажется зависшим при исходящих звонках, этот код был унаследован мной, чтобы помочь решить эту проблему с помощью рефакторинга и рефакторинга кода. Поэтому я ищу альтернативные решения, а также решение этой проблемы.

Итак, моя текущая реализация использует широковещательный приемник для проверки состояния телефона в методе onRecieve (), как показано ниже

public PhonecallService() {
        mApiService = new ApiService();
    }

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

        Timber.d("ONRECIEVE Service");

        String stateStr = intent.getExtras() != null ? intent.getExtras().getString(TelephonyManager.EXTRA_STATE) : "";
        String number = intent.getExtras() != null ? intent.getExtras().getString(TelephonyManager.EXTRA_INCOMING_NUMBER) : "";

        try {


            if (intent.getAction().equals("android.intent.action.NEW_OUTGOING_CALL")) {
                savedNumber = intent.getExtras().getString("android.intent.extra.PHONE_NUMBER");
            } else {
                int state = 0;
                if (stateStr != null) {
                    if (stateStr.equals(TelephonyManager.EXTRA_STATE_IDLE)) {
                        state = TelephonyManager.CALL_STATE_IDLE;
                    } else if (stateStr.equals(TelephonyManager.EXTRA_STATE_OFFHOOK)) {
                        state = TelephonyManager.CALL_STATE_OFFHOOK;
                    } else if (stateStr.equals(TelephonyManager.EXTRA_STATE_RINGING)) {
                        state = TelephonyManager.CALL_STATE_RINGING;
                    }
                }
                onCallStateChanged(context, state, number);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

Затем следует вызвать этот метод и проверить состояние телефона, чтобы затем отправить трансляцию следующим образом:

public void onCallStateChanged(Context context, int state, String number) {
        if (lastState == state) {
            return;
        }
        switch (state) {
            case TelephonyManager.CALL_STATE_RINGING:
                if (number != null) {
                    isIncoming = true;
                    callStartTime = new Date();
                    savedNumber = number;
                    onIncomingCallStarted(context, number, callStartTime);
                    lastState = state;
                }
                break;
            case TelephonyManager.CALL_STATE_OFFHOOK:
                if (lastState != TelephonyManager.CALL_STATE_RINGING) {
                    isIncoming = false;
                    callStartTime = new Date();
                    onOutgoingCallStarted(context, savedNumber, callStartTime);
                } else {
                    isIncoming = true;
                    callStartTime = new Date();
                    onIncomingCallAnswered(context, savedNumber, callStartTime);
                }
                lastState = state;
                break;
            case TelephonyManager.CALL_STATE_IDLE:
                if (lastState == TelephonyManager.CALL_STATE_RINGING) {
                    onMissedCall(context, savedNumber, callStartTime);
                } else if (isIncoming) {
                    onIncomingCallEnded(context, savedNumber, callStartTime, new Date());
                } else {
                    onOutgoingCallEnded(context, savedNumber, callStartTime, new Date());
                }
                lastState = state;
                break;
        }

    }

Это затем обрабатывается в CallOutFragment

public class CallOutFragment extends BaseFragment<MainViewModel, CallOutFragmentBinding> {
    private BroadcastReceiver mKeypadBroadCastReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            viewModel.getmIncomingCall().postValue(true);
        }
    };
    private BroadcastReceiver mBroadCastReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            viewModel.getmIncomingCall().postValue(true);
        }
    };

    @Override
    public Class<MainViewModel> getViewModel() {
        return MainViewModel.class;
    }

    @Override
    public int getLayoutRes() {
        return R.layout.call_out_fragment;

    }

    public static CallOutFragment newInstance() {
        Bundle args = new Bundle();
        CallOutFragment fragment = new CallOutFragment();
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        if (getActivity() != null) {
            if (mBroadCastReceiver.isOrderedBroadcast()) {
                try {
                    getActivity().unregisterReceiver(mBroadCastReceiver);

                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        if (mKeypadBroadCastReceiver.isOrderedBroadcast()) {
            try {

                getActivity().unregisterReceiver(mKeypadBroadCastReceiver);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (getActivity() != null) {
            if (mBroadCastReceiver.isOrderedBroadcast()) {

                getActivity().registerReceiver(mBroadCastReceiver, new IntentFilter(Constants.PREFERENCES_CALL_OUT_CLOSE_BROADCAST));
            }
            if (mKeypadBroadCastReceiver.isOrderedBroadcast()) {

                getActivity().registerReceiver(mKeypadBroadCastReceiver, new IntentFilter(Constants.KEYPAD_BROADCAST));
            }
        }
        setHasOptionsMenu(false);
    }

    @Override
    public void onPause() {
        if (getActivity() != null) {
            if (!mBroadCastReceiver.isOrderedBroadcast()) {
                try {
                    getActivity().unregisterReceiver(mBroadCastReceiver);

                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            if (!mKeypadBroadCastReceiver.isOrderedBroadcast()) {
                try {
                    getActivity().unregisterReceiver(mKeypadBroadCastReceiver);

                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }

        getActivity().registerReceiver(mBroadCastReceiver, new IntentFilter(Constants.PREFERENCES_CALL_OUT_CLOSE_BROADCAST));
        getActivity().registerReceiver(mKeypadBroadCastReceiver, new IntentFilter(Constants.KEYPAD_BROADCAST));
        super.onPause();
    }

    @Override
    public void onResume() {
        super.onResume();
        if (getActivity() != null) {
            getActivity().registerReceiver(mBroadCastReceiver, new IntentFilter(Constants.PREFERENCES_CALL_OUT_CLOSE_BROADCAST));
            getActivity().registerReceiver(mKeypadBroadCastReceiver, new IntentFilter(Constants.KEYPAD_BROADCAST));
        }
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
        super.onCreateView(inflater, container, savedInstanceState);
        setHasOptionsMenu(false);
        viewModel.getmDialerContactsFilter().postValue(null);
        if (getActivity() != null) {
            ((MainActivity) getActivity()).hideFloatingDialer();
        }
        viewModel.getmIncomingCall().observe(this, incomingCall -> {
            if (incomingCall != null && incomingCall) {
                viewModel.getmSelectedNumber().postValue(null);
                if (!Fragments.DIALER.equals(FragmentUtils.callOutPreviousFragment)) {
                    ((MainActivity) getActivity()).showFloatingDialer();
                }
                if (FragmentUtils.currFragment != FragmentUtils.callOutPreviousFragment && FragmentUtils.callOutPreviousFragment != null) {
                    FragmentUtils.switchFragment((AppCompatActivity) getActivity(), FragmentUtils.callOutPreviousFragment);
                }
            }
        });
        String number = viewModel.getmSelectedNumber().getValue();
        viewModel.getmOutCallStatus().observe(this, apiCallStatus -> {
            if (apiCallStatus != null) {
                if (apiCallStatus.getStatus().equals(Status.ERROR)) {
                    showOkDialog("Outbound call failed please check network", viewModel.getmMode().getValue(),
                            dialog -> viewModel.getmIncomingCall().postValue(true));
                }
            }
        });
        viewModel.getmOutCallDeclineStatus().observe(this, apiCallStatus -> {
            if (apiCallStatus != null) {
                viewModel.getmIncomingCall().postValue(true);
            }
        });
        if (viewModel.getmConversationId().getValue() == null && viewModel.getmOutCallStatus().getValue() == null) {
            if (number != null) {
                viewModel.callRemoteNumber(number);
            } else {
                viewModel.getmIncomingCall().postValue(true);
            }
        }
        Pair<String, String> numberDescription = viewModel.findNumberDescription(number);
        if (numberDescription != null) {
            dataBinding.outCallName.setText(numberDescription.first);
        } else {
            if (number != null) {
                dataBinding.outCallName.setText(PhonecallService.getNumberSmart(number));
            } else {
                getString(R.string.number_unknown);
            }
        }
        final Handler handler = new Handler();
        Runnable runnable = new Runnable() {

            int count = 0;

            @Override
            public void run() {
                try {
                    count++;

                    if (getActivity() != null) {
                        if (count == 1) {
                            dataBinding.outTextview.setText(String.format("%s.", getActivity().getResources().getString(R.string.call_out_call)));
                        } else if (count == 2) {
                            dataBinding.outTextview.setText(String.format("%s..", getActivity().getResources().getString(R.string.call_out_call)));
                        } else if (count == 3) {
                            dataBinding.outTextview.setText(String.format("%s...", getActivity().getResources().getString(R.string.call_out_call)));
                        }

                        if (count == 3)
                            count = 0;
                    }

                    handler.postDelayed(this, 2 * 500);
                } catch (Exception e) {
                    Timber.e(e);
                }
            }
        };
        handler.postDelayed(runnable, 500);

        final Handler closeHandler = new Handler();
        Runnable closeRunnable = () -> {
            try {
                if (Objects.equals(viewModel.getmIncomingCall().getValue(), false)) {
                    viewModel.getmIncomingCall().postValue(true);
                    viewModel.timeoutCall();
                    viewModel.getmDisplayAlert().postValue(getResources().getString(R.string.call_out_fail));
                }
            } catch (Exception e) {
                Timber.e(e);
            }
        };
        closeHandler.postDelayed(closeRunnable, 20000);

        dataBinding.getRoot().setFocusableInTouchMode(true);
        dataBinding.getRoot().requestFocus();
        dataBinding.getRoot().setOnKeyListener((v, keyCode, event) -> {
            if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) {
                viewModel.getmIncomingCall().postValue(true);
                return true;
            }
            return false;
        });
        return dataBinding.getRoot();
    }
}

Некоторые из моих подозрений пришли из того, как был настроен манифест так вот разрешения, установленные в нем вместе с настройками приемника;

  <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.READ_CONTACTS" />
    <uses-permission android:name="android.permission.WRITE_CONTACTS" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="android.permission.ANSWER_PHONE_CALLS" />
    <uses-permission android:name="android.permission.READ_CALL_LOG" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
    <uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
<receiver
            android:name="com.teliqo.pipcall.services.PhonecallService"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="com.android.vending.INSTALL_REFERRER" />
                <action android:name="android.intent.action.ANSWER" />
                <action android:name="android.intent.action.CALL_BUTTON" />
                <action android:name="android.intent.action.NEW_OUTGOING_CALL" />
                <action android:name="android.intent.action.PHONE_STATE" />
            </intent-filter>
        </receiver>

Это происходит довольно случайно и не удалось сгенерировать отчет cra sh, единственное, что имеет Я был в состоянии наблюдать, что фрагмент вызывается много раз.

Мой вопрос:
1) Есть ли причина, по которой телефон зависает при совершении вызова?
2) Есть ли альтернативный, более чистый способ реализации этого исправления?

Большое спасибо заранее, и я открыт для любых предложений, которые могут помочь решить эту проблему

...