這陣子公司安排進行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);
}