Здравствуйте и спасибо за вашу помощь в улучшении,
Так что я столкнулся с проблемой при разработке этого производственного кода. В некоторых случаях телефон кажется зависшим при исходящих звонках, этот код был унаследован мной, чтобы помочь решить эту проблему с помощью рефакторинга и рефакторинга кода. Поэтому я ищу альтернативные решения, а также решение этой проблемы.
Итак, моя текущая реализация использует широковещательный приемник для проверки состояния телефона в методе 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) Есть ли альтернативный, более чистый способ реализации этого исправления?
Большое спасибо заранее, и я открыт для любых предложений, которые могут помочь решить эту проблему