问题:把手机旋转到270°,然后打开任务列表(就是屏幕下方的方块按钮。)然后向上无法把打开的app删除,可以向下滑动,但是会crash。
修改后,在270°的时候可以向上删除app,无法向下滑动,也不会crash。
首先打开adb logcat,看问题出在哪。从long中,这个recent app不是packages下的的recent app。尽然是launcher3。
01-01 09:25:13.930 1455 8142 I ActivityTaskManager: START u0 {act=android.intent.action.MAIN cat=[android.intent.category.HOME] flg=0x10000000 pkg=com.android.launcher3 cmp=com.android.launcher3/com.android.searchlauncher.SearchLauncher (has extras)} from uid 10169
但是去launcher3下面也找不到SearchLauncher,所有这个launcher3可能被overlay了。所以去对应的地方找代码。一般都是在vendor下。我这边是在vendor/partner_gms/apps/SearchLauncher 在这,打开androidmanifest.xml
<activity
android:name="com.android.searchlauncher.SearchLauncher"
android:launchMode="singleTask"
android:clearTaskOnLaunch="true"
android:stateNotNeeded="true"
android:windowSoftInputMode="adjustPan"
android:screenOrientation="unspecified"
android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize"
android:resizeableActivity="true"
android:resumeWhilePausing="true"
android:taskAffinity=""
android:enabled="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.HOME" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.MONKEY"/>
<category android:name="android.intent.category.LAUNCHER_APP" />
</intent-filter>
<meta-data
android:name="com.android.launcher3.grid.control"
android:value="${packageName}.grid_control" />
</activity>
找到了这个SeracherLauncher,再往下也没深入追了。
---------------------------------------------------------------------------------------------------------------------
直接看crash的地方,是在packages/apps/Launcher3/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java 中的
mCurrentAnimation.setPlayFraction(Utilities.boundToRange(
totalDisplacement * mProgressMultiplier, 0, 1));
mCurrentAnimation空指针异常。
------------------------------------------------------------------------------------------------------------------------
滑动的流程
packages/apps/Launcher3/src/com/android/launcher3/touch/BaseSwipeDetector.java
中会去监听屏幕touch事件。
public boolean onTouchEvent(MotionEvent ev) {
int actionMasked = ev.getActionMasked();
if (actionMasked == MotionEvent.ACTION_DOWN && mVelocityTracker != null) {
mVelocityTracker.clear();
}
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
mVelocityTracker.addMovement(ev);
switch (actionMasked) {
case MotionEvent.ACTION_DOWN:
mActivePointerId = ev.getPointerId(0);
mDownPos.set(ev.getX(), ev.getY());
mLastPos.set(mDownPos);
mLastDisplacement.set(0, 0);
mDisplacement.set(0, 0);
if (mState == ScrollState.SETTLING && mIgnoreSlopWhenSettling) {
setState(ScrollState.DRAGGING);
}
break;
//case MotionEvent.ACTION_POINTER_DOWN:
case MotionEvent.ACTION_POINTER_UP:
int ptrIdx = ev.getActionIndex();
int ptrId = ev.getPointerId(ptrIdx);
if (ptrId == mActivePointerId) {
final int newPointerIdx = ptrIdx == 0 ? 1 : 0;
mDownPos.set(
ev.getX(newPointerIdx) - (mLastPos.x - mDownPos.x),
ev.getY(newPointerIdx) - (mLastPos.y - mDownPos.y));
mLastPos.set(ev.getX(newPointerIdx), ev.getY(newPointerIdx));
mActivePointerId = ev.getPointerId(newPointerIdx);
}
break;
case MotionEvent.ACTION_MOVE:
Log.i("c","ACTION_MOVE mState "+mState);
int pointerIndex = ev.findPointerIndex(mActivePointerId);
if (pointerIndex == INVALID_POINTER_ID) {
Log.i("c","INVALID_POINTER_ID");
break;
}
mDisplacement.set(ev.getX(pointerIndex) - mDownPos.x,
ev.getY(pointerIndex) - mDownPos.y);
if (mIsRtl) {
mDisplacement.x = -mDisplacement.x;
}
// handle state and listener calls.
if (mState != ScrollState.DRAGGING && shouldScrollStart(mDisplacement)) {
Log.i("c","setState DRAGGING");
setState(ScrollState.DRAGGING);
}
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.PAUSE_NOT_DETECTED, "before report dragging");
}
if (mState == ScrollState.DRAGGING) {
Log.i("c","reportDragging");
reportDragging(ev);
}
mLastPos.set(ev.getX(pointerIndex), ev.getY(pointerIndex));
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
// These are synthetic events and there is no need to update internal values.
if (mState == ScrollState.DRAGGING) {
setState(ScrollState.SETTLING);
}
mVelocityTracker.recycle();
mVelocityTracker = null;
break;
default:
break;
}
return true;
}
主要看ACTION_MOVE这个,mState是当前的状态,如果不是正在滑出app,并且可以开始滑动,那么就去把状态设置成滑动中。然后去滑动。
if (mState != ScrollState.DRAGGING && shouldScrollStart(mDisplacement)) {
Log.i("c","setState DRAGGING");
setState(ScrollState.DRAGGING);
}
if (mState == ScrollState.DRAGGING) {
Log.i("c","reportDragging");
reportDragging(ev);
}
shouldScrollStart 是 这个抽象函数,具体实现在packages/apps/Launcher3/src/com/android/launcher3/touch/SingleAxisSwipeDetector.java
@Override
protected boolean shouldScrollStart(PointF displacement) {
// Reject cases where the angle or slop condition is not met.
float minDisplacement = Math.max(mTouchSlop,
Math.abs(mDir.extractOrthogonalDirection(displacement)));
if (Math.abs(mDir.extractDirection(displacement)) < minDisplacement) {
Log.i("c","shouldScrollStart return false");
return false;
}
Log.i("c","shouldScrollStart minDisplacement "+minDisplacement+" extractDirection "+Math.abs(mDir.extractDirection(displacement)));
// Check if the client is interested in scroll in current direction.
float displacementComponent = mDir.extractDirection(displacement);
return canScrollNegative(displacementComponent) || canScrollPositive(displacementComponent);
}
private boolean canScrollNegative(float displacement) {
Log.i("c","canScrollNegative "+mScrollDirections +" "+ mDir.isNegative(displacement));
return (mScrollDirections & DIRECTION_NEGATIVE) > 0 && mDir.isNegative(displacement);
}
private boolean canScrollPositive(float displacement) {
Log.i("c"," canScrollPositive "+mScrollDirections +" "+mDir.isPositive(displacement));
return (mScrollDirections & DIRECTION_POSITIVE) > 0 && mDir.isPositive(displacement);
}
下面继续看reportDragging做了什么
private void reportDragging(MotionEvent event) {
Log.d(TAG, "mDisplacement "+mDisplacement+" mLastDisplacement "+mLastDisplacement);
if (mDisplacement != mLastDisplacement) {
if (DBG) {
Log.d(TAG, String.format("onDrag disp=%s", mDisplacement));
}
mLastDisplacement.set(mDisplacement);
sTempPoint.set(mDisplacement.x - mSubtractDisplacement.x,
mDisplacement.y - mSubtractDisplacement.y);
reportDraggingInternal(sTempPoint, event);
}
}
然后继续追reportDraggingInternal,是在SingleAxisSwipeDetector.java中实现
@Override
protected void reportDraggingInternal(PointF displacement, MotionEvent event) {
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.PAUSE_NOT_DETECTED, "SingleAxisSwipeDetector "
+ mListener.getClass().getSimpleName());
}
mListener.onDrag(mDir.extractDirection(displacement),
mDir.extractOrthogonalDirection(displacement), event);
}
然后追onDrag,这是一个listener,是在TaskViewTouchController中重写的。这边new SingleAxisSwipeDetector是时候this就是。onDrag onDragEnd onDragStart这些。分别是手滑动,离开屏幕和接触屏幕时会走的。
public TaskViewTouchController(T activity) {
mActivity = activity;
mRecentsView = activity.getOverviewPanel();
mIsRtl = Utilities.isRtl(activity.getResources());
SingleAxisSwipeDetector.Direction dir =
mRecentsView.getPagedOrientationHandler().getOppositeSwipeDirection();
mDetector = new SingleAxisSwipeDetector(activity, this, dir);
}
最后packages/apps/Launcher3/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java看一下。它会根据屏幕旋转的角度去处理一些东西。
public boolean update(
@SurfaceRotation int touchRotation, @SurfaceRotation int displayRotation) {
mRecentsActivityRotation = inferRecentsActivityRotation(displayRotation);
mDisplayRotation = displayRotation;
mTouchRotation = touchRotation;
mPreviousRotation = touchRotation;
PagedOrientationHandler oldHandler = mOrientationHandler;
if (mRecentsActivityRotation == mTouchRotation
|| (canRecentsActivityRotate() && (mFlags & FLAG_SWIPE_UP_NOT_RUNNING) != 0)) {
mOrientationHandler = PagedOrientationHandler.PORTRAIT;
if (DEBUG) {
Log.d(TAG, "current RecentsOrientedState: " + this);
}
} else if (mTouchRotation == ROTATION_90) {
mOrientationHandler = PagedOrientationHandler.LANDSCAPE;
} else if (mTouchRotation == ROTATION_270) {
mOrientationHandler = PagedOrientationHandler.SEASCAPE;
} else {
mOrientationHandler = PagedOrientationHandler.PORTRAIT;
}
if (DEBUG) {
Log.d(TAG, "current RecentsOrientedState: " + this);
}
return oldHandler != mOrientationHandler;
}
--------------------------------------------------------------------------------------------------------------------------
修改。
加了很多log,发现在270°的时候,向上滑动不会触发reportDragging。说明前面setState肯定没有走,log上看是shouldScrollStart返回false。
if (mState != ScrollState.DRAGGING && shouldScrollStart(mDisplacement)) {
Log.i("c","setState DRAGGING");
setState(ScrollState.DRAGGING);
}
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.PAUSE_NOT_DETECTED, "before report dragging");
}
if (mState == ScrollState.DRAGGING) {
Log.i("c","reportDragging");
reportDragging(ev);
}
继续追log, 其实是canScrollNegative返回false。(mScrollDirections & DIRECTION_NEGATIVE) > 0 不满足条件。所以在270°的时候要让这个条件满足。
@Override
protected boolean shouldScrollStart(PointF displacement) {
// Reject cases where the angle or slop condition is not met.
float minDisplacement = Math.max(mTouchSlop,
Math.abs(mDir.extractOrthogonalDirection(displacement)));
if (Math.abs(mDir.extractDirection(displacement)) < minDisplacement) {
Log.i("c","shouldScrollStart return false");
return false;
}
Log.i("c","shouldScrollStart minDisplacement "+minDisplacement+" extractDirection "+Math.abs(mDir.extractDirection(displacement)));
// Check if the client is interested in scroll in current direction.
float displacementComponent = mDir.extractDirection(displacement);
return canScrollNegative(displacementComponent) || canScrollPositive(displacementComponent);
}
private boolean canScrollNegative(float displacement) {
Log.i("c","canScrollNegative "+mScrollDirections +" "+ mDir.isNegative(displacement));
return (mScrollDirections & DIRECTION_NEGATIVE) > 0 && mDir.isNegative(displacement);
}
private boolean canScrollPositive(float displacement) {
Log.i("c"," canScrollPositive "+mScrollDirections +" "+mDir.isPositive(displacement));
return (mScrollDirections & DIRECTION_POSITIVE) > 0 && mDir.isPositive(displacement);
}
在TaskViewTouchController中的onControllerInterceptTouchEvent中添加,ACRION_DOWN的时候就去设置,DIRECTION_NEGATIVE。然后canScrollNegative那就能返回true,向上滑动就能触发了。
@Override
public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
if ((ev.getAction() == MotionEvent.ACTION_UP || ev.getAction() == MotionEvent.ACTION_CANCEL)
&& mCurrentAnimation == null) {
clearState();
}
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
//省略代码
//Add
mRecentsView.getOrientationState().getTouchRotation();
Log.i("a","onControllerInterceptTouchEvent touch rotation "+mRecentsView.getOrientationState().getTouchRotation());
if(mRecentsView.getOrientationState().getTouchRotation() ==ROTATION_270){
Log.i("a","onControllerInterceptTouchEvent 270 ");
directionsToDetectScroll= DIRECTION_NEGATIVE;
}
//end
Log.i("a","onControllerInterceptTouchEvent setDetectableScrollConditions "+directionsToDetectScroll);
mDetector.setDetectableScrollConditions(
directionsToDetectScroll, ignoreSlopWhenSettling);
}
if (mNoIntercept) {
return false;
}
onControllerTouchEvent(ev);
return mDetector.isDraggingOrSettling();
}
但是每次向下滑动还是会crash,所以我们要对270°做特殊处理。在crash的地方处理。
private void reInitAnimationController(boolean goingUp) {
Log.i("a","reInitAnimationController mCurrentAnimationIsGoingUp "+mCurrentAnimationIsGoingUp
+" goingUp "+goingUp);
if (mCurrentAnimation != null && mCurrentAnimationIsGoingUp == goingUp) {
// No need to init
Log.i("a","no need to init");
return;
}
int scrollDirections = mDetector.getScrollDirections();
Log.i("a","reInitAnimationController goingUp "+goingUp + " scrollDirections "+
scrollDirections );
//add 防止crash,在270°的时候不走走,继续初始化
if (mRecentsView.getOrientationState().getTouchRotation() != ROTATION_270) {
//end
if (goingUp && ((scrollDirections & DIRECTION_POSITIVE) == 0)
|| !goingUp && ((scrollDirections & DIRECTION_NEGATIVE) == 0)) {
// Trying to re-init in an unsupported direction.
Log.i("a", "203");
return;
}
}
//add 向下滑动会打开app,加了可以避免。
else {
if (goingUp == false && mCurrentAnimationIsGoingUp == true) {
Log.i("a", "220");
return;
}
}//end
}
到这修改完成,270°的时候,可以向上滑动删除app,向下无操作。