Setting up listeners in an MVP pattern is always a little tricky. I am pretty sure that there is no “right” or “wrong” answer to your question, since it heavily depends on your personal preferences and architecture style. But as I general rule it is always smart to move as much business logic to the presenter and keep everything else (this also includes listeners) as dumb as possible.
That is why I would suggest that you add an interface to your listener class and keep a (weak) reference to the presenter there. The only thing that the listener does in that example is listening to the state change and then forwarding the information to the presenter (where it gets distributed).
public class CallStateListener extends PhoneStateListener {
private final WeakReference<CallStateListenerInterface> presenterInterface;
public CallStateListener(CallStateListenerInterface presenterInterface){
this.presenterInterface = new WeakReference<>(presenterInterface);
}
public void onCallStateChanged(int state, String incomingNumber){
presenterInterface.get().onCallStateListenerCallStateChanged(state, incomingNumber);
}
public interface CallStateListenerInterface {
void onCallStateListenerCallStateChanged(int state, String incomingNumber);
}
}
You presenter needs to implement the CallStateListenerInterface (of course) and handles all the business logic when the call state changes.
public class myPresenter implements MVPInterface, CallStateListenerInterface {
/*your business logic*/
public void onCallStateListenerCallStateChanged(int state, String incomingNumber){
switch (state) {
case TelephonyManager.CALL_STATE_IDLE:
deactivateSpeaker();
if (callCount == 0) {
doCall();
} else {
checkForHangUp();
}
break;
case TelephonyManager.CALL_STATE_OFFHOOK:
checkAutoSpeaker();
break;
case TelephonyManager.CALL_STATE_RINGING:
break;
default:
break;
}
}
}
From here everything that needs to interact with the view will be called, but keep those calls to simple one line instructions and leave everything complicated for the presenter to decide.
If you don't want to add a new interface for each listener class you can also just pass the whole MVPInterface to the CallStateChangedListener (and declare everything at a central place in your contract).
If you implement the listener this way you got a pretty clean architecture that should be easily testable.
I hope this was helpfull and please let me know if you have any questions :)
SMALL ALTERNATIVE IMPLEMENTATION:
As pointed out by a commenter: In my original suggestion, the presenter accesses the static phone state integers from the TelephonyManager class. This is not the cleanest design, since the presenter should be kept completely separate from any Android specific code.
Since there are no real super clean ways to work around this, I can only suggest a mapping from the TelephonyManager states to a (self defined) phone state that is kept in the presenter (or better yet: a static constants file).
Basicly that means the onCallStateChanged in the CallStateListener should look something like this:
public void onCallStateChanged(int state, String incomingNumber){
int stateValue = myPresenter.PRESENTER_CALL_STATE_IDLE;
if (state == TelephonyManager.CALL_STATE_RINGING){
stateValue = myPresenter.PRESENTER_CALL_STATE_RINGING;
} else if (state == TelephonyManager.CALL_STATE_OFFHOOK){
stateValue = myPresenter.PRESENTER_CALL_STATE_OFFHOOK;
}
presenterInterface.get().onCallStateListenerCallStateChanged(stateValue , incomingNumber);
}
With this implementation you could move the TelephonyManager states out of the presenter and use your own defined ones.
(once again: I am really not sure if this is worth the effort, but I wanted to at least offer the alternative)