mirror of
https://github.com/LawnchairLauncher/lawnchair.git
synced 2026-03-04 18:06:48 +00:00
- Speculative fix, there are some scenarios where we update the swipe touch/assistant regions without updating the OHM regions Bug: 362039326 Flag: EXEMPT bugfix Test: Manual Change-Id: If5c558623073e5185827ae17a162dacf322c74a4
434 lines
18 KiB
Java
434 lines
18 KiB
Java
/*
|
|
* Copyright (C) 2019 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package com.android.quickstep;
|
|
|
|
import static android.view.MotionEvent.ACTION_CANCEL;
|
|
import static android.view.MotionEvent.ACTION_DOWN;
|
|
import static android.view.MotionEvent.ACTION_MOVE;
|
|
import static android.view.MotionEvent.ACTION_POINTER_DOWN;
|
|
import static android.view.MotionEvent.ACTION_UP;
|
|
|
|
import static com.android.launcher3.states.RotationHelper.deltaRotation;
|
|
|
|
import android.content.res.Resources;
|
|
import android.graphics.Point;
|
|
import android.graphics.RectF;
|
|
import android.util.Log;
|
|
import android.view.MotionEvent;
|
|
import android.view.Surface;
|
|
|
|
import com.android.launcher3.R;
|
|
import com.android.launcher3.testing.shared.ResourceUtils;
|
|
import com.android.launcher3.testing.shared.TestProtocol;
|
|
import com.android.launcher3.util.DisplayController.Info;
|
|
import com.android.launcher3.util.NavigationMode;
|
|
import com.android.launcher3.util.window.CachedDisplayInfo;
|
|
import com.android.quickstep.util.ActiveGestureProtoLogProxy;
|
|
import com.android.systemui.shared.Flags;
|
|
|
|
import java.io.PrintWriter;
|
|
import java.util.HashMap;
|
|
import java.util.Map;
|
|
|
|
/**
|
|
* Maintains state for supporting nav bars and tracking their gestures in multiple orientations.
|
|
* See {@link OrientationRectF#applyTransformToRotation(MotionEvent, int, boolean)} for
|
|
* transformation of MotionEvents from one orientation's coordinate space to another's.
|
|
*
|
|
* This class only supports single touch/pointer gesture tracking for touches started in a supported
|
|
* nav bar region.
|
|
*/
|
|
class OrientationTouchTransformer {
|
|
|
|
private static final String TAG = "OrientationTouchTransformer";
|
|
private static final boolean DEBUG = false;
|
|
|
|
private static final int QUICKSTEP_ROTATION_UNINITIALIZED = -1;
|
|
|
|
private final Map<CachedDisplayInfo, OrientationRectF> mSwipeTouchRegions =
|
|
new HashMap<CachedDisplayInfo, OrientationRectF>();
|
|
private final RectF mAssistantLeftRegion = new RectF();
|
|
private final RectF mAssistantRightRegion = new RectF();
|
|
private final RectF mOneHandedModeRegion = new RectF();
|
|
private CachedDisplayInfo mCachedDisplayInfo = new CachedDisplayInfo();
|
|
private int mNavBarGesturalHeight;
|
|
private final int mNavBarLargerGesturalHeight;
|
|
private boolean mEnableMultipleRegions;
|
|
private Resources mResources;
|
|
private OrientationRectF mLastRectTouched;
|
|
/**
|
|
* The rotation of the last touched nav bar, whether that be through the last region the user
|
|
* touched down on or valid rotation user turned their device to.
|
|
* Note this is different than
|
|
* {@link #mQuickStepStartingRotation} as it always updates its value on every touch whereas
|
|
* mQuickstepStartingRotation only updates when device rotation matches touch rotation.
|
|
*/
|
|
private int mActiveTouchRotation;
|
|
private NavigationMode mMode;
|
|
private QuickStepContractInfo mContractInfo;
|
|
|
|
/**
|
|
* Represents if we're currently in a swipe "session" of sorts. If value is
|
|
* QUICKSTEP_ROTATION_UNINITIALIZED, then user has not tapped on an active nav region.
|
|
* Otherwise it will be the rotation of the display when the user first interacted with the
|
|
* active nav bar region.
|
|
* The "session" ends when {@link #enableMultipleRegions(boolean, Info)} is
|
|
* called - usually from a timeout or if user starts interacting w/ the foreground app.
|
|
*
|
|
* This is different than {@link #mLastRectTouched} as it can get reset by the system whereas
|
|
* the rect is purely used for tracking touch interactions and usually this "session" will
|
|
* outlast the touch interaction.
|
|
*/
|
|
private int mQuickStepStartingRotation = QUICKSTEP_ROTATION_UNINITIALIZED;
|
|
|
|
/** For testability */
|
|
interface QuickStepContractInfo {
|
|
float getWindowCornerRadius();
|
|
}
|
|
|
|
|
|
OrientationTouchTransformer(Resources resources, NavigationMode mode,
|
|
QuickStepContractInfo contractInfo) {
|
|
mResources = resources;
|
|
mMode = mode;
|
|
mContractInfo = contractInfo;
|
|
mNavBarGesturalHeight = getNavbarSize(ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE);
|
|
mNavBarLargerGesturalHeight = ResourceUtils.getDimenByName(
|
|
ResourceUtils.NAVBAR_BOTTOM_GESTURE_LARGER_SIZE, resources,
|
|
mNavBarGesturalHeight);
|
|
}
|
|
|
|
private void refreshTouchRegion(Info info, Resources newRes, String reason) {
|
|
// Swipe touch regions are independent of nav mode, so we have to clear them explicitly
|
|
// here to avoid, for ex, a nav region for 2-button rotation 0 being used for 3-button mode
|
|
// It tries to cache and reuse swipe regions whenever possible based only on rotation
|
|
mResources = newRes;
|
|
mSwipeTouchRegions.clear();
|
|
resetSwipeRegions(info, reason);
|
|
}
|
|
|
|
void setNavigationMode(NavigationMode newMode, Info info, Resources newRes) {
|
|
if (enableLog()) {
|
|
Log.d(TAG, "setNavigationMode new: " + newMode + " oldMode: " + mMode + " " + this);
|
|
}
|
|
if (mMode == newMode) {
|
|
return;
|
|
}
|
|
this.mMode = newMode;
|
|
refreshTouchRegion(info, newRes, "setNavigationMode");
|
|
}
|
|
|
|
void setGesturalHeight(int newGesturalHeight, Info info, Resources newRes) {
|
|
if (mNavBarGesturalHeight == newGesturalHeight) {
|
|
return;
|
|
}
|
|
mNavBarGesturalHeight = newGesturalHeight;
|
|
refreshTouchRegion(info, newRes, "setGesturalHeight");
|
|
}
|
|
|
|
/**
|
|
* Sets the current nav bar region to listen to events for as determined by
|
|
* {@param info}. If multiple nav bar regions are enabled, then this region will be added
|
|
* alongside other regions.
|
|
* Ok to call multiple times
|
|
*
|
|
* @see #enableMultipleRegions(boolean, Info)
|
|
*/
|
|
void createOrAddTouchRegion(Info info, String reason) {
|
|
mCachedDisplayInfo = new CachedDisplayInfo(info.currentSize, info.rotation);
|
|
|
|
if (mQuickStepStartingRotation > QUICKSTEP_ROTATION_UNINITIALIZED
|
|
&& mCachedDisplayInfo.rotation == mQuickStepStartingRotation) {
|
|
// User already was swiping and the current screen is same rotation as the starting one
|
|
// Remove active nav bars in other rotations except for the one we started out in
|
|
resetSwipeRegions(info, reason);
|
|
return;
|
|
}
|
|
OrientationRectF region = mSwipeTouchRegions.get(mCachedDisplayInfo);
|
|
if (region != null) {
|
|
return;
|
|
}
|
|
|
|
if (mEnableMultipleRegions) {
|
|
mSwipeTouchRegions.put(mCachedDisplayInfo, createRegionForDisplay(info, reason));
|
|
} else {
|
|
resetSwipeRegions(info, reason);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Call when we want to start tracking nav bar touch regions in multiple orientations.
|
|
* ALSO, you BETTER call this with {@param enableMultipleRegions} set to false once you're done.
|
|
*
|
|
* @param enableMultipleRegions Set to true to start tracking multiple nav bar regions
|
|
* @param info The current displayInfo which will be the start of the quickswitch gesture
|
|
*/
|
|
void enableMultipleRegions(boolean enableMultipleRegions, Info info) {
|
|
mEnableMultipleRegions = enableMultipleRegions && mMode != NavigationMode.TWO_BUTTONS;
|
|
if (mEnableMultipleRegions) {
|
|
mQuickStepStartingRotation = info.rotation;
|
|
} else {
|
|
mActiveTouchRotation = 0;
|
|
mQuickStepStartingRotation = QUICKSTEP_ROTATION_UNINITIALIZED;
|
|
}
|
|
resetSwipeRegions(info, "enableMultipleRegions");
|
|
}
|
|
|
|
/**
|
|
* Call when removing multiple regions to swipe from, but still in active quickswitch mode (task
|
|
* list is still frozen).
|
|
* Ex. This would be called when user has quickswitched to the same app rotation that
|
|
* they started quickswitching in, indicating that extra nav regions can be ignored. Calling
|
|
* this will update the value of {@link #mActiveTouchRotation}
|
|
*
|
|
* @param displayInfo The display whos rotation will be used as the current active rotation
|
|
*/
|
|
void setSingleActiveRegion(Info displayInfo) {
|
|
mActiveTouchRotation = displayInfo.rotation;
|
|
resetSwipeRegions(displayInfo, "setSingleActiveRegion");
|
|
}
|
|
|
|
/**
|
|
* Only saves the swipe region represented by {@param region}, clears the
|
|
* rest from {@link #mSwipeTouchRegions}
|
|
* To be called whenever we want to stop tracking more than one swipe region.
|
|
* Ok to call multiple times.
|
|
*/
|
|
private void resetSwipeRegions(Info region, String reason) {
|
|
if (enableLog()) {
|
|
Log.d(TAG, "clearing all regions except rotation: " + mCachedDisplayInfo.rotation
|
|
+ " reason=" + reason);
|
|
}
|
|
|
|
mCachedDisplayInfo = new CachedDisplayInfo(region.currentSize, region.rotation);
|
|
OrientationRectF regionToKeep = mSwipeTouchRegions.get(mCachedDisplayInfo);
|
|
if (regionToKeep == null) {
|
|
regionToKeep = createRegionForDisplay(region, reason);
|
|
}
|
|
mSwipeTouchRegions.clear();
|
|
mSwipeTouchRegions.put(mCachedDisplayInfo, regionToKeep);
|
|
updateAssistantRegions(regionToKeep);
|
|
updateOneHandedRegions(regionToKeep);
|
|
}
|
|
|
|
private void resetSwipeRegions() {
|
|
OrientationRectF regionToKeep = mSwipeTouchRegions.get(mCachedDisplayInfo);
|
|
mSwipeTouchRegions.clear();
|
|
if (regionToKeep != null) {
|
|
mSwipeTouchRegions.put(mCachedDisplayInfo, regionToKeep);
|
|
updateAssistantRegions(regionToKeep);
|
|
updateOneHandedRegions(regionToKeep);
|
|
}
|
|
}
|
|
|
|
private OrientationRectF createRegionForDisplay(Info display, String reason) {
|
|
if (enableLog()) {
|
|
Log.d(TAG, "creating rotation region for: " + mCachedDisplayInfo.rotation
|
|
+ " with mode: " + mMode + " displayRotation: " + display.rotation +
|
|
" displaySize: " + display.currentSize +
|
|
" navBarHeight: " + mNavBarGesturalHeight +
|
|
" reason: " + reason);
|
|
}
|
|
|
|
Point size = display.currentSize;
|
|
int rotation = display.rotation;
|
|
int touchHeight = mNavBarGesturalHeight;
|
|
OrientationRectF orientationRectF = new OrientationRectF(0, 0, size.x, size.y, rotation);
|
|
if (mMode == NavigationMode.NO_BUTTON
|
|
|| (mMode == NavigationMode.THREE_BUTTONS && Flags.threeButtonCornerSwipe())) {
|
|
orientationRectF.top = orientationRectF.bottom - touchHeight;
|
|
updateAssistantRegions(orientationRectF);
|
|
} else {
|
|
mAssistantLeftRegion.setEmpty();
|
|
mAssistantRightRegion.setEmpty();
|
|
int navbarSize = getNavbarSize(ResourceUtils.NAVBAR_LANDSCAPE_LEFT_RIGHT_SIZE);
|
|
switch (rotation) {
|
|
case Surface.ROTATION_90:
|
|
orientationRectF.left = orientationRectF.right
|
|
- navbarSize;
|
|
break;
|
|
case Surface.ROTATION_270:
|
|
orientationRectF.right = orientationRectF.left
|
|
+ navbarSize;
|
|
break;
|
|
default:
|
|
orientationRectF.top = orientationRectF.bottom - touchHeight;
|
|
}
|
|
}
|
|
updateOneHandedRegions(orientationRectF);
|
|
ActiveGestureProtoLogProxy.logCreateTouchRegionForDisplay(rotation, size, orientationRectF,
|
|
mOneHandedModeRegion, mNavBarGesturalHeight, mNavBarLargerGesturalHeight,
|
|
reason);
|
|
|
|
return orientationRectF;
|
|
}
|
|
|
|
private void updateAssistantRegions(OrientationRectF orientationRectF) {
|
|
int navbarHeight = getNavbarSize(ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE);
|
|
int assistantWidth = mResources.getDimensionPixelSize(R.dimen.gestures_assistant_width);
|
|
float assistantHeight = Math.max(navbarHeight, mContractInfo.getWindowCornerRadius());
|
|
mAssistantLeftRegion.bottom = mAssistantRightRegion.bottom = orientationRectF.bottom;
|
|
mAssistantLeftRegion.top = mAssistantRightRegion.top =
|
|
orientationRectF.bottom - assistantHeight;
|
|
|
|
mAssistantLeftRegion.left = 0;
|
|
mAssistantLeftRegion.right = assistantWidth;
|
|
|
|
mAssistantRightRegion.right = orientationRectF.right;
|
|
mAssistantRightRegion.left = orientationRectF.right - assistantWidth;
|
|
}
|
|
|
|
private void updateOneHandedRegions(OrientationRectF orientationRectF) {
|
|
// One handed gestural only active on portrait mode
|
|
mOneHandedModeRegion.set(0, orientationRectF.bottom - mNavBarLargerGesturalHeight,
|
|
orientationRectF.right, orientationRectF.bottom);
|
|
}
|
|
|
|
boolean touchInAssistantRegion(MotionEvent ev) {
|
|
return mAssistantLeftRegion.contains(ev.getX(), ev.getY())
|
|
|| mAssistantRightRegion.contains(ev.getX(), ev.getY());
|
|
|
|
}
|
|
|
|
boolean touchInOneHandedModeRegion(MotionEvent ev) {
|
|
return mOneHandedModeRegion.contains(ev.getX(), ev.getY());
|
|
}
|
|
|
|
private int getNavbarSize(String resName) {
|
|
return ResourceUtils.getNavbarSize(resName, mResources);
|
|
}
|
|
|
|
boolean touchInValidSwipeRegions(float x, float y) {
|
|
if (enableLog()) {
|
|
Log.d(TAG, "touchInValidSwipeRegions " + x + "," + y + " in " + mLastRectTouched);
|
|
}
|
|
if (mLastRectTouched != null) {
|
|
return mLastRectTouched.contains(x, y);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
int getCurrentActiveRotation() {
|
|
return mActiveTouchRotation;
|
|
}
|
|
|
|
int getQuickStepStartingRotation() {
|
|
return mQuickStepStartingRotation;
|
|
}
|
|
|
|
public void transform(MotionEvent event) {
|
|
int eventAction = event.getActionMasked();
|
|
switch (eventAction) {
|
|
case ACTION_MOVE: {
|
|
if (mLastRectTouched == null) {
|
|
return;
|
|
}
|
|
if (TaskAnimationManager.SHELL_TRANSITIONS_ROTATION) {
|
|
if (event.getSurfaceRotation() != mActiveTouchRotation) {
|
|
// With Shell transitions, we should rotated to the orientation at the start
|
|
// of the gesture not the current display rotation which will happen early
|
|
mLastRectTouched.applyTransform(event,
|
|
deltaRotation(event.getSurfaceRotation(), mActiveTouchRotation),
|
|
true);
|
|
}
|
|
} else {
|
|
mLastRectTouched.applyTransformFromRotation(event, mCachedDisplayInfo.rotation,
|
|
true);
|
|
}
|
|
break;
|
|
}
|
|
case ACTION_CANCEL:
|
|
case ACTION_UP: {
|
|
if (mLastRectTouched == null) {
|
|
return;
|
|
}
|
|
if (TaskAnimationManager.SHELL_TRANSITIONS_ROTATION) {
|
|
if (event.getSurfaceRotation() != mActiveTouchRotation) {
|
|
// With Shell transitions, we should rotated to the orientation at the start
|
|
// of the gesture not the current display rotation which will happen early
|
|
mLastRectTouched.applyTransform(event,
|
|
deltaRotation(event.getSurfaceRotation(), mActiveTouchRotation),
|
|
true);
|
|
}
|
|
} else {
|
|
mLastRectTouched.applyTransformFromRotation(event, mCachedDisplayInfo.rotation,
|
|
true);
|
|
}
|
|
mLastRectTouched = null;
|
|
break;
|
|
}
|
|
case ACTION_POINTER_DOWN:
|
|
case ACTION_DOWN: {
|
|
if (enableLog()) {
|
|
Log.d(TAG, "ACTION_DOWN mLastRectTouched: " + mLastRectTouched);
|
|
}
|
|
if (mLastRectTouched != null) {
|
|
return;
|
|
}
|
|
|
|
for (OrientationRectF rect : mSwipeTouchRegions.values()) {
|
|
if (enableLog()) {
|
|
Log.d(TAG, "ACTION_DOWN rect: " + rect);
|
|
}
|
|
if (rect == null) {
|
|
continue;
|
|
}
|
|
if (rect.applyTransformFromRotation(
|
|
event, mCachedDisplayInfo.rotation, false)) {
|
|
mLastRectTouched = rect;
|
|
mActiveTouchRotation = rect.getRotation();
|
|
if (mEnableMultipleRegions
|
|
&& mCachedDisplayInfo.rotation == mActiveTouchRotation) {
|
|
// TODO(b/154580671) might make this block unnecessary
|
|
// Start a touch session for the default nav region for the display
|
|
mQuickStepStartingRotation = mLastRectTouched.getRotation();
|
|
resetSwipeRegions();
|
|
}
|
|
if (enableLog()) {
|
|
Log.d(TAG, "set active region: " + rect);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
private boolean enableLog() {
|
|
return DEBUG || TestProtocol.sDebugTracing;
|
|
}
|
|
|
|
public void dump(PrintWriter pw) {
|
|
pw.println("OrientationTouchTransformerState: ");
|
|
pw.println(" currentActiveRotation=" + getCurrentActiveRotation());
|
|
pw.println(" lastTouchedRegion=" + mLastRectTouched);
|
|
pw.println(" multipleRegionsEnabled=" + mEnableMultipleRegions);
|
|
StringBuilder regions = new StringBuilder(" currentTouchableRotations=");
|
|
for (CachedDisplayInfo key: mSwipeTouchRegions.keySet()) {
|
|
OrientationRectF rectF = mSwipeTouchRegions.get(key);
|
|
regions.append(rectF).append(" ");
|
|
}
|
|
pw.println(regions);
|
|
pw.println(" mNavBarGesturalHeight=" + mNavBarGesturalHeight);
|
|
pw.println(" mNavBarLargerGesturalHeight=" + mNavBarLargerGesturalHeight);
|
|
pw.println(" mAssistantLeftRegion=" + mAssistantLeftRegion);
|
|
pw.println(" mAssistantRightRegion=" + mAssistantRightRegion);
|
|
pw.println(" mOneHandedModeRegion=" + mOneHandedModeRegion);
|
|
}
|
|
}
|