这阵子公司安排进行8.0的适配,就对比着代码进行差异化分析,发现InCallUi的变化很大,整体架构变了,变的更加清晰。界面变的更加漂亮了,跟之前的版本相比,可以说的上是天壤之别啊。InCallUi8.0也增加了一些比较实用的功能,比如说是来电防误触。我们把手机放在口袋,这时候来电,可能就会出现误触,将电话挂断或者接听,而我们可能并不知道,这明显很不好。仔细的看了一下8.0防误触功能的实现,做一下笔记。
在AnswerScreenPresenter中的onCreate方法中,根据条件判断决定是否将距离传感器初始化好,AnswerScreenPresenter就相当于之前版本的AnswerPresenter。
PseudoScreenState pseudoScreenState = InCallPresenter.getInstance().getPseudoScreenState();
if (AnswerProximitySensor.shouldUse(context, call)) {
new AnswerProximitySensor(context, call, pseudoScreenState);
} else {
pseudoScreenState.setOn(true);
}
我们现在来看看详细的判断:
1.不是来电状态
2.不允许使用来电传感器
3.不支持距离传感器
4.当前状态时亮屏
public static boolean shouldUse(Context context, DialerCall call) {
// Don't use the AnswerProximitySensor for call waiting and other states. Those states are
// handled by the general ProximitySensor code.
if (call.getState() != State.INCOMING) {
LogUtil.i("AnswerProximitySensor.shouldUse", "call state is not incoming");
return false;
}
if (!ConfigProviderBindings.get(context)
.getBoolean(CONFIG_ANSWER_PROXIMITY_SENSOR_ENABLED, true)) {
LogUtil.i("AnswerProximitySensor.shouldUse", "disabled by config");
return false;
}
if (!context
.getSystemService(PowerManager.class)
.isWakeLockLevelSupported(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK)) {
LogUtil.i("AnswerProximitySensor.shouldUse", "wake lock level not supported");
return false;
}
if (isDefaultDisplayOn(context)) {
LogUtil.i("AnswerProximitySensor.shouldUse", "display is already on");
return false;
}
return true;
}
PseudoScreenState类的代码如下:
public class PseudoScreenState {
/** Notifies when the on state has changed. */
public interface StateChangedListener {
void onPseudoScreenStateChanged(boolean isOn);
}
private final Set<StateChangedListener> listeners = new ArraySet<>();
private boolean on = true;
public boolean isOn() {
return on;
}
public void setOn(boolean value) {
if (on != value) {
on = value;
for (StateChangedListener listener : listeners) {
listener.onPseudoScreenStateChanged(on);
}
}
}
public void addListener(StateChangedListener listener) {
listeners.add(listener);
}
public void removeListener(StateChangedListener listener) {
listeners.remove(listener);
}
}
AnswerProximitySensor的构造函数如下:
public AnswerProximitySensor(
Context context, Call call, PseudoScreenState pseudoScreenState) {
this.call = call;
Log.d("AnswerProximitySensor.constructor", "acquiring lock");
if (ConfigProviderBindings.get(context).getBoolean(CONFIG_ANSWER_PSEUDO_PROXIMITY_WAKE_LOCK_ENABLED, true)) {
answerProximityWakeLock = new PseudoProximityWakeLock(context, pseudoScreenState);
} else {
// TODO: choose a wake lock implementation base on framework/device.
// These bugs requires the PseudoProximityWakeLock workaround:
// b/30439151 Proximity sensor not working on M
// b/31499931 fautly touch input when screen is off on marlin/sailfish
answerProximityWakeLock = new SystemProximityWakeLock(context);
}
answerProximityWakeLock.setScreenOnListener(this);
answerProximityWakeLock.acquire();
CallList.getInstance().addListener(this);
}
在电话状态发生改变和远离距离传感器的情况下,将
在InCallActiivityde onResume方法中进行了调用:
PseudoScreenState pseudoScreenState = InCallPresenter.getInstance().getPseudoScreenState();
pseudoScreenState.addListener(this);
onPseudoScreenStateChanged(pseudoScreenState.isOn());
在onPause方法中将监听移除:
InCallPresenter.getInstance().getPseudoScreenState().removeListener(this);
在InCallActivity中,pseudoBlackScreenOverlay是一个黑色的浮层,当满足来电防误触的情况下时,将浮层设置为VISIBLE,使得触摸事件不响应,同时在dispatchTouchEvent方法中,直接返回true,不继续分发触摸事件
private View pseudoBlackScreenOverlay;
private boolean touchDownWhenPseudoScreenOff;
@Override
public void onPseudoScreenStateChanged(boolean isOn) {
Log.d("InCallActivity.onPseudoScreenStateChanged", "isOn: " + isOn);
pseudoBlackScreenOverlay.setVisibility(isOn ? View.GONE : View.VISIBLE);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
// Reject any gesture that started when the screen is in the fake off state.
if (touchDownWhenPseudoScreenOff) {
if (event.getAction() == MotionEvent.ACTION_UP) {
touchDownWhenPseudoScreenOff = false;
}
return true;
}
// Reject all touch event when the screen is in the fake off state.
if (!InCallPresenter.getInstance().getPseudoScreenState().isOn()) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
touchDownWhenPseudoScreenOff = true;
Log.d("InCallActivity.dispatchTouchEvent", "touchDownWhenPseudoScreenOff");
}
return true;
}
return super.dispatchTouchEvent(event);
}