blob: 4651570a600d80787f0e38ccaae9192e9fc58fdc [file] [log] [blame]
/*
* Copyright (C) 2022 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.launcher3.taskbar;
import android.graphics.Rect;
import android.os.Handler;
import android.view.MotionEvent;
import android.view.TouchDelegate;
import android.view.View;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.util.TouchController;
import java.util.function.Supplier;
/**
* Extends the Recents touch area during the taskbar to overview animation
* to give user some error room when trying to quickly double tap recents button since it moves.
*
* Listens for icon alignment as our indication for the animation.
*/
public class RecentsHitboxExtender implements TouchController {
private static final int RECENTS_HITBOX_TIMEOUT_MS = 500;
private View mRecentsButton;
private View mRecentsParent;
private DeviceProfile mDeviceProfile;
private Supplier<float[]> mParentCoordSupplier;
private TouchDelegate mRecentsTouchDelegate;
/**
* Will be true while the animation from taskbar to overview is occurring.
* Lifecycle of this variable slightly extends past the animation by
* {@link #RECENTS_HITBOX_TIMEOUT_MS}, so can use this variable as a proxy for if
* the current hitbox is extended or not.
*/
private boolean mAnimatingFromTaskbarToOverview;
private float mLastIconAlignment;
private final Rect mRecentsHitBox = new Rect();
private boolean mRecentsButtonClicked;
private Handler mHandler;
private final Runnable mRecentsHitboxResetRunnable = this::reset;
public void init(View recentsButton, View recentsParent, DeviceProfile deviceProfile,
Supplier<float[]> parentCoordSupplier, Handler handler) {
mRecentsButton = recentsButton;
mRecentsParent = recentsParent;
mDeviceProfile = deviceProfile;
mParentCoordSupplier = parentCoordSupplier;
mHandler = handler;
}
public void onRecentsButtonClicked() {
mRecentsButtonClicked = true;
}
/**
* @param progress 0 -> Taskbar, 1 -> Overview
*/
public void onAnimationProgressToOverview(float progress) {
if (progress == 1 || progress == 0) {
// Done w/ animation
mLastIconAlignment = progress;
if (mAnimatingFromTaskbarToOverview) {
if (progress == 1) {
// Finished animation to workspace, remove the touch delegate shortly
mHandler.postDelayed(mRecentsHitboxResetRunnable, RECENTS_HITBOX_TIMEOUT_MS);
return;
} else {
// Went back to taskbar, reset immediately
mHandler.removeCallbacks(mRecentsHitboxResetRunnable);
reset();
}
}
}
if (mAnimatingFromTaskbarToOverview) {
return;
}
if (progress > 0 && mLastIconAlignment == 0 && mRecentsButtonClicked) {
// Starting animation, previously we were showing taskbar
mAnimatingFromTaskbarToOverview = true;
float[] recentsCoords = mParentCoordSupplier.get();
int x = (int) recentsCoords[0];
int y = (int) (recentsCoords[1]);
// Extend hitbox vertically by the offset amount from mDeviceProfile.getTaskbarOffsetY()
mRecentsHitBox.set(x, y,
x + mRecentsButton.getWidth(),
y + mRecentsButton.getHeight() + mDeviceProfile.getTaskbarOffsetY()
);
mRecentsTouchDelegate = new TouchDelegate(mRecentsHitBox, mRecentsButton);
mRecentsParent.setTouchDelegate(mRecentsTouchDelegate);
}
}
private void reset() {
mAnimatingFromTaskbarToOverview = false;
mRecentsButton.setTouchDelegate(null);
mRecentsHitBox.setEmpty();
mRecentsButtonClicked = false;
}
/**
* @return {@code true} if the bounds for recents touches are currently extended
*/
public boolean extendedHitboxEnabled() {
return mAnimatingFromTaskbarToOverview;
}
@Override
public boolean onControllerTouchEvent(MotionEvent ev) {
return mRecentsTouchDelegate.onTouchEvent(ev);
}
@Override
public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
return mRecentsHitBox.contains((int)ev.getX(), (int)ev.getY());
}
}